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 readonly 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 Block)
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 bool omit_debug_info = ec.OmitDebuggingInfo;
2320 ec.OmitDebuggingInfo = true;
2321 foreach (StatementExpression s in scope_initializers)
2323 ec.OmitDebuggingInfo = omit_debug_info;
2325 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2328 ec.Mark (StartLocation);
2331 if (SymbolWriter.HasSymbolWriter)
2332 EmitSymbolInfo (ec);
2334 ec.CurrentBlock = prev_block;
2337 protected virtual void EmitSymbolInfo (EmitContext ec)
2339 if (variables != null) {
2340 foreach (LocalInfo vi in variables.Values) {
2341 vi.EmitSymbolInfo (ec);
2346 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2348 MutateVariables (storey);
2350 foreach (Statement s in statements)
2351 s.MutateHoistedGenericType (storey);
2354 void MutateVariables (AnonymousMethodStorey storey)
2356 if (variables != null) {
2357 foreach (LocalInfo vi in variables.Values) {
2358 vi.VariableType = storey.MutateType (vi.VariableType);
2362 if (temporary_variables != null) {
2363 foreach (LocalInfo vi in temporary_variables)
2364 vi.VariableType = storey.MutateType (vi.VariableType);
2368 public override string ToString ()
2370 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2373 protected override void CloneTo (CloneContext clonectx, Statement t)
2375 Block target = (Block) t;
2377 clonectx.AddBlockMap (this, target);
2379 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2380 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2382 target.Parent = clonectx.RemapBlockCopy (Parent);
2384 if (variables != null){
2385 target.variables = new Hashtable ();
2387 foreach (DictionaryEntry de in variables){
2388 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2389 target.variables [de.Key] = newlocal;
2390 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2394 target.statements = new ArrayList (statements.Count);
2395 foreach (Statement s in statements)
2396 target.statements.Add (s.Clone (clonectx));
2398 if (target.children != null){
2399 target.children = new ArrayList (children.Count);
2400 foreach (Block b in children){
2401 target.children.Add (clonectx.LookupBlock (b));
2406 // TODO: labels, switch_block, constants (?), anonymous_children
2411 public class ExplicitBlock : Block {
2412 HybridDictionary known_variables;
2413 protected AnonymousMethodStorey am_storey;
2415 public ExplicitBlock (Block parent, Location start, Location end)
2416 : this (parent, (Flags) 0, start, end)
2420 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2421 : base (parent, flags, start, end)
2423 this.Explicit = this;
2427 // Marks a variable with name @name as being used in this or a child block.
2428 // If a variable name has been used in a child block, it's illegal to
2429 // declare a variable with the same name in the current block.
2431 internal void AddKnownVariable (string name, IKnownVariable info)
2433 if (known_variables == null)
2434 known_variables = new HybridDictionary();
2436 known_variables [name] = info;
2439 Parent.Explicit.AddKnownVariable (name, info);
2442 public AnonymousMethodStorey AnonymousMethodStorey {
2443 get { return am_storey; }
2447 // Creates anonymous method storey in current block
2449 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2452 // When referencing a variable in iterator storey from children anonymous method
2454 if (Toplevel.am_storey is IteratorStorey) {
2455 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2456 return Toplevel.am_storey;
2460 // An iterator has only 1 storey block
2462 if (ec.CurrentIterator != null)
2463 return ec.CurrentIterator.Storey;
2465 if (am_storey == null) {
2466 MemberBase mc = ec.ResolveContext as MemberBase;
2467 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2470 // Create anonymous method storey for this block
2472 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2476 // Creates a link between this block and the anonymous method
2478 // An anonymous method can reference variables from any outer block, but they are
2479 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2480 // need to create another link between those variable storeys
2482 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2486 public override void Emit (EmitContext ec)
2488 if (am_storey != null)
2489 am_storey.EmitHoistedVariables (ec);
2491 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2492 if (emit_debug_info)
2497 if (emit_debug_info)
2501 public override void EmitMeta (EmitContext ec)
2504 // Creates anonymous method storey
2506 if (am_storey != null) {
2507 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2508 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2511 am_storey.DefineType ();
2512 am_storey.ResolveType ();
2513 am_storey.DefineMembers ();
2514 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2520 internal IKnownVariable GetKnownVariable (string name)
2522 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2525 public void PropagateStoreyReference (AnonymousMethodStorey s)
2527 if (Parent != null && am_storey != s) {
2528 if (am_storey != null)
2529 am_storey.AddParentStoreyReference (s);
2531 Parent.Explicit.PropagateStoreyReference (s);
2535 public override bool Resolve (EmitContext ec)
2537 bool ok = base.Resolve (ec);
2540 // Discard an anonymous method storey when this block has no hoisted variables
2542 if (am_storey != null && !am_storey.HasHoistedVariables) {
2550 protected override void CloneTo (CloneContext clonectx, Statement t)
2552 ExplicitBlock target = (ExplicitBlock) t;
2553 target.known_variables = null;
2554 base.CloneTo (clonectx, t);
2558 public class ToplevelParameterInfo : IKnownVariable {
2559 public readonly ToplevelBlock Block;
2560 public readonly int Index;
2561 public VariableInfo VariableInfo;
2563 Block IKnownVariable.Block {
2564 get { return Block; }
2566 public Parameter Parameter {
2567 get { return Block.Parameters [Index]; }
2570 public Type ParameterType {
2571 get { return Block.Parameters.Types [Index]; }
2574 public Location Location {
2575 get { return Parameter.Location; }
2578 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2586 // A toplevel block contains extra information, the split is done
2587 // only to separate information that would otherwise bloat the more
2588 // lightweight Block.
2590 // In particular, this was introduced when the support for Anonymous
2591 // Methods was implemented.
2593 public class ToplevelBlock : ExplicitBlock {
2594 GenericMethod generic;
2595 FlowBranchingToplevel top_level_branching;
2596 Parameters parameters;
2597 ToplevelParameterInfo[] parameter_info;
2598 LocalInfo this_variable;
2600 public HoistedVariable HoistedThisVariable;
2603 // The parameters for the block.
2605 public Parameters Parameters {
2606 get { return parameters; }
2609 public GenericMethod GenericMethod {
2610 get { return generic; }
2613 public bool HasStoreyAccess {
2614 set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
2615 get { return (flags & Flags.HasStoreyAccess) != 0; }
2618 public ToplevelBlock Container {
2619 get { return Parent == null ? null : Parent.Toplevel; }
2622 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2623 this (parent, (Flags) 0, parameters, start)
2627 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2628 this (parent, parameters, start)
2630 this.generic = generic;
2633 public ToplevelBlock (Parameters parameters, Location start) :
2634 this (null, (Flags) 0, parameters, start)
2638 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2639 this (null, flags, parameters, start)
2643 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2644 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2645 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2646 base (null, flags, start, Location.Null)
2648 this.Toplevel = this;
2650 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2651 this.Parent = parent;
2653 parent.AddAnonymousChild (this);
2655 if (!this.parameters.IsEmpty)
2656 ProcessParameters ();
2659 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2663 protected override void CloneTo (CloneContext clonectx, Statement t)
2665 ToplevelBlock target = (ToplevelBlock) t;
2666 base.CloneTo (clonectx, t);
2668 if (parameters.Count != 0)
2669 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2670 for (int i = 0; i < parameters.Count; ++i)
2671 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2674 public bool CheckError158 (string name, Location loc)
2676 if (AnonymousChildren != null) {
2677 foreach (ToplevelBlock child in AnonymousChildren) {
2678 if (!child.CheckError158 (name, loc))
2683 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2684 if (!c.DoCheckError158 (name, loc))
2691 public virtual Expression GetTransparentIdentifier (string name)
2696 void ProcessParameters ()
2698 int n = parameters.Count;
2699 parameter_info = new ToplevelParameterInfo [n];
2700 for (int i = 0; i < n; ++i) {
2701 parameter_info [i] = new ToplevelParameterInfo (this, i);
2703 Parameter p = parameters [i];
2707 string name = p.Name;
2708 LocalInfo vi = GetLocalInfo (name);
2710 Report.SymbolRelatedToPreviousError (vi.Location, name);
2711 Error_AlreadyDeclared (loc, name, "parent or current");
2715 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2717 Report.SymbolRelatedToPreviousError (pi.Location, name);
2718 Error_AlreadyDeclared (loc, name, "parent or current");
2722 AddKnownVariable (name, parameter_info [i]);
2725 // mark this block as "used" so that we create local declarations in a sub-block
2726 // FIXME: This appears to uncover a lot of bugs
2730 bool DoCheckError158 (string name, Location loc)
2732 LabeledStatement s = LookupLabel (name);
2734 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2735 Error_158 (name, loc);
2742 public override Expression CreateExpressionTree (EmitContext ec)
2744 if (statements.Count == 1)
2745 return ((Statement) statements [0]).CreateExpressionTree (ec);
2747 return base.CreateExpressionTree (ec);
2751 // Reformats this block to be top-level iterator block
2753 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2755 // Create block with original statements
2756 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2759 // TODO: Change to iter_block.statements = statements;
2760 foreach (Statement stmt in source.statements)
2761 iter_block.AddStatement (stmt);
2762 labels = source.labels;
2764 AddStatement (new IteratorStatement (iterator, iter_block));
2766 source.statements = new ArrayList (1);
2767 source.AddStatement (new Return (iterator, iterator.Location));
2768 source.IsIterator = false;
2770 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2771 source.am_storey = iterator_storey;
2772 return iterator_storey;
2775 public FlowBranchingToplevel TopLevelBranching {
2776 get { return top_level_branching; }
2780 // Returns a `ParameterReference' for the given name, or null if there
2781 // is no such parameter
2783 public ParameterReference GetParameterReference (string name, Location loc)
2785 ToplevelParameterInfo p = GetParameterInfo (name);
2786 return p == null ? null : new ParameterReference (this, p, loc);
2789 public ToplevelParameterInfo GetParameterInfo (string name)
2792 for (ToplevelBlock t = this; t != null; t = t.Container) {
2793 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2795 return t.parameter_info [idx];
2801 // Returns the "this" instance variable of this block.
2802 // See AddThisVariable() for more information.
2804 public LocalInfo ThisVariable {
2805 get { return this_variable; }
2809 // This is used by non-static `struct' constructors which do not have an
2810 // initializer - in this case, the constructor must initialize all of the
2811 // struct's fields. To do this, we add a "this" variable and use the flow
2812 // analysis code to ensure that it's been fully initialized before control
2813 // leaves the constructor.
2815 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2817 if (this_variable == null) {
2818 this_variable = new LocalInfo (ds, this, l);
2819 this_variable.Used = true;
2820 this_variable.IsThis = true;
2822 Variables.Add ("this", this_variable);
2825 return this_variable;
2828 public bool IsIterator {
2829 get { return (flags & Flags.IsIterator) != 0; }
2830 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2833 public bool IsThisAssigned (EmitContext ec)
2835 return this_variable == null || this_variable.IsThisAssigned (ec);
2838 public bool ResolveMeta (EmitContext ec, Parameters ip)
2840 int errors = Report.Errors;
2841 int orig_count = parameters.Count;
2843 if (top_level_branching != null)
2849 // Assert: orig_count != parameter.Count => orig_count == 0
2850 if (orig_count != 0 && orig_count != parameters.Count)
2851 throw new InternalErrorException ("parameter information mismatch");
2853 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2855 for (int i = 0; i < orig_count; ++i) {
2856 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2858 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2861 VariableInfo vi = new VariableInfo (ip, i, offset);
2862 parameter_info [i].VariableInfo = vi;
2863 offset += vi.Length;
2866 ResolveMeta (ec, offset);
2868 top_level_branching = ec.StartFlowBranching (this);
2870 return Report.Errors == errors;
2874 // Check whether all `out' parameters have been assigned.
2876 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2878 if (vector.IsUnreachable)
2881 int n = parameter_info == null ? 0 : parameter_info.Length;
2883 for (int i = 0; i < n; i++) {
2884 VariableInfo var = parameter_info [i].VariableInfo;
2889 if (vector.IsAssigned (var, false))
2892 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2897 public override void EmitMeta (EmitContext ec)
2899 parameters.ResolveVariable ();
2901 // Avoid declaring an IL variable for this_variable since it is not accessed
2902 // from the generated IL
2903 if (this_variable != null)
2904 Variables.Remove ("this");
2908 protected override void EmitSymbolInfo (EmitContext ec)
2910 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2911 if ((ae != null) && (ae.Storey != null))
2912 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2914 base.EmitSymbolInfo (ec);
2917 public override void Emit (EmitContext ec)
2920 ec.Mark (EndLocation);
2924 public class SwitchLabel {
2931 Label il_label_code;
2932 bool il_label_code_set;
2934 public static readonly object NullStringCase = new object ();
2937 // if expr == null, then it is the default case.
2939 public SwitchLabel (Expression expr, Location l)
2945 public Expression Label {
2951 public Location Location {
2955 public object Converted {
2961 public Label GetILLabel (EmitContext ec)
2964 il_label = ec.ig.DefineLabel ();
2965 il_label_set = true;
2970 public Label GetILLabelCode (EmitContext ec)
2972 if (!il_label_code_set){
2973 il_label_code = ec.ig.DefineLabel ();
2974 il_label_code_set = true;
2976 return il_label_code;
2980 // Resolves the expression, reduces it to a literal if possible
2981 // and then converts it to the requested type.
2983 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2985 Expression e = label.Resolve (ec);
2990 Constant c = e as Constant;
2992 Report.Error (150, loc, "A constant value is expected");
2996 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2997 converted = NullStringCase;
3001 if (allow_nullable && c.GetValue () == null) {
3002 converted = NullStringCase;
3006 c = c.ImplicitConversionRequired (ec, required_type, loc);
3010 converted = c.GetValue ();
3014 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3017 if (converted == null)
3019 else if (converted == NullStringCase)
3022 label = converted.ToString ();
3024 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3025 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3028 public SwitchLabel Clone (CloneContext clonectx)
3030 return new SwitchLabel (label.Clone (clonectx), loc);
3034 public class SwitchSection {
3035 // An array of SwitchLabels.
3036 public readonly ArrayList Labels;
3037 public readonly Block Block;
3039 public SwitchSection (ArrayList labels, Block block)
3045 public SwitchSection Clone (CloneContext clonectx)
3047 ArrayList cloned_labels = new ArrayList ();
3049 foreach (SwitchLabel sl in cloned_labels)
3050 cloned_labels.Add (sl.Clone (clonectx));
3052 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3056 public class Switch : Statement {
3057 public ArrayList Sections;
3058 public Expression Expr;
3061 /// Maps constants whose type type SwitchType to their SwitchLabels.
3063 public IDictionary Elements;
3066 /// The governing switch type
3068 public Type SwitchType;
3073 Label default_target;
3075 Expression new_expr;
3078 SwitchSection constant_section;
3079 SwitchSection default_section;
3081 ExpressionStatement string_dictionary;
3082 FieldExpr switch_cache_field;
3083 static int unique_counter;
3087 // Nullable Types support for GMCS.
3089 Nullable.Unwrap unwrap;
3091 protected bool HaveUnwrap {
3092 get { return unwrap != null; }
3095 protected bool HaveUnwrap {
3096 get { return false; }
3101 // The types allowed to be implicitly cast from
3102 // on the governing type
3104 static Type [] allowed_types;
3106 public Switch (Expression e, ArrayList sects, Location l)
3113 public bool GotDefault {
3115 return default_section != null;
3119 public Label DefaultTarget {
3121 return default_target;
3126 // Determines the governing type for a switch. The returned
3127 // expression might be the expression from the switch, or an
3128 // expression that includes any potential conversions to the
3129 // integral types or to string.
3131 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3135 if (t == TypeManager.byte_type ||
3136 t == TypeManager.sbyte_type ||
3137 t == TypeManager.ushort_type ||
3138 t == TypeManager.short_type ||
3139 t == TypeManager.uint32_type ||
3140 t == TypeManager.int32_type ||
3141 t == TypeManager.uint64_type ||
3142 t == TypeManager.int64_type ||
3143 t == TypeManager.char_type ||
3144 t == TypeManager.string_type ||
3145 t == TypeManager.bool_type ||
3146 TypeManager.IsEnumType (t))
3149 if (allowed_types == null){
3150 allowed_types = new Type [] {
3151 TypeManager.sbyte_type,
3152 TypeManager.byte_type,
3153 TypeManager.short_type,
3154 TypeManager.ushort_type,
3155 TypeManager.int32_type,
3156 TypeManager.uint32_type,
3157 TypeManager.int64_type,
3158 TypeManager.uint64_type,
3159 TypeManager.char_type,
3160 TypeManager.string_type
3165 // Try to find a *user* defined implicit conversion.
3167 // If there is no implicit conversion, or if there are multiple
3168 // conversions, we have to report an error
3170 Expression converted = null;
3171 foreach (Type tt in allowed_types){
3174 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3179 // Ignore over-worked ImplicitUserConversions that do
3180 // an implicit conversion in addition to the user conversion.
3182 if (!(e is UserCast))
3185 if (converted != null){
3186 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3196 // Performs the basic sanity checks on the switch statement
3197 // (looks for duplicate keys and non-constant expressions).
3199 // It also returns a hashtable with the keys that we will later
3200 // use to compute the switch tables
3202 bool CheckSwitch (EmitContext ec)
3205 Elements = Sections.Count > 10 ?
3206 (IDictionary)new Hashtable () :
3207 (IDictionary)new ListDictionary ();
3209 foreach (SwitchSection ss in Sections){
3210 foreach (SwitchLabel sl in ss.Labels){
3211 if (sl.Label == null){
3212 if (default_section != null){
3213 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3216 default_section = ss;
3220 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3225 object key = sl.Converted;
3226 if (key == SwitchLabel.NullStringCase)
3227 has_null_case = true;
3230 Elements.Add (key, sl);
3231 } catch (ArgumentException) {
3232 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3240 void EmitObjectInteger (ILGenerator ig, object k)
3243 IntConstant.EmitInt (ig, (int) k);
3244 else if (k is Constant) {
3245 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3248 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3251 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3253 IntConstant.EmitInt (ig, (int) (long) k);
3254 ig.Emit (OpCodes.Conv_I8);
3257 LongConstant.EmitLong (ig, (long) k);
3259 else if (k is ulong)
3261 ulong ul = (ulong) k;
3264 IntConstant.EmitInt (ig, unchecked ((int) ul));
3265 ig.Emit (OpCodes.Conv_U8);
3269 LongConstant.EmitLong (ig, unchecked ((long) ul));
3273 IntConstant.EmitInt (ig, (int) ((char) k));
3274 else if (k is sbyte)
3275 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3277 IntConstant.EmitInt (ig, (int) ((byte) k));
3278 else if (k is short)
3279 IntConstant.EmitInt (ig, (int) ((short) k));
3280 else if (k is ushort)
3281 IntConstant.EmitInt (ig, (int) ((ushort) k));
3283 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3285 throw new Exception ("Unhandled case");
3288 // structure used to hold blocks of keys while calculating table switch
3289 class KeyBlock : IComparable
3291 public KeyBlock (long _first)
3293 first = last = _first;
3297 public ArrayList element_keys = null;
3298 // how many items are in the bucket
3299 public int Size = 1;
3302 get { return (int) (last - first + 1); }
3304 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3306 return kb_last.last - kb_first.first + 1;
3308 public int CompareTo (object obj)
3310 KeyBlock kb = (KeyBlock) obj;
3311 int nLength = Length;
3312 int nLengthOther = kb.Length;
3313 if (nLengthOther == nLength)
3314 return (int) (kb.first - first);
3315 return nLength - nLengthOther;
3320 /// This method emits code for a lookup-based switch statement (non-string)
3321 /// Basically it groups the cases into blocks that are at least half full,
3322 /// and then spits out individual lookup opcodes for each block.
3323 /// It emits the longest blocks first, and short blocks are just
3324 /// handled with direct compares.
3326 /// <param name="ec"></param>
3327 /// <param name="val"></param>
3328 /// <returns></returns>
3329 void TableSwitchEmit (EmitContext ec, Expression val)
3331 int element_count = Elements.Count;
3332 object [] element_keys = new object [element_count];
3333 Elements.Keys.CopyTo (element_keys, 0);
3334 Array.Sort (element_keys);
3336 // initialize the block list with one element per key
3337 ArrayList key_blocks = new ArrayList (element_count);
3338 foreach (object key in element_keys)
3339 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3341 KeyBlock current_kb;
3342 // iteratively merge the blocks while they are at least half full
3343 // there's probably a really cool way to do this with a tree...
3344 while (key_blocks.Count > 1)
3346 ArrayList key_blocks_new = new ArrayList ();
3347 current_kb = (KeyBlock) key_blocks [0];
3348 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3350 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3351 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3354 current_kb.last = kb.last;
3355 current_kb.Size += kb.Size;
3359 // start a new block
3360 key_blocks_new.Add (current_kb);
3364 key_blocks_new.Add (current_kb);
3365 if (key_blocks.Count == key_blocks_new.Count)
3367 key_blocks = key_blocks_new;
3370 // initialize the key lists
3371 foreach (KeyBlock kb in key_blocks)
3372 kb.element_keys = new ArrayList ();
3374 // fill the key lists
3376 if (key_blocks.Count > 0) {
3377 current_kb = (KeyBlock) key_blocks [0];
3378 foreach (object key in element_keys)
3380 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3381 System.Convert.ToInt64 (key) > current_kb.last;
3383 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3384 current_kb.element_keys.Add (key);
3388 // sort the blocks so we can tackle the largest ones first
3391 // okay now we can start...
3392 ILGenerator ig = ec.ig;
3393 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3394 Label lbl_default = default_target;
3396 Type type_keys = null;
3397 if (element_keys.Length > 0)
3398 type_keys = element_keys [0].GetType (); // used for conversions
3402 if (TypeManager.IsEnumType (SwitchType))
3403 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3405 compare_type = SwitchType;
3407 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3409 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3410 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3413 foreach (object key in kb.element_keys) {
3414 SwitchLabel sl = (SwitchLabel) Elements [key];
3415 if (key is int && (int) key == 0) {
3416 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3419 EmitObjectInteger (ig, key);
3420 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3426 // TODO: if all the keys in the block are the same and there are
3427 // no gaps/defaults then just use a range-check.
3428 if (compare_type == TypeManager.int64_type ||
3429 compare_type == TypeManager.uint64_type)
3431 // TODO: optimize constant/I4 cases
3433 // check block range (could be > 2^31)
3435 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3436 ig.Emit (OpCodes.Blt, lbl_default);
3438 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3439 ig.Emit (OpCodes.Bgt, lbl_default);
3445 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3446 ig.Emit (OpCodes.Sub);
3448 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3454 int first = (int) kb.first;
3457 IntConstant.EmitInt (ig, first);
3458 ig.Emit (OpCodes.Sub);
3462 IntConstant.EmitInt (ig, -first);
3463 ig.Emit (OpCodes.Add);
3467 // first, build the list of labels for the switch
3469 int cJumps = kb.Length;
3470 Label [] switch_labels = new Label [cJumps];
3471 for (int iJump = 0; iJump < cJumps; iJump++)
3473 object key = kb.element_keys [iKey];
3474 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3476 SwitchLabel sl = (SwitchLabel) Elements [key];
3477 switch_labels [iJump] = sl.GetILLabel (ec);
3481 switch_labels [iJump] = lbl_default;
3483 // emit the switch opcode
3484 ig.Emit (OpCodes.Switch, switch_labels);
3487 // mark the default for this block
3489 ig.MarkLabel (lbl_default);
3492 // TODO: find the default case and emit it here,
3493 // to prevent having to do the following jump.
3494 // make sure to mark other labels in the default section
3496 // the last default just goes to the end
3497 if (element_keys.Length > 0)
3498 ig.Emit (OpCodes.Br, lbl_default);
3500 // now emit the code for the sections
3501 bool found_default = false;
3503 foreach (SwitchSection ss in Sections) {
3504 foreach (SwitchLabel sl in ss.Labels) {
3505 if (sl.Converted == SwitchLabel.NullStringCase) {
3506 ig.MarkLabel (null_target);
3507 } else if (sl.Label == null) {
3508 ig.MarkLabel (lbl_default);
3509 found_default = true;
3511 ig.MarkLabel (null_target);
3513 ig.MarkLabel (sl.GetILLabel (ec));
3514 ig.MarkLabel (sl.GetILLabelCode (ec));
3519 if (!found_default) {
3520 ig.MarkLabel (lbl_default);
3521 if (!has_null_case) {
3522 ig.MarkLabel (null_target);
3526 ig.MarkLabel (lbl_end);
3529 SwitchSection FindSection (SwitchLabel label)
3531 foreach (SwitchSection ss in Sections){
3532 foreach (SwitchLabel sl in ss.Labels){
3541 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3543 foreach (SwitchSection ss in Sections)
3544 ss.Block.MutateHoistedGenericType (storey);
3547 public static void Reset ()
3552 public override bool Resolve (EmitContext ec)
3554 Expr = Expr.Resolve (ec);
3558 new_expr = SwitchGoverningType (ec, Expr);
3561 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3562 unwrap = Nullable.Unwrap.Create (Expr, ec);
3566 new_expr = SwitchGoverningType (ec, unwrap);
3570 if (new_expr == null){
3571 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3576 SwitchType = new_expr.Type;
3578 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3579 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3583 if (!CheckSwitch (ec))
3587 Elements.Remove (SwitchLabel.NullStringCase);
3589 Switch old_switch = ec.Switch;
3591 ec.Switch.SwitchType = SwitchType;
3593 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3594 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3596 is_constant = new_expr is Constant;
3598 object key = ((Constant) new_expr).GetValue ();
3599 SwitchLabel label = (SwitchLabel) Elements [key];
3601 constant_section = FindSection (label);
3602 if (constant_section == null)
3603 constant_section = default_section;
3608 foreach (SwitchSection ss in Sections){
3610 ec.CurrentBranching.CreateSibling (
3611 null, FlowBranching.SiblingType.SwitchSection);
3615 if (is_constant && (ss != constant_section)) {
3616 // If we're a constant switch, we're only emitting
3617 // one single section - mark all the others as
3619 ec.CurrentBranching.CurrentUsageVector.Goto ();
3620 if (!ss.Block.ResolveUnreachable (ec, true)) {
3624 if (!ss.Block.Resolve (ec))
3629 if (default_section == null)
3630 ec.CurrentBranching.CreateSibling (
3631 null, FlowBranching.SiblingType.SwitchSection);
3633 ec.EndFlowBranching ();
3634 ec.Switch = old_switch;
3636 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3641 if (SwitchType == TypeManager.string_type && !is_constant) {
3642 // TODO: Optimize single case, and single+default case
3643 ResolveStringSwitchMap (ec);
3649 void ResolveStringSwitchMap (EmitContext ec)
3651 FullNamedExpression string_dictionary_type;
3653 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3654 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3656 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3657 new TypeArguments (loc,
3658 new TypeExpression (TypeManager.string_type, loc),
3659 new TypeExpression (TypeManager.int32_type, loc)), loc);
3661 MemberAccess system_collections_generic = new MemberAccess (
3662 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3664 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3666 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3667 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3668 CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), null, loc);
3669 if (!field.Define ())
3671 ec.TypeContainer.PartialContainer.AddField (field);
3673 ArrayList init = new ArrayList ();
3676 string value = null;
3677 foreach (SwitchSection section in Sections) {
3678 foreach (SwitchLabel sl in section.Labels) {
3679 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3684 value = (string) sl.Converted;
3685 ArrayList init_args = new ArrayList (2);
3686 init_args.Add (new StringLiteral (value, sl.Location));
3687 init_args.Add (new IntConstant (counter, loc));
3688 init.Add (new CollectionElementInitializer (init_args, loc));
3694 Elements.Add (counter, section.Labels [0]);
3698 ArrayList args = new ArrayList (1);
3699 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3700 Expression initializer = new NewInitialize (string_dictionary_type, args,
3701 new CollectionOrObjectInitializers (init, loc), loc);
3703 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3704 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3707 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3709 ILGenerator ig = ec.ig;
3710 Label l_initialized = ig.DefineLabel ();
3713 // Skip initialization when value is null
3715 value.EmitBranchable (ec, null_target, false);
3718 // Check if string dictionary is initialized and initialize
3720 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3721 string_dictionary.EmitStatement (ec);
3722 ig.MarkLabel (l_initialized);
3724 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3727 ArrayList get_value_args = new ArrayList (2);
3728 get_value_args.Add (new Argument (value));
3729 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3730 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3731 if (get_item == null)
3735 // A value was not found, go to default case
3737 get_item.EmitBranchable (ec, default_target, false);
3739 ArrayList get_value_args = new ArrayList (1);
3740 get_value_args.Add (value);
3742 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3743 if (get_item == null)
3746 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3747 get_item_object.EmitAssign (ec, get_item, true, false);
3748 ec.ig.Emit (OpCodes.Brfalse, default_target);
3750 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3751 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3753 get_item_int.EmitStatement (ec);
3754 get_item_object.Release (ec);
3756 TableSwitchEmit (ec, string_switch_variable);
3757 string_switch_variable.Release (ec);
3760 protected override void DoEmit (EmitContext ec)
3762 ILGenerator ig = ec.ig;
3764 default_target = ig.DefineLabel ();
3765 null_target = ig.DefineLabel ();
3767 // Store variable for comparission purposes
3768 // TODO: Don't duplicate non-captured VariableReference
3769 LocalTemporary value;
3771 value = new LocalTemporary (SwitchType);
3773 unwrap.EmitCheck (ec);
3774 ig.Emit (OpCodes.Brfalse, null_target);
3778 } else if (!is_constant) {
3779 value = new LocalTemporary (SwitchType);
3786 // Setup the codegen context
3788 Label old_end = ec.LoopEnd;
3789 Switch old_switch = ec.Switch;
3791 ec.LoopEnd = ig.DefineLabel ();
3796 if (constant_section != null)
3797 constant_section.Block.Emit (ec);
3798 } else if (string_dictionary != null) {
3799 DoEmitStringSwitch (value, ec);
3801 TableSwitchEmit (ec, value);
3807 // Restore context state.
3808 ig.MarkLabel (ec.LoopEnd);
3811 // Restore the previous context
3813 ec.LoopEnd = old_end;
3814 ec.Switch = old_switch;
3817 protected override void CloneTo (CloneContext clonectx, Statement t)
3819 Switch target = (Switch) t;
3821 target.Expr = Expr.Clone (clonectx);
3822 target.Sections = new ArrayList ();
3823 foreach (SwitchSection ss in Sections){
3824 target.Sections.Add (ss.Clone (clonectx));
3829 // A place where execution can restart in an iterator
3830 public abstract class ResumableStatement : Statement
3833 protected Label resume_point;
3835 public Label PrepareForEmit (EmitContext ec)
3839 resume_point = ec.ig.DefineLabel ();
3841 return resume_point;
3844 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3848 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3853 // Base class for statements that are implemented in terms of try...finally
3854 public abstract class ExceptionStatement : ResumableStatement
3858 protected abstract void EmitPreTryBody (EmitContext ec);
3859 protected abstract void EmitTryBody (EmitContext ec);
3860 protected abstract void EmitFinallyBody (EmitContext ec);
3862 protected sealed override void DoEmit (EmitContext ec)
3864 ILGenerator ig = ec.ig;
3866 EmitPreTryBody (ec);
3868 if (resume_points != null) {
3869 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3870 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3873 ig.BeginExceptionBlock ();
3875 if (resume_points != null) {
3876 ig.MarkLabel (resume_point);
3878 // For normal control flow, we want to fall-through the Switch
3879 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3880 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3881 IntConstant.EmitInt (ig, first_resume_pc);
3882 ig.Emit (OpCodes.Sub);
3884 Label [] labels = new Label [resume_points.Count];
3885 for (int i = 0; i < resume_points.Count; ++i)
3886 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3887 ig.Emit (OpCodes.Switch, labels);
3892 ig.BeginFinallyBlock ();
3894 Label start_finally = ec.ig.DefineLabel ();
3895 if (resume_points != null) {
3896 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3897 ig.Emit (OpCodes.Brfalse_S, start_finally);
3898 ig.Emit (OpCodes.Endfinally);
3901 ig.MarkLabel (start_finally);
3902 EmitFinallyBody (ec);
3904 ig.EndExceptionBlock ();
3907 public void SomeCodeFollows ()
3909 code_follows = true;
3912 protected void ResolveReachability (EmitContext ec)
3914 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3915 // So, ensure there's some IL code after this statement.
3916 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3917 ec.NeedReturnLabel ();
3921 ArrayList resume_points;
3922 int first_resume_pc;
3923 public void AddResumePoint (ResumableStatement stmt, int pc)
3925 if (resume_points == null) {
3926 resume_points = new ArrayList ();
3927 first_resume_pc = pc;
3930 if (pc != first_resume_pc + resume_points.Count)
3931 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3933 resume_points.Add (stmt);
3936 Label dispose_try_block;
3937 bool prepared_for_dispose, emitted_dispose;
3938 public override Label PrepareForDispose (EmitContext ec, Label end)
3940 if (!prepared_for_dispose) {
3941 prepared_for_dispose = true;
3942 dispose_try_block = ec.ig.DefineLabel ();
3944 return dispose_try_block;
3947 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3949 if (emitted_dispose)
3952 emitted_dispose = true;
3954 ILGenerator ig = ec.ig;
3956 Label end_of_try = ig.DefineLabel ();
3958 // Ensure that the only way we can get into this code is through a dispatcher
3959 if (have_dispatcher)
3960 ig.Emit (OpCodes.Br, end);
3962 ig.BeginExceptionBlock ();
3964 ig.MarkLabel (dispose_try_block);
3966 Label [] labels = null;
3967 for (int i = 0; i < resume_points.Count; ++i) {
3968 ResumableStatement s = (ResumableStatement) resume_points [i];
3969 Label ret = s.PrepareForDispose (ec, end_of_try);
3970 if (ret.Equals (end_of_try) && labels == null)
3972 if (labels == null) {
3973 labels = new Label [resume_points.Count];
3974 for (int j = 0; j < i; ++j)
3975 labels [j] = end_of_try;
3980 if (labels != null) {
3982 for (j = 1; j < labels.Length; ++j)
3983 if (!labels [0].Equals (labels [j]))
3985 bool emit_dispatcher = j < labels.Length;
3987 if (emit_dispatcher) {
3988 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3989 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3990 IntConstant.EmitInt (ig, first_resume_pc);
3991 ig.Emit (OpCodes.Sub);
3992 ig.Emit (OpCodes.Switch, labels);
3993 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3996 foreach (ResumableStatement s in resume_points)
3997 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4000 ig.MarkLabel (end_of_try);
4002 ig.BeginFinallyBlock ();
4004 EmitFinallyBody (ec);
4006 ig.EndExceptionBlock ();
4010 public class Lock : ExceptionStatement {
4012 public Statement Statement;
4013 TemporaryVariable temp;
4015 public Lock (Expression expr, Statement stmt, Location l)
4022 public override bool Resolve (EmitContext ec)
4024 expr = expr.Resolve (ec);
4028 if (expr.Type.IsValueType){
4029 Report.Error (185, loc,
4030 "`{0}' is not a reference type as required by the lock statement",
4031 TypeManager.CSharpName (expr.Type));
4035 ec.StartFlowBranching (this);
4036 bool ok = Statement.Resolve (ec);
4037 ec.EndFlowBranching ();
4039 ResolveReachability (ec);
4041 // Avoid creating libraries that reference the internal
4044 if (t == TypeManager.null_type)
4045 t = TypeManager.object_type;
4047 temp = new TemporaryVariable (t, loc);
4050 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4051 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4052 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4053 monitor_type, "Enter", loc, TypeManager.object_type);
4054 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4055 monitor_type, "Exit", loc, TypeManager.object_type);
4061 protected override void EmitPreTryBody (EmitContext ec)
4063 ILGenerator ig = ec.ig;
4065 temp.EmitAssign (ec, expr);
4067 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4070 protected override void EmitTryBody (EmitContext ec)
4072 Statement.Emit (ec);
4075 protected override void EmitFinallyBody (EmitContext ec)
4078 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4081 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4083 expr.MutateHoistedGenericType (storey);
4084 temp.MutateHoistedGenericType (storey);
4085 Statement.MutateHoistedGenericType (storey);
4088 protected override void CloneTo (CloneContext clonectx, Statement t)
4090 Lock target = (Lock) t;
4092 target.expr = expr.Clone (clonectx);
4093 target.Statement = Statement.Clone (clonectx);
4097 public class Unchecked : Statement {
4100 public Unchecked (Block b)
4106 public override bool Resolve (EmitContext ec)
4108 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4109 return Block.Resolve (ec);
4112 protected override void DoEmit (EmitContext ec)
4114 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4118 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4120 Block.MutateHoistedGenericType (storey);
4123 protected override void CloneTo (CloneContext clonectx, Statement t)
4125 Unchecked target = (Unchecked) t;
4127 target.Block = clonectx.LookupBlock (Block);
4131 public class Checked : Statement {
4134 public Checked (Block b)
4137 b.Unchecked = false;
4140 public override bool Resolve (EmitContext ec)
4142 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4143 return Block.Resolve (ec);
4146 protected override void DoEmit (EmitContext ec)
4148 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4152 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4154 Block.MutateHoistedGenericType (storey);
4157 protected override void CloneTo (CloneContext clonectx, Statement t)
4159 Checked target = (Checked) t;
4161 target.Block = clonectx.LookupBlock (Block);
4165 public class Unsafe : Statement {
4168 public Unsafe (Block b)
4171 Block.Unsafe = true;
4174 public override bool Resolve (EmitContext ec)
4176 using (ec.With (EmitContext.Flags.InUnsafe, true))
4177 return Block.Resolve (ec);
4180 protected override void DoEmit (EmitContext ec)
4182 using (ec.With (EmitContext.Flags.InUnsafe, true))
4186 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4188 Block.MutateHoistedGenericType (storey);
4191 protected override void CloneTo (CloneContext clonectx, Statement t)
4193 Unsafe target = (Unsafe) t;
4195 target.Block = clonectx.LookupBlock (Block);
4202 public class Fixed : Statement {
4204 ArrayList declarators;
4205 Statement statement;
4210 abstract class Emitter
4212 protected LocalInfo vi;
4213 protected Expression converted;
4215 protected Emitter (Expression expr, LocalInfo li)
4221 public abstract void Emit (EmitContext ec);
4222 public abstract void EmitExit (EmitContext ec);
4225 class ExpressionEmitter : Emitter {
4226 public ExpressionEmitter (Expression converted, LocalInfo li) :
4227 base (converted, li)
4231 public override void Emit (EmitContext ec) {
4233 // Store pointer in pinned location
4235 converted.Emit (ec);
4239 public override void EmitExit (EmitContext ec)
4241 ec.ig.Emit (OpCodes.Ldc_I4_0);
4242 ec.ig.Emit (OpCodes.Conv_U);
4247 class StringEmitter : Emitter {
4248 class StringPtr : Expression
4252 public StringPtr (LocalBuilder b, Location l)
4255 eclass = ExprClass.Value;
4256 type = TypeManager.char_ptr_type;
4260 public override Expression CreateExpressionTree (EmitContext ec)
4262 throw new NotSupportedException ("ET");
4265 public override Expression DoResolve (EmitContext ec)
4267 // This should never be invoked, we are born in fully
4268 // initialized state.
4273 public override void Emit (EmitContext ec)
4275 if (TypeManager.int_get_offset_to_string_data == null) {
4276 // TODO: Move to resolve !!
4277 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4278 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4281 ILGenerator ig = ec.ig;
4283 ig.Emit (OpCodes.Ldloc, b);
4284 ig.Emit (OpCodes.Conv_I);
4285 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4286 ig.Emit (OpCodes.Add);
4290 LocalBuilder pinned_string;
4293 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4299 public override void Emit (EmitContext ec)
4301 ILGenerator ig = ec.ig;
4302 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4304 converted.Emit (ec);
4305 ig.Emit (OpCodes.Stloc, pinned_string);
4307 Expression sptr = new StringPtr (pinned_string, loc);
4308 converted = Convert.ImplicitConversionRequired (
4309 ec, sptr, vi.VariableType, loc);
4311 if (converted == null)
4314 converted.Emit (ec);
4318 public override void EmitExit (EmitContext ec)
4320 ec.ig.Emit (OpCodes.Ldnull);
4321 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4325 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4328 declarators = decls;
4333 public Statement Statement {
4334 get { return statement; }
4337 public override bool Resolve (EmitContext ec)
4340 Expression.UnsafeError (loc);
4344 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4345 if (texpr == null) {
4346 if (type is VarExpr)
4347 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4352 expr_type = texpr.Type;
4354 data = new Emitter [declarators.Count];
4356 if (!expr_type.IsPointer){
4357 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4362 foreach (Pair p in declarators){
4363 LocalInfo vi = (LocalInfo) p.First;
4364 Expression e = (Expression) p.Second;
4366 vi.VariableInfo.SetAssigned (ec);
4367 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4370 // The rules for the possible declarators are pretty wise,
4371 // but the production on the grammar is more concise.
4373 // So we have to enforce these rules here.
4375 // We do not resolve before doing the case 1 test,
4376 // because the grammar is explicit in that the token &
4377 // is present, so we need to test for this particular case.
4381 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4385 ec.InFixedInitializer = true;
4387 ec.InFixedInitializer = false;
4394 if (e.Type.IsArray){
4395 Type array_type = TypeManager.GetElementType (e.Type);
4398 // Provided that array_type is unmanaged,
4400 if (!TypeManager.VerifyUnManaged (array_type, loc))
4404 // and T* is implicitly convertible to the
4405 // pointer type given in the fixed statement.
4407 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4409 Expression converted = Convert.ImplicitConversionRequired (
4410 ec, array_ptr, vi.VariableType, loc);
4411 if (converted == null)
4415 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4417 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4418 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4419 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4420 new NullPointer (loc),
4423 converted = converted.Resolve (ec);
4425 data [i] = new ExpressionEmitter (converted, vi);
4434 if (e.Type == TypeManager.string_type){
4435 data [i] = new StringEmitter (e, vi, loc);
4440 // Case 4: fixed buffer
4441 if (e is FixedBufferPtr) {
4442 data [i++] = new ExpressionEmitter (e, vi);
4447 // Case 1: & object.
4449 Unary u = e as Unary;
4450 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4451 IVariableReference vr = u.Expr as IVariableReference;
4452 if (vr == null || !vr.IsFixedVariable) {
4453 data [i] = new ExpressionEmitter (e, vi);
4457 if (data [i++] == null)
4458 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4460 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4463 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4464 bool ok = statement.Resolve (ec);
4465 bool flow_unreachable = ec.EndFlowBranching ();
4466 has_ret = flow_unreachable;
4471 protected override void DoEmit (EmitContext ec)
4473 for (int i = 0; i < data.Length; i++) {
4477 statement.Emit (ec);
4483 // Clear the pinned variable
4485 for (int i = 0; i < data.Length; i++) {
4486 data [i].EmitExit (ec);
4490 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4492 // Fixed statement cannot be used inside anonymous methods or lambdas
4493 throw new NotSupportedException ();
4496 protected override void CloneTo (CloneContext clonectx, Statement t)
4498 Fixed target = (Fixed) t;
4500 target.type = type.Clone (clonectx);
4501 target.declarators = new ArrayList (declarators.Count);
4502 foreach (Pair p in declarators) {
4503 LocalInfo vi = (LocalInfo) p.First;
4504 Expression e = (Expression) p.Second;
4506 target.declarators.Add (
4507 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4510 target.statement = statement.Clone (clonectx);
4514 public class Catch : Statement {
4515 public readonly string Name;
4517 public Block VarBlock;
4519 Expression type_expr;
4522 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4527 VarBlock = var_block;
4531 public Type CatchType {
4537 public bool IsGeneral {
4539 return type_expr == null;
4543 protected override void DoEmit (EmitContext ec)
4545 ILGenerator ig = ec.ig;
4547 if (CatchType != null)
4548 ig.BeginCatchBlock (CatchType);
4550 ig.BeginCatchBlock (TypeManager.object_type);
4552 if (VarBlock != null)
4556 // TODO: Move to resolve
4557 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4561 if (lvr.IsHoisted) {
4562 LocalTemporary lt = new LocalTemporary (lvr.Type);
4566 // Variable is at the top of the stack
4567 source = EmptyExpression.Null;
4570 lvr.EmitAssign (ec, source, false, false);
4572 ig.Emit (OpCodes.Pop);
4577 public override bool Resolve (EmitContext ec)
4579 using (ec.With (EmitContext.Flags.InCatch, true)) {
4580 if (type_expr != null) {
4581 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4587 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4588 Error (155, "The type caught or thrown must be derived from System.Exception");
4594 if (!Block.Resolve (ec))
4597 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4598 // emit the "unused variable" warnings.
4599 if (VarBlock != null)
4600 return VarBlock.Resolve (ec);
4606 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4609 type = storey.MutateType (type);
4610 if (VarBlock != null)
4611 VarBlock.MutateHoistedGenericType (storey);
4612 Block.MutateHoistedGenericType (storey);
4615 protected override void CloneTo (CloneContext clonectx, Statement t)
4617 Catch target = (Catch) t;
4619 if (type_expr != null)
4620 target.type_expr = type_expr.Clone (clonectx);
4621 if (VarBlock != null)
4622 target.VarBlock = clonectx.LookupBlock (VarBlock);
4623 target.Block = clonectx.LookupBlock (Block);
4627 public class TryFinally : ExceptionStatement {
4631 public TryFinally (Statement stmt, Block fini, Location l)
4638 public override bool Resolve (EmitContext ec)
4642 ec.StartFlowBranching (this);
4644 if (!stmt.Resolve (ec))
4648 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4649 using (ec.With (EmitContext.Flags.InFinally, true)) {
4650 if (!fini.Resolve (ec))
4654 ec.EndFlowBranching ();
4656 ResolveReachability (ec);
4661 protected override void EmitPreTryBody (EmitContext ec)
4665 protected override void EmitTryBody (EmitContext ec)
4670 protected override void EmitFinallyBody (EmitContext ec)
4675 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4677 stmt.MutateHoistedGenericType (storey);
4678 fini.MutateHoistedGenericType (storey);
4681 protected override void CloneTo (CloneContext clonectx, Statement t)
4683 TryFinally target = (TryFinally) t;
4685 target.stmt = (Statement) stmt.Clone (clonectx);
4687 target.fini = clonectx.LookupBlock (fini);
4691 public class TryCatch : Statement {
4693 public ArrayList Specific;
4694 public Catch General;
4695 bool inside_try_finally, code_follows;
4697 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4700 this.Specific = catch_clauses;
4701 this.General = null;
4702 this.inside_try_finally = inside_try_finally;
4704 for (int i = 0; i < catch_clauses.Count; ++i) {
4705 Catch c = (Catch) catch_clauses [i];
4707 if (i != catch_clauses.Count - 1)
4708 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4710 catch_clauses.RemoveAt (i);
4718 public override bool Resolve (EmitContext ec)
4722 ec.StartFlowBranching (this);
4724 if (!Block.Resolve (ec))
4727 Type[] prev_catches = new Type [Specific.Count];
4729 foreach (Catch c in Specific){
4730 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4732 if (c.Name != null) {
4733 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4735 throw new Exception ();
4737 vi.VariableInfo = null;
4740 if (!c.Resolve (ec))
4743 Type resolved_type = c.CatchType;
4744 for (int ii = 0; ii < last_index; ++ii) {
4745 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4746 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4751 prev_catches [last_index++] = resolved_type;
4754 if (General != null) {
4755 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4756 foreach (Catch c in Specific){
4757 if (c.CatchType == TypeManager.exception_type) {
4758 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'");
4763 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4765 if (!General.Resolve (ec))
4769 ec.EndFlowBranching ();
4771 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4772 // So, ensure there's some IL code after this statement
4773 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4774 ec.NeedReturnLabel ();
4779 public void SomeCodeFollows ()
4781 code_follows = true;
4784 protected override void DoEmit (EmitContext ec)
4786 ILGenerator ig = ec.ig;
4788 if (!inside_try_finally)
4789 ig.BeginExceptionBlock ();
4793 foreach (Catch c in Specific)
4796 if (General != null)
4799 if (!inside_try_finally)
4800 ig.EndExceptionBlock ();
4803 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4805 Block.MutateHoistedGenericType (storey);
4807 if (General != null)
4808 General.MutateHoistedGenericType (storey);
4809 if (Specific != null) {
4810 foreach (Catch c in Specific)
4811 c.MutateHoistedGenericType (storey);
4815 protected override void CloneTo (CloneContext clonectx, Statement t)
4817 TryCatch target = (TryCatch) t;
4819 target.Block = clonectx.LookupBlock (Block);
4820 if (General != null)
4821 target.General = (Catch) General.Clone (clonectx);
4822 if (Specific != null){
4823 target.Specific = new ArrayList ();
4824 foreach (Catch c in Specific)
4825 target.Specific.Add (c.Clone (clonectx));
4830 public class UsingTemporary : ExceptionStatement {
4831 TemporaryVariable local_copy;
4832 public Statement Statement;
4836 public UsingTemporary (Expression expr, Statement stmt, Location l)
4843 public override bool Resolve (EmitContext ec)
4845 expr = expr.Resolve (ec);
4849 expr_type = expr.Type;
4851 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4852 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4853 Using.Error_IsNotConvertibleToIDisposable (expr);
4858 local_copy = new TemporaryVariable (expr_type, loc);
4859 local_copy.Resolve (ec);
4861 ec.StartFlowBranching (this);
4863 bool ok = Statement.Resolve (ec);
4865 ec.EndFlowBranching ();
4867 ResolveReachability (ec);
4869 if (TypeManager.void_dispose_void == null) {
4870 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4871 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4877 protected override void EmitPreTryBody (EmitContext ec)
4879 local_copy.EmitAssign (ec, expr);
4882 protected override void EmitTryBody (EmitContext ec)
4884 Statement.Emit (ec);
4887 protected override void EmitFinallyBody (EmitContext ec)
4889 ILGenerator ig = ec.ig;
4890 if (!expr_type.IsValueType) {
4891 Label skip = ig.DefineLabel ();
4892 local_copy.Emit (ec);
4893 ig.Emit (OpCodes.Brfalse, skip);
4894 local_copy.Emit (ec);
4895 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4896 ig.MarkLabel (skip);
4900 Expression ml = Expression.MemberLookup (
4901 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4902 "Dispose", Location.Null);
4904 if (!(ml is MethodGroupExpr)) {
4905 local_copy.Emit (ec);
4906 ig.Emit (OpCodes.Box, expr_type);
4907 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4911 MethodInfo mi = null;
4913 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4914 if (TypeManager.GetParameterData (mk).Count == 0) {
4921 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4925 local_copy.AddressOf (ec, AddressOp.Load);
4926 ig.Emit (OpCodes.Call, mi);
4929 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4931 expr_type = storey.MutateType (expr_type);
4932 local_copy.MutateHoistedGenericType (storey);
4933 Statement.MutateHoistedGenericType (storey);
4936 protected override void CloneTo (CloneContext clonectx, Statement t)
4938 UsingTemporary target = (UsingTemporary) t;
4940 target.expr = expr.Clone (clonectx);
4941 target.Statement = Statement.Clone (clonectx);
4945 public class Using : ExceptionStatement {
4947 public Statement EmbeddedStatement {
4948 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4954 Expression converted_var;
4955 ExpressionStatement assign;
4957 public Using (Expression var, Expression init, Statement stmt, Location l)
4965 bool ResolveVariable (EmitContext ec)
4967 ExpressionStatement a = new SimpleAssign (var, init, loc);
4968 a = a.ResolveStatement (ec);
4974 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4975 converted_var = var;
4979 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4981 Error_IsNotConvertibleToIDisposable (var);
4990 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4992 Report.SymbolRelatedToPreviousError (expr.Type);
4993 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4994 expr.GetSignatureForError ());
4997 protected override void EmitPreTryBody (EmitContext ec)
4999 assign.EmitStatement (ec);
5002 protected override void EmitTryBody (EmitContext ec)
5007 protected override void EmitFinallyBody (EmitContext ec)
5009 ILGenerator ig = ec.ig;
5011 if (!var.Type.IsValueType) {
5012 Label skip = ig.DefineLabel ();
5014 ig.Emit (OpCodes.Brfalse, skip);
5015 converted_var.Emit (ec);
5016 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5017 ig.MarkLabel (skip);
5019 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5021 if (!(ml is MethodGroupExpr)) {
5023 ig.Emit (OpCodes.Box, var.Type);
5024 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5026 MethodInfo mi = null;
5028 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5029 if (TypeManager.GetParameterData (mk).Count == 0) {
5036 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5040 IMemoryLocation mloc = (IMemoryLocation) var;
5042 mloc.AddressOf (ec, AddressOp.Load);
5043 ig.Emit (OpCodes.Call, mi);
5048 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5050 assign.MutateHoistedGenericType (storey);
5051 var.MutateHoistedGenericType (storey);
5052 stmt.MutateHoistedGenericType (storey);
5055 public override bool Resolve (EmitContext ec)
5057 if (!ResolveVariable (ec))
5060 ec.StartFlowBranching (this);
5062 bool ok = stmt.Resolve (ec);
5064 ec.EndFlowBranching ();
5066 ResolveReachability (ec);
5068 if (TypeManager.void_dispose_void == null) {
5069 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5070 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5076 protected override void CloneTo (CloneContext clonectx, Statement t)
5078 Using target = (Using) t;
5080 target.var = var.Clone (clonectx);
5081 target.init = init.Clone (clonectx);
5082 target.stmt = stmt.Clone (clonectx);
5087 /// Implementation of the foreach C# statement
5089 public class Foreach : Statement {
5091 Expression variable;
5093 Statement statement;
5095 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5096 Statement stmt, Location l)
5099 this.variable = var;
5105 public Statement Statement {
5106 get { return statement; }
5109 public override bool Resolve (EmitContext ec)
5111 expr = expr.Resolve (ec);
5116 Report.Error (186, loc, "Use of null is not valid in this context");
5120 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5121 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5122 expr.ExprClassName);
5126 if (expr.Type.IsArray) {
5127 statement = new ArrayForeach (type, variable, expr, statement, loc);
5129 statement = new CollectionForeach (type, variable, expr, statement, loc);
5132 return statement.Resolve (ec);
5135 protected override void DoEmit (EmitContext ec)
5137 ILGenerator ig = ec.ig;
5139 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5140 ec.LoopBegin = ig.DefineLabel ();
5141 ec.LoopEnd = ig.DefineLabel ();
5143 statement.Emit (ec);
5145 ec.LoopBegin = old_begin;
5146 ec.LoopEnd = old_end;
5149 protected class ArrayCounter : TemporaryVariable
5151 StatementExpression increment;
5153 public ArrayCounter (Location loc)
5154 : base (TypeManager.int32_type, loc)
5158 public void ResolveIncrement (EmitContext ec)
5160 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5161 increment.Resolve (ec);
5164 public void EmitIncrement (EmitContext ec)
5166 increment.Emit (ec);
5170 protected class ArrayForeach : Statement
5172 Expression variable, expr, conv;
5173 Statement statement;
5175 Expression var_type;
5176 TemporaryVariable[] lengths;
5177 ArrayCounter[] counter;
5180 TemporaryVariable copy;
5182 Expression[] length_exprs;
5184 public ArrayForeach (Expression var_type, Expression var,
5185 Expression expr, Statement stmt, Location l)
5187 this.var_type = var_type;
5188 this.variable = var;
5194 protected override void CloneTo (CloneContext clonectx, Statement target)
5196 throw new NotImplementedException ();
5199 public override bool Resolve (EmitContext ec)
5201 array_type = expr.Type;
5202 rank = array_type.GetArrayRank ();
5204 copy = new TemporaryVariable (array_type, loc);
5207 counter = new ArrayCounter [rank];
5208 lengths = new TemporaryVariable [rank];
5209 length_exprs = new Expression [rank];
5211 ArrayList list = new ArrayList (rank);
5212 for (int i = 0; i < rank; i++) {
5213 counter [i] = new ArrayCounter (loc);
5214 counter [i].ResolveIncrement (ec);
5216 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5217 lengths [i].Resolve (ec);
5220 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5222 ArrayList args = new ArrayList (1);
5223 args.Add (new Argument (new IntConstant (i, loc)));
5224 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5227 list.Add (counter [i]);
5230 access = new ElementAccess (copy, list).Resolve (ec);
5234 VarExpr ve = var_type as VarExpr;
5236 // Infer implicitly typed local variable from foreach array type
5237 var_type = new TypeExpression (access.Type, ve.Location);
5240 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5241 if (var_type == null)
5244 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5250 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5251 ec.CurrentBranching.CreateSibling ();
5253 variable = variable.ResolveLValue (ec, conv, loc);
5254 if (variable == null)
5257 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5258 if (!statement.Resolve (ec))
5260 ec.EndFlowBranching ();
5262 // There's no direct control flow from the end of the embedded statement to the end of the loop
5263 ec.CurrentBranching.CurrentUsageVector.Goto ();
5265 ec.EndFlowBranching ();
5270 protected override void DoEmit (EmitContext ec)
5272 ILGenerator ig = ec.ig;
5274 copy.EmitAssign (ec, expr);
5276 Label[] test = new Label [rank];
5277 Label[] loop = new Label [rank];
5279 for (int i = 0; i < rank; i++) {
5280 test [i] = ig.DefineLabel ();
5281 loop [i] = ig.DefineLabel ();
5283 lengths [i].EmitAssign (ec, length_exprs [i]);
5286 IntConstant zero = new IntConstant (0, loc);
5287 for (int i = 0; i < rank; i++) {
5288 counter [i].EmitAssign (ec, zero);
5290 ig.Emit (OpCodes.Br, test [i]);
5291 ig.MarkLabel (loop [i]);
5294 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5296 statement.Emit (ec);
5298 ig.MarkLabel (ec.LoopBegin);
5300 for (int i = rank - 1; i >= 0; i--){
5301 counter [i].EmitIncrement (ec);
5303 ig.MarkLabel (test [i]);
5304 counter [i].Emit (ec);
5305 lengths [i].Emit (ec);
5306 ig.Emit (OpCodes.Blt, loop [i]);
5309 ig.MarkLabel (ec.LoopEnd);
5312 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5314 copy.MutateHoistedGenericType (storey);
5315 conv.MutateHoistedGenericType (storey);
5316 variable.MutateHoistedGenericType (storey);
5317 statement.MutateHoistedGenericType (storey);
5319 for (int i = 0; i < rank; i++) {
5320 counter [i].MutateHoistedGenericType (storey);
5321 lengths [i].MutateHoistedGenericType (storey);
5326 protected class CollectionForeach : Statement
5328 Expression variable, expr;
5329 Statement statement;
5331 TemporaryVariable enumerator;
5336 MethodGroupExpr get_enumerator;
5337 PropertyExpr get_current;
5338 MethodInfo move_next;
5339 Expression var_type;
5340 Type enumerator_type;
5341 bool enumerator_found;
5343 public CollectionForeach (Expression var_type, Expression var,
5344 Expression expr, Statement stmt, Location l)
5346 this.var_type = var_type;
5347 this.variable = var;
5353 protected override void CloneTo (CloneContext clonectx, Statement target)
5355 throw new NotImplementedException ();
5358 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5360 Type return_type = mi.ReturnType;
5362 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5364 // Apply the same optimization as MS: skip the GetEnumerator
5365 // returning an IEnumerator, and use the one returning a
5366 // CharEnumerator instead. This allows us to avoid the
5367 // try-finally block and the boxing.
5372 // Ok, we can access it, now make sure that we can do something
5373 // with this `GetEnumerator'
5376 if (return_type == TypeManager.ienumerator_type ||
5377 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5378 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5380 // If it is not an interface, lets try to find the methods ourselves.
5381 // For example, if we have:
5382 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5383 // We can avoid the iface call. This is a runtime perf boost.
5384 // even bigger if we have a ValueType, because we avoid the cost
5387 // We have to make sure that both methods exist for us to take
5388 // this path. If one of the methods does not exist, we will just
5389 // use the interface. Sadly, this complex if statement is the only
5390 // way I could do this without a goto
5393 if (TypeManager.bool_movenext_void == null) {
5394 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5395 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5398 if (TypeManager.ienumerator_getcurrent == null) {
5399 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5400 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5405 // Prefer a generic enumerator over a non-generic one.
5407 if (return_type.IsInterface && return_type.IsGenericType) {
5408 enumerator_type = return_type;
5409 if (!FetchGetCurrent (ec, return_type))
5410 get_current = new PropertyExpr (
5411 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5412 if (!FetchMoveNext (return_type))
5413 move_next = TypeManager.bool_movenext_void;
5418 if (return_type.IsInterface ||
5419 !FetchMoveNext (return_type) ||
5420 !FetchGetCurrent (ec, return_type)) {
5421 enumerator_type = return_type;
5422 move_next = TypeManager.bool_movenext_void;
5423 get_current = new PropertyExpr (
5424 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5429 // Ok, so they dont return an IEnumerable, we will have to
5430 // find if they support the GetEnumerator pattern.
5433 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5434 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",
5435 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5440 enumerator_type = return_type;
5446 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5448 bool FetchMoveNext (Type t)
5450 MemberList move_next_list;
5452 move_next_list = TypeContainer.FindMembers (
5453 t, MemberTypes.Method,
5454 BindingFlags.Public | BindingFlags.Instance,
5455 Type.FilterName, "MoveNext");
5456 if (move_next_list.Count == 0)
5459 foreach (MemberInfo m in move_next_list){
5460 MethodInfo mi = (MethodInfo) m;
5462 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5463 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5473 // Retrieves a `public T get_Current ()' method from the Type `t'
5475 bool FetchGetCurrent (EmitContext ec, Type t)
5477 PropertyExpr pe = Expression.MemberLookup (
5478 ec.ContainerType, t, "Current", MemberTypes.Property,
5479 Expression.AllBindingFlags, loc) as PropertyExpr;
5488 // Retrieves a `public void Dispose ()' method from the Type `t'
5490 static MethodInfo FetchMethodDispose (Type t)
5492 MemberList dispose_list;
5494 dispose_list = TypeContainer.FindMembers (
5495 t, MemberTypes.Method,
5496 BindingFlags.Public | BindingFlags.Instance,
5497 Type.FilterName, "Dispose");
5498 if (dispose_list.Count == 0)
5501 foreach (MemberInfo m in dispose_list){
5502 MethodInfo mi = (MethodInfo) m;
5504 if (TypeManager.GetParameterData (mi).Count == 0){
5505 if (mi.ReturnType == TypeManager.void_type)
5512 void Error_Enumerator ()
5514 if (enumerator_found) {
5518 Report.Error (1579, loc,
5519 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5520 TypeManager.CSharpName (expr.Type));
5523 bool IsOverride (MethodInfo m)
5525 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5527 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5529 if (m is MethodBuilder)
5532 MethodInfo base_method = m.GetBaseDefinition ();
5533 return base_method != m;
5536 bool TryType (EmitContext ec, Type t)
5538 MethodGroupExpr mg = Expression.MemberLookup (
5539 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5540 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5544 MethodInfo result = null;
5545 MethodInfo tmp_move_next = null;
5546 PropertyExpr tmp_get_cur = null;
5547 Type tmp_enumerator_type = enumerator_type;
5548 foreach (MethodInfo mi in mg.Methods) {
5549 if (TypeManager.GetParameterData (mi).Count != 0)
5552 // Check whether GetEnumerator is public
5553 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5556 if (IsOverride (mi))
5559 enumerator_found = true;
5561 if (!GetEnumeratorFilter (ec, mi))
5564 if (result != null) {
5565 if (TypeManager.IsGenericType (result.ReturnType)) {
5566 if (!TypeManager.IsGenericType (mi.ReturnType))
5569 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5570 Report.SymbolRelatedToPreviousError (t);
5571 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5572 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5573 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5577 // Always prefer generics enumerators
5578 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5579 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5580 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5583 Report.SymbolRelatedToPreviousError (result);
5584 Report.SymbolRelatedToPreviousError (mi);
5585 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5586 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5591 tmp_move_next = move_next;
5592 tmp_get_cur = get_current;
5593 tmp_enumerator_type = enumerator_type;
5594 if (mi.DeclaringType == t)
5598 if (result != null) {
5599 move_next = tmp_move_next;
5600 get_current = tmp_get_cur;
5601 enumerator_type = tmp_enumerator_type;
5602 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5603 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5605 if (t != expr.Type) {
5606 expr = Convert.ExplicitConversion (
5609 throw new InternalErrorException ();
5612 get_enumerator.InstanceExpression = expr;
5613 get_enumerator.IsBase = t != expr.Type;
5621 bool ProbeCollectionType (EmitContext ec, Type t)
5623 int errors = Report.Errors;
5624 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5625 if (TryType (ec, tt))
5630 if (Report.Errors > errors)
5634 // Now try to find the method in the interfaces
5636 Type [] ifaces = TypeManager.GetInterfaces (t);
5637 foreach (Type i in ifaces){
5638 if (TryType (ec, i))
5645 public override bool Resolve (EmitContext ec)
5647 enumerator_type = TypeManager.ienumerator_type;
5649 if (!ProbeCollectionType (ec, expr.Type)) {
5650 Error_Enumerator ();
5654 bool is_disposable = !enumerator_type.IsSealed ||
5655 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5657 VarExpr ve = var_type as VarExpr;
5659 // Infer implicitly typed local variable from foreach enumerable type
5660 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5663 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5664 if (var_type == null)
5667 enumerator = new TemporaryVariable (enumerator_type, loc);
5668 enumerator.Resolve (ec);
5670 init = new Invocation (get_enumerator, null);
5671 init = init.Resolve (ec);
5675 Expression move_next_expr;
5677 MemberInfo[] mi = new MemberInfo[] { move_next };
5678 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5679 mg.InstanceExpression = enumerator;
5681 move_next_expr = new Invocation (mg, null);
5684 get_current.InstanceExpression = enumerator;
5686 Statement block = new CollectionForeachStatement (
5687 var_type.Type, variable, get_current, statement, loc);
5689 loop = new While (move_next_expr, block, loc);
5691 wrapper = is_disposable ?
5692 (Statement) new DisposableWrapper (this) :
5693 (Statement) new NonDisposableWrapper (this);
5694 return wrapper.Resolve (ec);
5697 protected override void DoEmit (EmitContext ec)
5702 class NonDisposableWrapper : Statement {
5703 CollectionForeach parent;
5705 internal NonDisposableWrapper (CollectionForeach parent)
5707 this.parent = parent;
5710 protected override void CloneTo (CloneContext clonectx, Statement target)
5712 throw new NotSupportedException ();
5715 public override bool Resolve (EmitContext ec)
5717 return parent.ResolveLoop (ec);
5720 protected override void DoEmit (EmitContext ec)
5722 parent.EmitLoopInit (ec);
5723 parent.EmitLoopBody (ec);
5726 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5728 throw new NotSupportedException ();
5732 class DisposableWrapper : ExceptionStatement {
5733 CollectionForeach parent;
5735 internal DisposableWrapper (CollectionForeach parent)
5737 this.parent = parent;
5740 protected override void CloneTo (CloneContext clonectx, Statement target)
5742 throw new NotSupportedException ();
5745 public override bool Resolve (EmitContext ec)
5749 ec.StartFlowBranching (this);
5751 if (!parent.ResolveLoop (ec))
5754 ec.EndFlowBranching ();
5756 ResolveReachability (ec);
5758 if (TypeManager.void_dispose_void == null) {
5759 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5760 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5765 protected override void EmitPreTryBody (EmitContext ec)
5767 parent.EmitLoopInit (ec);
5770 protected override void EmitTryBody (EmitContext ec)
5772 parent.EmitLoopBody (ec);
5775 protected override void EmitFinallyBody (EmitContext ec)
5777 parent.EmitFinallyBody (ec);
5780 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5782 throw new NotSupportedException ();
5786 bool ResolveLoop (EmitContext ec)
5788 return loop.Resolve (ec);
5791 void EmitLoopInit (EmitContext ec)
5793 enumerator.EmitAssign (ec, init);
5796 void EmitLoopBody (EmitContext ec)
5801 void EmitFinallyBody (EmitContext ec)
5803 ILGenerator ig = ec.ig;
5805 if (enumerator_type.IsValueType) {
5806 MethodInfo mi = FetchMethodDispose (enumerator_type);
5808 enumerator.AddressOf (ec, AddressOp.Load);
5809 ig.Emit (OpCodes.Call, mi);
5811 enumerator.Emit (ec);
5812 ig.Emit (OpCodes.Box, enumerator_type);
5813 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5816 Label call_dispose = ig.DefineLabel ();
5818 enumerator.Emit (ec);
5819 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5820 ig.Emit (OpCodes.Dup);
5821 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5823 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5824 // (Partition III, Section 3.35)
5825 ig.Emit (OpCodes.Endfinally);
5827 ig.MarkLabel (call_dispose);
5828 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5832 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5834 enumerator_type = storey.MutateType (enumerator_type);
5835 init.MutateHoistedGenericType (storey);
5836 loop.MutateHoistedGenericType (storey);
5840 protected class CollectionForeachStatement : Statement
5843 Expression variable, current, conv;
5844 Statement statement;
5847 public CollectionForeachStatement (Type type, Expression variable,
5848 Expression current, Statement statement,
5852 this.variable = variable;
5853 this.current = current;
5854 this.statement = statement;
5858 protected override void CloneTo (CloneContext clonectx, Statement target)
5860 throw new NotImplementedException ();
5863 public override bool Resolve (EmitContext ec)
5865 current = current.Resolve (ec);
5866 if (current == null)
5869 conv = Convert.ExplicitConversion (ec, current, type, loc);
5873 assign = new SimpleAssign (variable, conv, loc);
5874 if (assign.Resolve (ec) == null)
5877 if (!statement.Resolve (ec))
5883 protected override void DoEmit (EmitContext ec)
5885 assign.EmitStatement (ec);
5886 statement.Emit (ec);
5889 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5891 assign.MutateHoistedGenericType (storey);
5892 statement.MutateHoistedGenericType (storey);
5896 protected override void CloneTo (CloneContext clonectx, Statement t)
5898 Foreach target = (Foreach) t;
5900 target.type = type.Clone (clonectx);
5901 target.variable = variable.Clone (clonectx);
5902 target.expr = expr.Clone (clonectx);
5903 target.statement = statement.Clone (clonectx);
5906 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5908 statement.MutateHoistedGenericType (storey);