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 == TypeManager.void_type) {
1393 Expression.Error_VoidInvalidInTheContext (Location);
1397 if (VariableType.IsAbstract && VariableType.IsSealed) {
1398 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1402 if (VariableType.IsPointer && !ec.InUnsafe)
1403 Expression.UnsafeError (Location);
1408 public bool IsConstant {
1409 get { return (flags & Flags.IsConstant) != 0; }
1410 set { flags |= Flags.IsConstant; }
1413 public bool AddressTaken {
1414 get { return (flags & Flags.AddressTaken) != 0; }
1415 set { flags |= Flags.AddressTaken; }
1418 public bool CompilerGenerated {
1419 get { return (flags & Flags.CompilerGenerated) != 0; }
1420 set { flags |= Flags.CompilerGenerated; }
1423 public override string ToString ()
1425 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1426 Name, Type, VariableInfo, Location);
1430 get { return (flags & Flags.Used) != 0; }
1431 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1434 public bool ReadOnly {
1435 get { return (flags & Flags.ReadOnly) != 0; }
1438 public void SetReadOnlyContext (ReadOnlyContext context)
1440 flags |= Flags.ReadOnly;
1441 ro_context = context;
1444 public string GetReadOnlyContext ()
1447 throw new InternalErrorException ("Variable is not readonly");
1449 switch (ro_context) {
1450 case ReadOnlyContext.Fixed:
1451 return "fixed variable";
1452 case ReadOnlyContext.Foreach:
1453 return "foreach iteration variable";
1454 case ReadOnlyContext.Using:
1455 return "using variable";
1457 throw new NotImplementedException ();
1461 // Whether the variable is pinned, if Pinned the variable has been
1462 // allocated in a pinned slot with DeclareLocal.
1464 public bool Pinned {
1465 get { return (flags & Flags.Pinned) != 0; }
1466 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1469 public bool IsThis {
1470 get { return (flags & Flags.IsThis) != 0; }
1471 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1474 Block IKnownVariable.Block {
1475 get { return Block; }
1478 Location IKnownVariable.Location {
1479 get { return Location; }
1482 public LocalInfo Clone (CloneContext clonectx)
1485 // Variables in anonymous block are not resolved yet
1487 if (VariableType == null)
1488 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1491 // Variables in method block are resolved
1493 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1494 li.VariableType = VariableType;
1500 /// Block represents a C# block.
1504 /// This class is used in a number of places: either to represent
1505 /// explicit blocks that the programmer places or implicit blocks.
1507 /// Implicit blocks are used as labels or to introduce variable
1510 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1511 /// they contain extra information that is not necessary on normal blocks.
1513 public class Block : Statement {
1514 public Block Parent;
1515 public readonly Location StartLocation;
1516 public Location EndLocation = Location.Null;
1518 public ExplicitBlock Explicit;
1519 public ToplevelBlock Toplevel; // TODO: Use Explicit
1522 public enum Flags : byte {
1525 VariablesInitialized = 4,
1530 HasStoreyAccess = 128
1532 protected Flags flags;
1534 public bool Unchecked {
1535 get { return (flags & Flags.Unchecked) != 0; }
1536 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1539 public bool Unsafe {
1540 get { return (flags & Flags.Unsafe) != 0; }
1541 set { flags |= Flags.Unsafe; }
1545 // The statements in this block
1547 protected ArrayList statements;
1550 // An array of Blocks. We keep track of children just
1551 // to generate the local variable declarations.
1553 // Statements and child statements are handled through the
1559 // Labels. (label, block) pairs.
1561 protected HybridDictionary labels;
1564 // Keeps track of (name, type) pairs
1566 IDictionary variables;
1567 protected IDictionary range_variables;
1570 // Keeps track of constants
1571 HybridDictionary constants;
1574 // Temporary variables.
1576 ArrayList temporary_variables;
1579 // If this is a switch section, the enclosing switch block.
1583 ArrayList scope_initializers;
1585 ArrayList anonymous_children;
1587 protected static int id;
1591 int assignable_slots;
1592 bool unreachable_shown;
1595 public Block (Block parent)
1596 : this (parent, (Flags) 0, Location.Null, Location.Null)
1599 public Block (Block parent, Flags flags)
1600 : this (parent, flags, Location.Null, Location.Null)
1603 public Block (Block parent, Location start, Location end)
1604 : this (parent, (Flags) 0, start, end)
1607 public Block (Block parent, Flags flags, Location start, Location end)
1609 if (parent != null) {
1610 parent.AddChild (this);
1612 // the appropriate constructors will fixup these fields
1613 Toplevel = parent.Toplevel;
1614 Explicit = parent.Explicit;
1617 this.Parent = parent;
1619 this.StartLocation = start;
1620 this.EndLocation = end;
1623 statements = new ArrayList (4);
1626 public Block CreateSwitchBlock (Location start)
1628 // FIXME: should this be implicit?
1629 Block new_block = new ExplicitBlock (this, start, start);
1630 new_block.switch_block = this;
1635 get { return this_id; }
1638 public IDictionary Variables {
1640 if (variables == null)
1641 variables = new ListDictionary ();
1646 void AddChild (Block b)
1648 if (children == null)
1649 children = new ArrayList (1);
1654 public void SetEndLocation (Location loc)
1659 protected static void Error_158 (string name, Location loc)
1661 Report.Error (158, loc, "The label `{0}' shadows another label " +
1662 "by the same name in a contained scope", name);
1666 /// Adds a label to the current block.
1670 /// false if the name already exists in this block. true
1674 public bool AddLabel (LabeledStatement target)
1676 if (switch_block != null)
1677 return switch_block.AddLabel (target);
1679 string name = target.Name;
1682 while (cur != null) {
1683 LabeledStatement s = cur.DoLookupLabel (name);
1685 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1686 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1690 if (this == Explicit)
1696 while (cur != null) {
1697 if (cur.DoLookupLabel (name) != null) {
1698 Error_158 (name, target.loc);
1702 if (children != null) {
1703 foreach (Block b in children) {
1704 LabeledStatement s = b.DoLookupLabel (name);
1708 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1709 Error_158 (name, target.loc);
1717 Toplevel.CheckError158 (name, target.loc);
1720 labels = new HybridDictionary();
1722 labels.Add (name, target);
1726 public LabeledStatement LookupLabel (string name)
1728 LabeledStatement s = DoLookupLabel (name);
1732 if (children == null)
1735 foreach (Block child in children) {
1736 if (Explicit != child.Explicit)
1739 s = child.LookupLabel (name);
1747 LabeledStatement DoLookupLabel (string name)
1749 if (switch_block != null)
1750 return switch_block.LookupLabel (name);
1753 if (labels.Contains (name))
1754 return ((LabeledStatement) labels [name]);
1759 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1762 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1763 while (kvi == null) {
1764 b = b.Explicit.Parent;
1767 kvi = b.Explicit.GetKnownVariable (name);
1773 // Is kvi.Block nested inside 'b'
1774 if (b.Explicit != kvi.Block.Explicit) {
1776 // If a variable by the same name it defined in a nested block of this
1777 // block, we violate the invariant meaning in a block.
1780 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1781 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1786 // It's ok if the definition is in a nested subblock of b, but not
1787 // nested inside this block -- a definition in a sibling block
1788 // should not affect us.
1794 // Block 'b' and kvi.Block are the same textual block.
1795 // However, different variables are extant.
1797 // Check if the variable is in scope in both blocks. We use
1798 // an indirect check that depends on AddVariable doing its
1799 // part in maintaining the invariant-meaning-in-block property.
1801 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1804 if (this is ToplevelBlock) {
1805 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1806 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1811 // Even though we detected the error when the name is used, we
1812 // treat it as if the variable declaration was in error.
1814 Report.SymbolRelatedToPreviousError (loc, name);
1815 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1819 public LocalInfo AddVariable (Expression type, string name, Location l)
1821 LocalInfo vi = GetLocalInfo (name);
1823 Report.SymbolRelatedToPreviousError (vi.Location, name);
1824 if (Explicit == vi.Block.Explicit) {
1825 if (type == Linq.ImplicitQueryParameter.ImplicitType.Instance && type == vi.Type)
1826 Error_AlreadyDeclared (l, name);
1828 Error_AlreadyDeclared (l, name, null);
1830 Error_AlreadyDeclared (l, name, "parent");
1835 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1837 Report.SymbolRelatedToPreviousError (pi.Location, name);
1838 Error_AlreadyDeclared (loc, name,
1839 pi.Block == Toplevel ? "method argument" : "parent or current");
1843 if (Toplevel.GenericMethod != null) {
1844 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1845 if (tp.Name == name) {
1846 Report.SymbolRelatedToPreviousError (tp);
1847 Error_AlreadyDeclaredTypeParameter (loc, name);
1853 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1855 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1856 Error_AlreadyDeclared (l, name, "child");
1860 vi = new LocalInfo (type, name, this, l);
1863 if ((flags & Flags.VariablesInitialized) != 0)
1864 throw new InternalErrorException ("block has already been resolved");
1869 protected virtual void AddVariable (LocalInfo li)
1871 Variables.Add (li.Name, li);
1872 Explicit.AddKnownVariable (li.Name, li);
1875 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1877 if (reason == null) {
1878 Error_AlreadyDeclared (loc, var);
1882 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1883 "in this scope because it would give a different meaning " +
1884 "to `{0}', which is already used in a `{1}' scope " +
1885 "to denote something else", var, reason);
1888 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1890 Report.Error (128, loc,
1891 "A local variable named `{0}' is already defined in this scope", name);
1894 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1896 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1899 public bool AddConstant (Expression type, string name, Expression value, Location l)
1901 if (AddVariable (type, name, l) == null)
1904 if (constants == null)
1905 constants = new HybridDictionary();
1907 constants.Add (name, value);
1909 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1914 static int next_temp_id = 0;
1916 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1918 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1920 if (temporary_variables == null)
1921 temporary_variables = new ArrayList ();
1923 int id = ++next_temp_id;
1924 string name = "$s_" + id.ToString ();
1926 LocalInfo li = new LocalInfo (te, name, this, loc);
1927 li.CompilerGenerated = true;
1928 temporary_variables.Add (li);
1932 public LocalInfo GetLocalInfo (string name)
1935 for (Block b = this; b != null; b = b.Parent) {
1936 if (b.variables != null) {
1937 ret = (LocalInfo) b.variables [name];
1942 if (b.range_variables != null) {
1943 ret = (LocalInfo) b.range_variables [name];
1952 public Expression GetVariableType (string name)
1954 LocalInfo vi = GetLocalInfo (name);
1955 return vi == null ? null : vi.Type;
1958 public Expression GetConstantExpression (string name)
1960 for (Block b = this; b != null; b = b.Parent) {
1961 if (b.constants != null) {
1962 Expression ret = b.constants [name] as Expression;
1971 // It should be used by expressions which require to
1972 // register a statement during resolve process.
1974 public void AddScopeStatement (StatementExpression s)
1976 if (scope_initializers == null)
1977 scope_initializers = new ArrayList ();
1979 scope_initializers.Add (s);
1982 public void AddStatement (Statement s)
1985 flags |= Flags.BlockUsed;
1989 get { return (flags & Flags.BlockUsed) != 0; }
1994 flags |= Flags.BlockUsed;
1997 public bool HasRet {
1998 get { return (flags & Flags.HasRet) != 0; }
2001 public bool IsDestructor {
2002 get { return (flags & Flags.IsDestructor) != 0; }
2005 public void SetDestructor ()
2007 flags |= Flags.IsDestructor;
2010 public int AssignableSlots {
2013 // if ((flags & Flags.VariablesInitialized) == 0)
2014 // throw new Exception ("Variables have not been initialized yet");
2015 return assignable_slots;
2019 public ArrayList AnonymousChildren {
2020 get { return anonymous_children; }
2023 public void AddAnonymousChild (ToplevelBlock b)
2025 if (anonymous_children == null)
2026 anonymous_children = new ArrayList ();
2028 anonymous_children.Add (b);
2031 void DoResolveConstants (EmitContext ec)
2033 if (constants == null)
2036 if (variables == null)
2037 throw new InternalErrorException ("cannot happen");
2039 foreach (DictionaryEntry de in variables) {
2040 string name = (string) de.Key;
2041 LocalInfo vi = (LocalInfo) de.Value;
2042 Type variable_type = vi.VariableType;
2044 if (variable_type == null) {
2045 if (vi.Type is VarExpr)
2046 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2051 Expression cv = (Expression) constants [name];
2055 // Don't let 'const int Foo = Foo;' succeed.
2056 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2057 // which in turn causes the 'must be constant' error to be triggered.
2058 constants.Remove (name);
2060 if (!Const.IsConstantTypeValid (variable_type)) {
2061 Const.Error_InvalidConstantType (variable_type, loc);
2065 ec.CurrentBlock = this;
2067 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2068 e = cv.Resolve (ec);
2073 Constant ce = e as Constant;
2075 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2079 e = ce.ConvertImplicitly (variable_type);
2081 if (TypeManager.IsReferenceType (variable_type))
2082 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2084 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2088 constants.Add (name, e);
2089 vi.IsConstant = true;
2093 protected void ResolveMeta (EmitContext ec, int offset)
2095 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2097 // If some parent block was unsafe, we remain unsafe even if this block
2098 // isn't explicitly marked as such.
2099 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2100 flags |= Flags.VariablesInitialized;
2102 if (variables != null) {
2103 foreach (LocalInfo li in variables.Values) {
2104 if (!li.Resolve (ec))
2106 li.VariableInfo = new VariableInfo (li, offset);
2107 offset += li.VariableInfo.Length;
2110 assignable_slots = offset;
2112 DoResolveConstants (ec);
2114 if (children == null)
2116 foreach (Block b in children)
2117 b.ResolveMeta (ec, offset);
2122 // Emits the local variable declarations for a block
2124 public virtual void EmitMeta (EmitContext ec)
2126 if (variables != null){
2127 foreach (LocalInfo vi in variables.Values)
2128 vi.ResolveVariable (ec);
2131 if (temporary_variables != null) {
2132 for (int i = 0; i < temporary_variables.Count; i++)
2133 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2136 if (children != null) {
2137 for (int i = 0; i < children.Count; i++)
2138 ((Block)children[i]).EmitMeta(ec);
2142 void UsageWarning ()
2144 if (variables == null || Report.WarningLevel < 3)
2147 foreach (DictionaryEntry de in variables) {
2148 LocalInfo vi = (LocalInfo) de.Value;
2151 string name = (string) de.Key;
2153 // vi.VariableInfo can be null for 'catch' variables
2154 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2155 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2157 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2162 static void CheckPossibleMistakenEmptyStatement (Statement s)
2166 // Some statements are wrapped by a Block. Since
2167 // others' internal could be changed, here I treat
2168 // them as possibly wrapped by Block equally.
2169 Block b = s as Block;
2170 if (b != null && b.statements.Count == 1)
2171 s = (Statement) b.statements [0];
2174 body = ((Lock) s).Statement;
2176 body = ((For) s).Statement;
2177 else if (s is Foreach)
2178 body = ((Foreach) s).Statement;
2179 else if (s is While)
2180 body = ((While) s).Statement;
2181 else if (s is Fixed)
2182 body = ((Fixed) s).Statement;
2183 else if (s is Using)
2184 body = ((Using) s).EmbeddedStatement;
2185 else if (s is UsingTemporary)
2186 body = ((UsingTemporary) s).Statement;
2190 if (body == null || body is EmptyStatement)
2191 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2194 public override bool Resolve (EmitContext ec)
2196 Block prev_block = ec.CurrentBlock;
2199 int errors = Report.Errors;
2201 ec.CurrentBlock = this;
2202 ec.StartFlowBranching (this);
2204 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2207 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2208 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2209 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2210 // responsible for handling the situation.
2212 int statement_count = statements.Count;
2213 for (int ix = 0; ix < statement_count; ix++){
2214 Statement s = (Statement) statements [ix];
2215 // Check possible empty statement (CS0642)
2216 if (Report.WarningLevel >= 3 &&
2217 ix + 1 < statement_count &&
2218 statements [ix + 1] is Block)
2219 CheckPossibleMistakenEmptyStatement (s);
2222 // Warn if we detect unreachable code.
2225 if (s is EmptyStatement)
2228 if (!unreachable_shown && !(s is LabeledStatement)) {
2229 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2230 unreachable_shown = true;
2233 Block c_block = s as Block;
2234 if (c_block != null)
2235 c_block.unreachable = c_block.unreachable_shown = true;
2239 // Note that we're not using ResolveUnreachable() for unreachable
2240 // statements here. ResolveUnreachable() creates a temporary
2241 // flow branching and kills it afterwards. This leads to problems
2242 // if you have two unreachable statements where the first one
2243 // assigns a variable and the second one tries to access it.
2246 if (!s.Resolve (ec)) {
2248 if (ec.IsInProbingMode)
2251 statements [ix] = EmptyStatement.Value;
2255 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2256 statements [ix] = EmptyStatement.Value;
2258 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2259 if (unreachable && s is LabeledStatement)
2260 throw new InternalErrorException ("should not happen");
2263 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2264 ec.CurrentBranching, statement_count);
2266 while (ec.CurrentBranching is FlowBranchingLabeled)
2267 ec.EndFlowBranching ();
2269 bool flow_unreachable = ec.EndFlowBranching ();
2271 ec.CurrentBlock = prev_block;
2273 if (flow_unreachable)
2274 flags |= Flags.HasRet;
2276 // If we're a non-static `struct' constructor which doesn't have an
2277 // initializer, then we must initialize all of the struct's fields.
2278 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2281 if ((labels != null) && (Report.WarningLevel >= 2)) {
2282 foreach (LabeledStatement label in labels.Values)
2283 if (!label.HasBeenReferenced)
2284 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2287 if (ok && errors == Report.Errors)
2293 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2295 unreachable_shown = true;
2299 Report.Warning (162, 2, loc, "Unreachable code detected");
2301 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2302 bool ok = Resolve (ec);
2303 ec.KillFlowBranching ();
2308 protected override void DoEmit (EmitContext ec)
2310 for (int ix = 0; ix < statements.Count; ix++){
2311 Statement s = (Statement) statements [ix];
2316 public override void Emit (EmitContext ec)
2318 Block prev_block = ec.CurrentBlock;
2319 ec.CurrentBlock = this;
2321 if (scope_initializers != null) {
2322 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2324 bool omit_debug_info = ec.OmitDebuggingInfo;
2325 ec.OmitDebuggingInfo = true;
2326 foreach (StatementExpression s in scope_initializers)
2328 ec.OmitDebuggingInfo = omit_debug_info;
2330 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2333 ec.Mark (StartLocation);
2336 if (SymbolWriter.HasSymbolWriter)
2337 EmitSymbolInfo (ec);
2339 ec.CurrentBlock = prev_block;
2342 protected virtual void EmitSymbolInfo (EmitContext ec)
2344 if (variables != null) {
2345 foreach (LocalInfo vi in variables.Values) {
2346 vi.EmitSymbolInfo (ec);
2351 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2353 MutateVariables (storey);
2355 foreach (Statement s in statements)
2356 s.MutateHoistedGenericType (storey);
2359 void MutateVariables (AnonymousMethodStorey storey)
2361 if (variables != null) {
2362 foreach (LocalInfo vi in variables.Values) {
2363 vi.VariableType = storey.MutateType (vi.VariableType);
2367 if (temporary_variables != null) {
2368 foreach (LocalInfo vi in temporary_variables)
2369 vi.VariableType = storey.MutateType (vi.VariableType);
2373 public override string ToString ()
2375 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2378 protected override void CloneTo (CloneContext clonectx, Statement t)
2380 Block target = (Block) t;
2382 clonectx.AddBlockMap (this, target);
2384 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2385 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2387 target.Parent = clonectx.RemapBlockCopy (Parent);
2389 if (variables != null){
2390 target.variables = new Hashtable ();
2392 foreach (DictionaryEntry de in variables){
2393 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2394 target.variables [de.Key] = newlocal;
2395 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2399 target.statements = new ArrayList (statements.Count);
2400 foreach (Statement s in statements)
2401 target.statements.Add (s.Clone (clonectx));
2403 if (target.children != null){
2404 target.children = new ArrayList (children.Count);
2405 foreach (Block b in children){
2406 target.children.Add (clonectx.LookupBlock (b));
2411 // TODO: labels, switch_block, constants (?), anonymous_children
2416 public class ExplicitBlock : Block {
2417 HybridDictionary known_variables;
2418 protected AnonymousMethodStorey am_storey;
2420 public ExplicitBlock (Block parent, Location start, Location end)
2421 : this (parent, (Flags) 0, start, end)
2425 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2426 : base (parent, flags, start, end)
2428 this.Explicit = this;
2432 // Marks a variable with name @name as being used in this or a child block.
2433 // If a variable name has been used in a child block, it's illegal to
2434 // declare a variable with the same name in the current block.
2436 internal void AddKnownVariable (string name, IKnownVariable info)
2438 if (known_variables == null)
2439 known_variables = new HybridDictionary();
2441 known_variables [name] = info;
2444 Parent.Explicit.AddKnownVariable (name, info);
2447 public AnonymousMethodStorey AnonymousMethodStorey {
2448 get { return am_storey; }
2452 // Creates anonymous method storey in current block
2454 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2457 // When referencing a variable in iterator storey from children anonymous method
2459 if (Toplevel.am_storey is IteratorStorey) {
2460 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2461 return Toplevel.am_storey;
2465 // An iterator has only 1 storey block
2467 if (ec.CurrentIterator != null)
2468 return ec.CurrentIterator.Storey;
2470 if (am_storey == null) {
2471 MemberBase mc = ec.ResolveContext as MemberBase;
2472 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2475 // Create anonymous method storey for this block
2477 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2481 // Creates a link between this block and the anonymous method
2483 // An anonymous method can reference variables from any outer block, but they are
2484 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2485 // need to create another link between those variable storeys
2487 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2491 public override void Emit (EmitContext ec)
2493 if (am_storey != null)
2494 am_storey.EmitHoistedVariables (ec);
2496 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2497 if (emit_debug_info)
2502 if (emit_debug_info)
2506 public override void EmitMeta (EmitContext ec)
2509 // Creates anonymous method storey
2511 if (am_storey != null) {
2512 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2513 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2516 am_storey.DefineType ();
2517 am_storey.ResolveType ();
2518 am_storey.DefineMembers ();
2519 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2525 internal IKnownVariable GetKnownVariable (string name)
2527 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2530 public void PropagateStoreyReference (AnonymousMethodStorey s)
2532 if (Parent != null && am_storey != s) {
2533 if (am_storey != null)
2534 am_storey.AddParentStoreyReference (s);
2536 Parent.Explicit.PropagateStoreyReference (s);
2540 public override bool Resolve (EmitContext ec)
2542 bool ok = base.Resolve (ec);
2545 // Discard an anonymous method storey when this block has no hoisted variables
2547 if (am_storey != null && !am_storey.HasHoistedVariables) {
2555 protected override void CloneTo (CloneContext clonectx, Statement t)
2557 ExplicitBlock target = (ExplicitBlock) t;
2558 target.known_variables = null;
2559 base.CloneTo (clonectx, t);
2563 public class ToplevelParameterInfo : IKnownVariable {
2564 public readonly ToplevelBlock Block;
2565 public readonly int Index;
2566 public VariableInfo VariableInfo;
2568 Block IKnownVariable.Block {
2569 get { return Block; }
2571 public Parameter Parameter {
2572 get { return Block.Parameters [Index]; }
2575 public Type ParameterType {
2576 get { return Block.Parameters.Types [Index]; }
2579 public Location Location {
2580 get { return Parameter.Location; }
2583 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2591 // A toplevel block contains extra information, the split is done
2592 // only to separate information that would otherwise bloat the more
2593 // lightweight Block.
2595 // In particular, this was introduced when the support for Anonymous
2596 // Methods was implemented.
2598 public class ToplevelBlock : ExplicitBlock {
2599 GenericMethod generic;
2600 FlowBranchingToplevel top_level_branching;
2601 Parameters parameters;
2602 ToplevelParameterInfo[] parameter_info;
2603 LocalInfo this_variable;
2605 public HoistedVariable HoistedThisVariable;
2608 // The parameters for the block.
2610 public Parameters Parameters {
2611 get { return parameters; }
2614 public GenericMethod GenericMethod {
2615 get { return generic; }
2618 public bool HasStoreyAccess {
2619 set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
2620 get { return (flags & Flags.HasStoreyAccess) != 0; }
2623 public ToplevelBlock Container {
2624 get { return Parent == null ? null : Parent.Toplevel; }
2627 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2628 this (parent, (Flags) 0, parameters, start)
2632 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2633 this (parent, parameters, start)
2635 this.generic = generic;
2638 public ToplevelBlock (Parameters parameters, Location start) :
2639 this (null, (Flags) 0, parameters, start)
2643 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2644 this (null, flags, parameters, start)
2648 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2649 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2650 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2651 base (null, flags, start, Location.Null)
2653 this.Toplevel = this;
2655 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2656 this.Parent = parent;
2658 parent.AddAnonymousChild (this);
2660 if (!this.parameters.IsEmpty)
2661 ProcessParameters ();
2664 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2668 protected override void CloneTo (CloneContext clonectx, Statement t)
2670 ToplevelBlock target = (ToplevelBlock) t;
2671 base.CloneTo (clonectx, t);
2673 if (parameters.Count != 0)
2674 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2675 for (int i = 0; i < parameters.Count; ++i)
2676 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2679 public bool CheckError158 (string name, Location loc)
2681 if (AnonymousChildren != null) {
2682 foreach (ToplevelBlock child in AnonymousChildren) {
2683 if (!child.CheckError158 (name, loc))
2688 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2689 if (!c.DoCheckError158 (name, loc))
2696 public virtual Expression GetTransparentIdentifier (string name)
2701 void ProcessParameters ()
2703 int n = parameters.Count;
2704 parameter_info = new ToplevelParameterInfo [n];
2705 for (int i = 0; i < n; ++i) {
2706 parameter_info [i] = new ToplevelParameterInfo (this, i);
2708 Parameter p = parameters [i];
2712 string name = p.Name;
2713 LocalInfo vi = GetLocalInfo (name);
2715 Report.SymbolRelatedToPreviousError (vi.Location, name);
2716 Error_AlreadyDeclared (loc, name, "parent or current");
2720 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2722 Report.SymbolRelatedToPreviousError (pi.Location, name);
2723 Error_AlreadyDeclared (loc, name, "parent or current");
2727 AddKnownVariable (name, parameter_info [i]);
2730 // mark this block as "used" so that we create local declarations in a sub-block
2731 // FIXME: This appears to uncover a lot of bugs
2735 bool DoCheckError158 (string name, Location loc)
2737 LabeledStatement s = LookupLabel (name);
2739 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2740 Error_158 (name, loc);
2747 public override Expression CreateExpressionTree (EmitContext ec)
2749 if (statements.Count == 1)
2750 return ((Statement) statements [0]).CreateExpressionTree (ec);
2752 return base.CreateExpressionTree (ec);
2756 // Reformats this block to be top-level iterator block
2758 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2760 // Create block with original statements
2761 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2764 // TODO: Change to iter_block.statements = statements;
2765 foreach (Statement stmt in source.statements)
2766 iter_block.AddStatement (stmt);
2767 labels = source.labels;
2769 AddStatement (new IteratorStatement (iterator, iter_block));
2771 source.statements = new ArrayList (1);
2772 source.AddStatement (new Return (iterator, iterator.Location));
2773 source.IsIterator = false;
2775 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2776 source.am_storey = iterator_storey;
2777 return iterator_storey;
2780 public FlowBranchingToplevel TopLevelBranching {
2781 get { return top_level_branching; }
2785 // Returns a `ParameterReference' for the given name, or null if there
2786 // is no such parameter
2788 public ParameterReference GetParameterReference (string name, Location loc)
2790 ToplevelParameterInfo p = GetParameterInfo (name);
2791 return p == null ? null : new ParameterReference (this, p, loc);
2794 public ToplevelParameterInfo GetParameterInfo (string name)
2797 for (ToplevelBlock t = this; t != null; t = t.Container) {
2798 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2800 return t.parameter_info [idx];
2806 // Returns the "this" instance variable of this block.
2807 // See AddThisVariable() for more information.
2809 public LocalInfo ThisVariable {
2810 get { return this_variable; }
2814 // This is used by non-static `struct' constructors which do not have an
2815 // initializer - in this case, the constructor must initialize all of the
2816 // struct's fields. To do this, we add a "this" variable and use the flow
2817 // analysis code to ensure that it's been fully initialized before control
2818 // leaves the constructor.
2820 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2822 if (this_variable == null) {
2823 this_variable = new LocalInfo (ds, this, l);
2824 this_variable.Used = true;
2825 this_variable.IsThis = true;
2827 Variables.Add ("this", this_variable);
2830 return this_variable;
2833 public bool IsIterator {
2834 get { return (flags & Flags.IsIterator) != 0; }
2835 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2838 public bool IsThisAssigned (EmitContext ec)
2840 return this_variable == null || this_variable.IsThisAssigned (ec);
2843 public bool ResolveMeta (EmitContext ec, Parameters ip)
2845 int errors = Report.Errors;
2846 int orig_count = parameters.Count;
2848 if (top_level_branching != null)
2854 // Assert: orig_count != parameter.Count => orig_count == 0
2855 if (orig_count != 0 && orig_count != parameters.Count)
2856 throw new InternalErrorException ("parameter information mismatch");
2858 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2860 for (int i = 0; i < orig_count; ++i) {
2861 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2863 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2866 VariableInfo vi = new VariableInfo (ip, i, offset);
2867 parameter_info [i].VariableInfo = vi;
2868 offset += vi.Length;
2871 ResolveMeta (ec, offset);
2873 top_level_branching = ec.StartFlowBranching (this);
2875 return Report.Errors == errors;
2879 // Check whether all `out' parameters have been assigned.
2881 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2883 if (vector.IsUnreachable)
2886 int n = parameter_info == null ? 0 : parameter_info.Length;
2888 for (int i = 0; i < n; i++) {
2889 VariableInfo var = parameter_info [i].VariableInfo;
2894 if (vector.IsAssigned (var, false))
2897 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2902 public override void EmitMeta (EmitContext ec)
2904 parameters.ResolveVariable ();
2906 // Avoid declaring an IL variable for this_variable since it is not accessed
2907 // from the generated IL
2908 if (this_variable != null)
2909 Variables.Remove ("this");
2913 protected override void EmitSymbolInfo (EmitContext ec)
2915 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2916 if ((ae != null) && (ae.Storey != null))
2917 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2919 base.EmitSymbolInfo (ec);
2922 public override void Emit (EmitContext ec)
2925 ec.Mark (EndLocation);
2929 public class SwitchLabel {
2936 Label il_label_code;
2937 bool il_label_code_set;
2939 public static readonly object NullStringCase = new object ();
2942 // if expr == null, then it is the default case.
2944 public SwitchLabel (Expression expr, Location l)
2950 public Expression Label {
2956 public Location Location {
2960 public object Converted {
2966 public Label GetILLabel (EmitContext ec)
2969 il_label = ec.ig.DefineLabel ();
2970 il_label_set = true;
2975 public Label GetILLabelCode (EmitContext ec)
2977 if (!il_label_code_set){
2978 il_label_code = ec.ig.DefineLabel ();
2979 il_label_code_set = true;
2981 return il_label_code;
2985 // Resolves the expression, reduces it to a literal if possible
2986 // and then converts it to the requested type.
2988 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2990 Expression e = label.Resolve (ec);
2995 Constant c = e as Constant;
2997 Report.Error (150, loc, "A constant value is expected");
3001 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3002 converted = NullStringCase;
3006 if (allow_nullable && c.GetValue () == null) {
3007 converted = NullStringCase;
3011 c = c.ImplicitConversionRequired (ec, required_type, loc);
3015 converted = c.GetValue ();
3019 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3022 if (converted == null)
3024 else if (converted == NullStringCase)
3027 label = converted.ToString ();
3029 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3030 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3033 public SwitchLabel Clone (CloneContext clonectx)
3035 return new SwitchLabel (label.Clone (clonectx), loc);
3039 public class SwitchSection {
3040 // An array of SwitchLabels.
3041 public readonly ArrayList Labels;
3042 public readonly Block Block;
3044 public SwitchSection (ArrayList labels, Block block)
3050 public SwitchSection Clone (CloneContext clonectx)
3052 ArrayList cloned_labels = new ArrayList ();
3054 foreach (SwitchLabel sl in cloned_labels)
3055 cloned_labels.Add (sl.Clone (clonectx));
3057 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3061 public class Switch : Statement {
3062 public ArrayList Sections;
3063 public Expression Expr;
3066 /// Maps constants whose type type SwitchType to their SwitchLabels.
3068 public IDictionary Elements;
3071 /// The governing switch type
3073 public Type SwitchType;
3078 Label default_target;
3080 Expression new_expr;
3083 SwitchSection constant_section;
3084 SwitchSection default_section;
3086 ExpressionStatement string_dictionary;
3087 FieldExpr switch_cache_field;
3088 static int unique_counter;
3092 // Nullable Types support for GMCS.
3094 Nullable.Unwrap unwrap;
3096 protected bool HaveUnwrap {
3097 get { return unwrap != null; }
3100 protected bool HaveUnwrap {
3101 get { return false; }
3106 // The types allowed to be implicitly cast from
3107 // on the governing type
3109 static Type [] allowed_types;
3111 public Switch (Expression e, ArrayList sects, Location l)
3118 public bool GotDefault {
3120 return default_section != null;
3124 public Label DefaultTarget {
3126 return default_target;
3131 // Determines the governing type for a switch. The returned
3132 // expression might be the expression from the switch, or an
3133 // expression that includes any potential conversions to the
3134 // integral types or to string.
3136 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3140 if (t == TypeManager.byte_type ||
3141 t == TypeManager.sbyte_type ||
3142 t == TypeManager.ushort_type ||
3143 t == TypeManager.short_type ||
3144 t == TypeManager.uint32_type ||
3145 t == TypeManager.int32_type ||
3146 t == TypeManager.uint64_type ||
3147 t == TypeManager.int64_type ||
3148 t == TypeManager.char_type ||
3149 t == TypeManager.string_type ||
3150 t == TypeManager.bool_type ||
3151 TypeManager.IsEnumType (t))
3154 if (allowed_types == null){
3155 allowed_types = new Type [] {
3156 TypeManager.sbyte_type,
3157 TypeManager.byte_type,
3158 TypeManager.short_type,
3159 TypeManager.ushort_type,
3160 TypeManager.int32_type,
3161 TypeManager.uint32_type,
3162 TypeManager.int64_type,
3163 TypeManager.uint64_type,
3164 TypeManager.char_type,
3165 TypeManager.string_type
3170 // Try to find a *user* defined implicit conversion.
3172 // If there is no implicit conversion, or if there are multiple
3173 // conversions, we have to report an error
3175 Expression converted = null;
3176 foreach (Type tt in allowed_types){
3179 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3184 // Ignore over-worked ImplicitUserConversions that do
3185 // an implicit conversion in addition to the user conversion.
3187 if (!(e is UserCast))
3190 if (converted != null){
3191 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3201 // Performs the basic sanity checks on the switch statement
3202 // (looks for duplicate keys and non-constant expressions).
3204 // It also returns a hashtable with the keys that we will later
3205 // use to compute the switch tables
3207 bool CheckSwitch (EmitContext ec)
3210 Elements = Sections.Count > 10 ?
3211 (IDictionary)new Hashtable () :
3212 (IDictionary)new ListDictionary ();
3214 foreach (SwitchSection ss in Sections){
3215 foreach (SwitchLabel sl in ss.Labels){
3216 if (sl.Label == null){
3217 if (default_section != null){
3218 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3221 default_section = ss;
3225 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3230 object key = sl.Converted;
3231 if (key == SwitchLabel.NullStringCase)
3232 has_null_case = true;
3235 Elements.Add (key, sl);
3236 } catch (ArgumentException) {
3237 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3245 void EmitObjectInteger (ILGenerator ig, object k)
3248 IntConstant.EmitInt (ig, (int) k);
3249 else if (k is Constant) {
3250 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3253 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3256 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3258 IntConstant.EmitInt (ig, (int) (long) k);
3259 ig.Emit (OpCodes.Conv_I8);
3262 LongConstant.EmitLong (ig, (long) k);
3264 else if (k is ulong)
3266 ulong ul = (ulong) k;
3269 IntConstant.EmitInt (ig, unchecked ((int) ul));
3270 ig.Emit (OpCodes.Conv_U8);
3274 LongConstant.EmitLong (ig, unchecked ((long) ul));
3278 IntConstant.EmitInt (ig, (int) ((char) k));
3279 else if (k is sbyte)
3280 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3282 IntConstant.EmitInt (ig, (int) ((byte) k));
3283 else if (k is short)
3284 IntConstant.EmitInt (ig, (int) ((short) k));
3285 else if (k is ushort)
3286 IntConstant.EmitInt (ig, (int) ((ushort) k));
3288 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3290 throw new Exception ("Unhandled case");
3293 // structure used to hold blocks of keys while calculating table switch
3294 class KeyBlock : IComparable
3296 public KeyBlock (long _first)
3298 first = last = _first;
3302 public ArrayList element_keys = null;
3303 // how many items are in the bucket
3304 public int Size = 1;
3307 get { return (int) (last - first + 1); }
3309 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3311 return kb_last.last - kb_first.first + 1;
3313 public int CompareTo (object obj)
3315 KeyBlock kb = (KeyBlock) obj;
3316 int nLength = Length;
3317 int nLengthOther = kb.Length;
3318 if (nLengthOther == nLength)
3319 return (int) (kb.first - first);
3320 return nLength - nLengthOther;
3325 /// This method emits code for a lookup-based switch statement (non-string)
3326 /// Basically it groups the cases into blocks that are at least half full,
3327 /// and then spits out individual lookup opcodes for each block.
3328 /// It emits the longest blocks first, and short blocks are just
3329 /// handled with direct compares.
3331 /// <param name="ec"></param>
3332 /// <param name="val"></param>
3333 /// <returns></returns>
3334 void TableSwitchEmit (EmitContext ec, Expression val)
3336 int element_count = Elements.Count;
3337 object [] element_keys = new object [element_count];
3338 Elements.Keys.CopyTo (element_keys, 0);
3339 Array.Sort (element_keys);
3341 // initialize the block list with one element per key
3342 ArrayList key_blocks = new ArrayList (element_count);
3343 foreach (object key in element_keys)
3344 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3346 KeyBlock current_kb;
3347 // iteratively merge the blocks while they are at least half full
3348 // there's probably a really cool way to do this with a tree...
3349 while (key_blocks.Count > 1)
3351 ArrayList key_blocks_new = new ArrayList ();
3352 current_kb = (KeyBlock) key_blocks [0];
3353 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3355 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3356 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3359 current_kb.last = kb.last;
3360 current_kb.Size += kb.Size;
3364 // start a new block
3365 key_blocks_new.Add (current_kb);
3369 key_blocks_new.Add (current_kb);
3370 if (key_blocks.Count == key_blocks_new.Count)
3372 key_blocks = key_blocks_new;
3375 // initialize the key lists
3376 foreach (KeyBlock kb in key_blocks)
3377 kb.element_keys = new ArrayList ();
3379 // fill the key lists
3381 if (key_blocks.Count > 0) {
3382 current_kb = (KeyBlock) key_blocks [0];
3383 foreach (object key in element_keys)
3385 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3386 System.Convert.ToInt64 (key) > current_kb.last;
3388 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3389 current_kb.element_keys.Add (key);
3393 // sort the blocks so we can tackle the largest ones first
3396 // okay now we can start...
3397 ILGenerator ig = ec.ig;
3398 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3399 Label lbl_default = default_target;
3401 Type type_keys = null;
3402 if (element_keys.Length > 0)
3403 type_keys = element_keys [0].GetType (); // used for conversions
3407 if (TypeManager.IsEnumType (SwitchType))
3408 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3410 compare_type = SwitchType;
3412 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3414 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3415 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3418 foreach (object key in kb.element_keys) {
3419 SwitchLabel sl = (SwitchLabel) Elements [key];
3420 if (key is int && (int) key == 0) {
3421 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3424 EmitObjectInteger (ig, key);
3425 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3431 // TODO: if all the keys in the block are the same and there are
3432 // no gaps/defaults then just use a range-check.
3433 if (compare_type == TypeManager.int64_type ||
3434 compare_type == TypeManager.uint64_type)
3436 // TODO: optimize constant/I4 cases
3438 // check block range (could be > 2^31)
3440 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3441 ig.Emit (OpCodes.Blt, lbl_default);
3443 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3444 ig.Emit (OpCodes.Bgt, lbl_default);
3450 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3451 ig.Emit (OpCodes.Sub);
3453 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3459 int first = (int) kb.first;
3462 IntConstant.EmitInt (ig, first);
3463 ig.Emit (OpCodes.Sub);
3467 IntConstant.EmitInt (ig, -first);
3468 ig.Emit (OpCodes.Add);
3472 // first, build the list of labels for the switch
3474 int cJumps = kb.Length;
3475 Label [] switch_labels = new Label [cJumps];
3476 for (int iJump = 0; iJump < cJumps; iJump++)
3478 object key = kb.element_keys [iKey];
3479 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3481 SwitchLabel sl = (SwitchLabel) Elements [key];
3482 switch_labels [iJump] = sl.GetILLabel (ec);
3486 switch_labels [iJump] = lbl_default;
3488 // emit the switch opcode
3489 ig.Emit (OpCodes.Switch, switch_labels);
3492 // mark the default for this block
3494 ig.MarkLabel (lbl_default);
3497 // TODO: find the default case and emit it here,
3498 // to prevent having to do the following jump.
3499 // make sure to mark other labels in the default section
3501 // the last default just goes to the end
3502 if (element_keys.Length > 0)
3503 ig.Emit (OpCodes.Br, lbl_default);
3505 // now emit the code for the sections
3506 bool found_default = false;
3508 foreach (SwitchSection ss in Sections) {
3509 foreach (SwitchLabel sl in ss.Labels) {
3510 if (sl.Converted == SwitchLabel.NullStringCase) {
3511 ig.MarkLabel (null_target);
3512 } else if (sl.Label == null) {
3513 ig.MarkLabel (lbl_default);
3514 found_default = true;
3516 ig.MarkLabel (null_target);
3518 ig.MarkLabel (sl.GetILLabel (ec));
3519 ig.MarkLabel (sl.GetILLabelCode (ec));
3524 if (!found_default) {
3525 ig.MarkLabel (lbl_default);
3526 if (!has_null_case) {
3527 ig.MarkLabel (null_target);
3531 ig.MarkLabel (lbl_end);
3534 SwitchSection FindSection (SwitchLabel label)
3536 foreach (SwitchSection ss in Sections){
3537 foreach (SwitchLabel sl in ss.Labels){
3546 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3548 foreach (SwitchSection ss in Sections)
3549 ss.Block.MutateHoistedGenericType (storey);
3552 public static void Reset ()
3557 public override bool Resolve (EmitContext ec)
3559 Expr = Expr.Resolve (ec);
3563 new_expr = SwitchGoverningType (ec, Expr);
3566 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3567 unwrap = Nullable.Unwrap.Create (Expr, ec);
3571 new_expr = SwitchGoverningType (ec, unwrap);
3575 if (new_expr == null){
3576 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3581 SwitchType = new_expr.Type;
3583 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3584 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3588 if (!CheckSwitch (ec))
3592 Elements.Remove (SwitchLabel.NullStringCase);
3594 Switch old_switch = ec.Switch;
3596 ec.Switch.SwitchType = SwitchType;
3598 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3599 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3601 is_constant = new_expr is Constant;
3603 object key = ((Constant) new_expr).GetValue ();
3604 SwitchLabel label = (SwitchLabel) Elements [key];
3606 constant_section = FindSection (label);
3607 if (constant_section == null)
3608 constant_section = default_section;
3613 foreach (SwitchSection ss in Sections){
3615 ec.CurrentBranching.CreateSibling (
3616 null, FlowBranching.SiblingType.SwitchSection);
3620 if (is_constant && (ss != constant_section)) {
3621 // If we're a constant switch, we're only emitting
3622 // one single section - mark all the others as
3624 ec.CurrentBranching.CurrentUsageVector.Goto ();
3625 if (!ss.Block.ResolveUnreachable (ec, true)) {
3629 if (!ss.Block.Resolve (ec))
3634 if (default_section == null)
3635 ec.CurrentBranching.CreateSibling (
3636 null, FlowBranching.SiblingType.SwitchSection);
3638 ec.EndFlowBranching ();
3639 ec.Switch = old_switch;
3641 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3646 if (SwitchType == TypeManager.string_type && !is_constant) {
3647 // TODO: Optimize single case, and single+default case
3648 ResolveStringSwitchMap (ec);
3654 void ResolveStringSwitchMap (EmitContext ec)
3656 FullNamedExpression string_dictionary_type;
3658 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3659 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3661 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3662 new TypeArguments (loc,
3663 new TypeExpression (TypeManager.string_type, loc),
3664 new TypeExpression (TypeManager.int32_type, loc)), loc);
3666 MemberAccess system_collections_generic = new MemberAccess (
3667 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3669 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3671 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3672 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3673 CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), null, loc);
3674 if (!field.Define ())
3676 ec.TypeContainer.PartialContainer.AddField (field);
3678 ArrayList init = new ArrayList ();
3681 string value = null;
3682 foreach (SwitchSection section in Sections) {
3683 foreach (SwitchLabel sl in section.Labels) {
3684 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3689 value = (string) sl.Converted;
3690 ArrayList init_args = new ArrayList (2);
3691 init_args.Add (new StringLiteral (value, sl.Location));
3692 init_args.Add (new IntConstant (counter, loc));
3693 init.Add (new CollectionElementInitializer (init_args, loc));
3699 Elements.Add (counter, section.Labels [0]);
3703 ArrayList args = new ArrayList (1);
3704 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3705 Expression initializer = new NewInitialize (string_dictionary_type, args,
3706 new CollectionOrObjectInitializers (init, loc), loc);
3708 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3709 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3712 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3714 ILGenerator ig = ec.ig;
3715 Label l_initialized = ig.DefineLabel ();
3718 // Skip initialization when value is null
3720 value.EmitBranchable (ec, null_target, false);
3723 // Check if string dictionary is initialized and initialize
3725 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3726 string_dictionary.EmitStatement (ec);
3727 ig.MarkLabel (l_initialized);
3729 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3732 ArrayList get_value_args = new ArrayList (2);
3733 get_value_args.Add (new Argument (value));
3734 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3735 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3736 if (get_item == null)
3740 // A value was not found, go to default case
3742 get_item.EmitBranchable (ec, default_target, false);
3744 ArrayList get_value_args = new ArrayList (1);
3745 get_value_args.Add (value);
3747 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3748 if (get_item == null)
3751 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3752 get_item_object.EmitAssign (ec, get_item, true, false);
3753 ec.ig.Emit (OpCodes.Brfalse, default_target);
3755 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3756 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3758 get_item_int.EmitStatement (ec);
3759 get_item_object.Release (ec);
3761 TableSwitchEmit (ec, string_switch_variable);
3762 string_switch_variable.Release (ec);
3765 protected override void DoEmit (EmitContext ec)
3767 ILGenerator ig = ec.ig;
3769 default_target = ig.DefineLabel ();
3770 null_target = ig.DefineLabel ();
3772 // Store variable for comparission purposes
3773 // TODO: Don't duplicate non-captured VariableReference
3774 LocalTemporary value;
3776 value = new LocalTemporary (SwitchType);
3778 unwrap.EmitCheck (ec);
3779 ig.Emit (OpCodes.Brfalse, null_target);
3783 } else if (!is_constant) {
3784 value = new LocalTemporary (SwitchType);
3791 // Setup the codegen context
3793 Label old_end = ec.LoopEnd;
3794 Switch old_switch = ec.Switch;
3796 ec.LoopEnd = ig.DefineLabel ();
3801 if (constant_section != null)
3802 constant_section.Block.Emit (ec);
3803 } else if (string_dictionary != null) {
3804 DoEmitStringSwitch (value, ec);
3806 TableSwitchEmit (ec, value);
3812 // Restore context state.
3813 ig.MarkLabel (ec.LoopEnd);
3816 // Restore the previous context
3818 ec.LoopEnd = old_end;
3819 ec.Switch = old_switch;
3822 protected override void CloneTo (CloneContext clonectx, Statement t)
3824 Switch target = (Switch) t;
3826 target.Expr = Expr.Clone (clonectx);
3827 target.Sections = new ArrayList ();
3828 foreach (SwitchSection ss in Sections){
3829 target.Sections.Add (ss.Clone (clonectx));
3834 // A place where execution can restart in an iterator
3835 public abstract class ResumableStatement : Statement
3838 protected Label resume_point;
3840 public Label PrepareForEmit (EmitContext ec)
3844 resume_point = ec.ig.DefineLabel ();
3846 return resume_point;
3849 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3853 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3858 // Base class for statements that are implemented in terms of try...finally
3859 public abstract class ExceptionStatement : ResumableStatement
3863 protected abstract void EmitPreTryBody (EmitContext ec);
3864 protected abstract void EmitTryBody (EmitContext ec);
3865 protected abstract void EmitFinallyBody (EmitContext ec);
3867 protected sealed override void DoEmit (EmitContext ec)
3869 ILGenerator ig = ec.ig;
3871 EmitPreTryBody (ec);
3873 if (resume_points != null) {
3874 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3875 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3878 ig.BeginExceptionBlock ();
3880 if (resume_points != null) {
3881 ig.MarkLabel (resume_point);
3883 // For normal control flow, we want to fall-through the Switch
3884 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3885 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3886 IntConstant.EmitInt (ig, first_resume_pc);
3887 ig.Emit (OpCodes.Sub);
3889 Label [] labels = new Label [resume_points.Count];
3890 for (int i = 0; i < resume_points.Count; ++i)
3891 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3892 ig.Emit (OpCodes.Switch, labels);
3897 ig.BeginFinallyBlock ();
3899 Label start_finally = ec.ig.DefineLabel ();
3900 if (resume_points != null) {
3901 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3902 ig.Emit (OpCodes.Brfalse_S, start_finally);
3903 ig.Emit (OpCodes.Endfinally);
3906 ig.MarkLabel (start_finally);
3907 EmitFinallyBody (ec);
3909 ig.EndExceptionBlock ();
3912 public void SomeCodeFollows ()
3914 code_follows = true;
3917 protected void ResolveReachability (EmitContext ec)
3919 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3920 // So, ensure there's some IL code after this statement.
3921 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3922 ec.NeedReturnLabel ();
3926 ArrayList resume_points;
3927 int first_resume_pc;
3928 public void AddResumePoint (ResumableStatement stmt, int pc)
3930 if (resume_points == null) {
3931 resume_points = new ArrayList ();
3932 first_resume_pc = pc;
3935 if (pc != first_resume_pc + resume_points.Count)
3936 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3938 resume_points.Add (stmt);
3941 Label dispose_try_block;
3942 bool prepared_for_dispose, emitted_dispose;
3943 public override Label PrepareForDispose (EmitContext ec, Label end)
3945 if (!prepared_for_dispose) {
3946 prepared_for_dispose = true;
3947 dispose_try_block = ec.ig.DefineLabel ();
3949 return dispose_try_block;
3952 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3954 if (emitted_dispose)
3957 emitted_dispose = true;
3959 ILGenerator ig = ec.ig;
3961 Label end_of_try = ig.DefineLabel ();
3963 // Ensure that the only way we can get into this code is through a dispatcher
3964 if (have_dispatcher)
3965 ig.Emit (OpCodes.Br, end);
3967 ig.BeginExceptionBlock ();
3969 ig.MarkLabel (dispose_try_block);
3971 Label [] labels = null;
3972 for (int i = 0; i < resume_points.Count; ++i) {
3973 ResumableStatement s = (ResumableStatement) resume_points [i];
3974 Label ret = s.PrepareForDispose (ec, end_of_try);
3975 if (ret.Equals (end_of_try) && labels == null)
3977 if (labels == null) {
3978 labels = new Label [resume_points.Count];
3979 for (int j = 0; j < i; ++j)
3980 labels [j] = end_of_try;
3985 if (labels != null) {
3987 for (j = 1; j < labels.Length; ++j)
3988 if (!labels [0].Equals (labels [j]))
3990 bool emit_dispatcher = j < labels.Length;
3992 if (emit_dispatcher) {
3993 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3994 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3995 IntConstant.EmitInt (ig, first_resume_pc);
3996 ig.Emit (OpCodes.Sub);
3997 ig.Emit (OpCodes.Switch, labels);
3998 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4001 foreach (ResumableStatement s in resume_points)
4002 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4005 ig.MarkLabel (end_of_try);
4007 ig.BeginFinallyBlock ();
4009 EmitFinallyBody (ec);
4011 ig.EndExceptionBlock ();
4015 public class Lock : ExceptionStatement {
4017 public Statement Statement;
4018 TemporaryVariable temp;
4020 public Lock (Expression expr, Statement stmt, Location l)
4027 public override bool Resolve (EmitContext ec)
4029 expr = expr.Resolve (ec);
4033 if (expr.Type.IsValueType){
4034 Report.Error (185, loc,
4035 "`{0}' is not a reference type as required by the lock statement",
4036 TypeManager.CSharpName (expr.Type));
4040 ec.StartFlowBranching (this);
4041 bool ok = Statement.Resolve (ec);
4042 ec.EndFlowBranching ();
4044 ResolveReachability (ec);
4046 // Avoid creating libraries that reference the internal
4049 if (t == TypeManager.null_type)
4050 t = TypeManager.object_type;
4052 temp = new TemporaryVariable (t, loc);
4055 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4056 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4057 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4058 monitor_type, "Enter", loc, TypeManager.object_type);
4059 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4060 monitor_type, "Exit", loc, TypeManager.object_type);
4066 protected override void EmitPreTryBody (EmitContext ec)
4068 ILGenerator ig = ec.ig;
4070 temp.EmitAssign (ec, expr);
4072 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4075 protected override void EmitTryBody (EmitContext ec)
4077 Statement.Emit (ec);
4080 protected override void EmitFinallyBody (EmitContext ec)
4083 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4086 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4088 expr.MutateHoistedGenericType (storey);
4089 temp.MutateHoistedGenericType (storey);
4090 Statement.MutateHoistedGenericType (storey);
4093 protected override void CloneTo (CloneContext clonectx, Statement t)
4095 Lock target = (Lock) t;
4097 target.expr = expr.Clone (clonectx);
4098 target.Statement = Statement.Clone (clonectx);
4102 public class Unchecked : Statement {
4105 public Unchecked (Block b)
4111 public override bool Resolve (EmitContext ec)
4113 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4114 return Block.Resolve (ec);
4117 protected override void DoEmit (EmitContext ec)
4119 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4123 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4125 Block.MutateHoistedGenericType (storey);
4128 protected override void CloneTo (CloneContext clonectx, Statement t)
4130 Unchecked target = (Unchecked) t;
4132 target.Block = clonectx.LookupBlock (Block);
4136 public class Checked : Statement {
4139 public Checked (Block b)
4142 b.Unchecked = false;
4145 public override bool Resolve (EmitContext ec)
4147 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4148 return Block.Resolve (ec);
4151 protected override void DoEmit (EmitContext ec)
4153 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4157 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4159 Block.MutateHoistedGenericType (storey);
4162 protected override void CloneTo (CloneContext clonectx, Statement t)
4164 Checked target = (Checked) t;
4166 target.Block = clonectx.LookupBlock (Block);
4170 public class Unsafe : Statement {
4173 public Unsafe (Block b)
4176 Block.Unsafe = true;
4179 public override bool Resolve (EmitContext ec)
4181 using (ec.With (EmitContext.Flags.InUnsafe, true))
4182 return Block.Resolve (ec);
4185 protected override void DoEmit (EmitContext ec)
4187 using (ec.With (EmitContext.Flags.InUnsafe, true))
4191 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4193 Block.MutateHoistedGenericType (storey);
4196 protected override void CloneTo (CloneContext clonectx, Statement t)
4198 Unsafe target = (Unsafe) t;
4200 target.Block = clonectx.LookupBlock (Block);
4207 public class Fixed : Statement {
4209 ArrayList declarators;
4210 Statement statement;
4215 abstract class Emitter
4217 protected LocalInfo vi;
4218 protected Expression converted;
4220 protected Emitter (Expression expr, LocalInfo li)
4226 public abstract void Emit (EmitContext ec);
4227 public abstract void EmitExit (EmitContext ec);
4230 class ExpressionEmitter : Emitter {
4231 public ExpressionEmitter (Expression converted, LocalInfo li) :
4232 base (converted, li)
4236 public override void Emit (EmitContext ec) {
4238 // Store pointer in pinned location
4240 converted.Emit (ec);
4244 public override void EmitExit (EmitContext ec)
4246 ec.ig.Emit (OpCodes.Ldc_I4_0);
4247 ec.ig.Emit (OpCodes.Conv_U);
4252 class StringEmitter : Emitter {
4253 class StringPtr : Expression
4257 public StringPtr (LocalBuilder b, Location l)
4260 eclass = ExprClass.Value;
4261 type = TypeManager.char_ptr_type;
4265 public override Expression CreateExpressionTree (EmitContext ec)
4267 throw new NotSupportedException ("ET");
4270 public override Expression DoResolve (EmitContext ec)
4272 // This should never be invoked, we are born in fully
4273 // initialized state.
4278 public override void Emit (EmitContext ec)
4280 if (TypeManager.int_get_offset_to_string_data == null) {
4281 // TODO: Move to resolve !!
4282 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4283 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4286 ILGenerator ig = ec.ig;
4288 ig.Emit (OpCodes.Ldloc, b);
4289 ig.Emit (OpCodes.Conv_I);
4290 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4291 ig.Emit (OpCodes.Add);
4295 LocalBuilder pinned_string;
4298 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4304 public override void Emit (EmitContext ec)
4306 ILGenerator ig = ec.ig;
4307 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4309 converted.Emit (ec);
4310 ig.Emit (OpCodes.Stloc, pinned_string);
4312 Expression sptr = new StringPtr (pinned_string, loc);
4313 converted = Convert.ImplicitConversionRequired (
4314 ec, sptr, vi.VariableType, loc);
4316 if (converted == null)
4319 converted.Emit (ec);
4323 public override void EmitExit (EmitContext ec)
4325 ec.ig.Emit (OpCodes.Ldnull);
4326 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4330 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4333 declarators = decls;
4338 public Statement Statement {
4339 get { return statement; }
4342 public override bool Resolve (EmitContext ec)
4345 Expression.UnsafeError (loc);
4349 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4350 if (texpr == null) {
4351 if (type is VarExpr)
4352 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4357 expr_type = texpr.Type;
4359 data = new Emitter [declarators.Count];
4361 if (!expr_type.IsPointer){
4362 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4367 foreach (Pair p in declarators){
4368 LocalInfo vi = (LocalInfo) p.First;
4369 Expression e = (Expression) p.Second;
4371 vi.VariableInfo.SetAssigned (ec);
4372 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4375 // The rules for the possible declarators are pretty wise,
4376 // but the production on the grammar is more concise.
4378 // So we have to enforce these rules here.
4380 // We do not resolve before doing the case 1 test,
4381 // because the grammar is explicit in that the token &
4382 // is present, so we need to test for this particular case.
4386 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4390 ec.InFixedInitializer = true;
4392 ec.InFixedInitializer = false;
4399 if (e.Type.IsArray){
4400 Type array_type = TypeManager.GetElementType (e.Type);
4403 // Provided that array_type is unmanaged,
4405 if (!TypeManager.VerifyUnManaged (array_type, loc))
4409 // and T* is implicitly convertible to the
4410 // pointer type given in the fixed statement.
4412 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4414 Expression converted = Convert.ImplicitConversionRequired (
4415 ec, array_ptr, vi.VariableType, loc);
4416 if (converted == null)
4420 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4422 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4423 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4424 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4425 new NullPointer (loc),
4428 converted = converted.Resolve (ec);
4430 data [i] = new ExpressionEmitter (converted, vi);
4439 if (e.Type == TypeManager.string_type){
4440 data [i] = new StringEmitter (e, vi, loc);
4445 // Case 4: fixed buffer
4446 if (e is FixedBufferPtr) {
4447 data [i++] = new ExpressionEmitter (e, vi);
4452 // Case 1: & object.
4454 Unary u = e as Unary;
4455 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4456 IVariableReference vr = u.Expr as IVariableReference;
4457 if (vr == null || !vr.IsFixedVariable) {
4458 data [i] = new ExpressionEmitter (e, vi);
4462 if (data [i++] == null)
4463 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4465 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4468 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4469 bool ok = statement.Resolve (ec);
4470 bool flow_unreachable = ec.EndFlowBranching ();
4471 has_ret = flow_unreachable;
4476 protected override void DoEmit (EmitContext ec)
4478 for (int i = 0; i < data.Length; i++) {
4482 statement.Emit (ec);
4488 // Clear the pinned variable
4490 for (int i = 0; i < data.Length; i++) {
4491 data [i].EmitExit (ec);
4495 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4497 // Fixed statement cannot be used inside anonymous methods or lambdas
4498 throw new NotSupportedException ();
4501 protected override void CloneTo (CloneContext clonectx, Statement t)
4503 Fixed target = (Fixed) t;
4505 target.type = type.Clone (clonectx);
4506 target.declarators = new ArrayList (declarators.Count);
4507 foreach (Pair p in declarators) {
4508 LocalInfo vi = (LocalInfo) p.First;
4509 Expression e = (Expression) p.Second;
4511 target.declarators.Add (
4512 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4515 target.statement = statement.Clone (clonectx);
4519 public class Catch : Statement {
4520 public readonly string Name;
4522 public Block VarBlock;
4524 Expression type_expr;
4527 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4532 VarBlock = var_block;
4536 public Type CatchType {
4542 public bool IsGeneral {
4544 return type_expr == null;
4548 protected override void DoEmit (EmitContext ec)
4550 ILGenerator ig = ec.ig;
4552 if (CatchType != null)
4553 ig.BeginCatchBlock (CatchType);
4555 ig.BeginCatchBlock (TypeManager.object_type);
4557 if (VarBlock != null)
4561 // TODO: Move to resolve
4562 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4566 if (lvr.IsHoisted) {
4567 LocalTemporary lt = new LocalTemporary (lvr.Type);
4571 // Variable is at the top of the stack
4572 source = EmptyExpression.Null;
4575 lvr.EmitAssign (ec, source, false, false);
4577 ig.Emit (OpCodes.Pop);
4582 public override bool Resolve (EmitContext ec)
4584 using (ec.With (EmitContext.Flags.InCatch, true)) {
4585 if (type_expr != null) {
4586 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4592 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4593 Error (155, "The type caught or thrown must be derived from System.Exception");
4599 if (!Block.Resolve (ec))
4602 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4603 // emit the "unused variable" warnings.
4604 if (VarBlock != null)
4605 return VarBlock.Resolve (ec);
4611 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4614 type = storey.MutateType (type);
4615 if (VarBlock != null)
4616 VarBlock.MutateHoistedGenericType (storey);
4617 Block.MutateHoistedGenericType (storey);
4620 protected override void CloneTo (CloneContext clonectx, Statement t)
4622 Catch target = (Catch) t;
4624 if (type_expr != null)
4625 target.type_expr = type_expr.Clone (clonectx);
4626 if (VarBlock != null)
4627 target.VarBlock = clonectx.LookupBlock (VarBlock);
4628 target.Block = clonectx.LookupBlock (Block);
4632 public class TryFinally : ExceptionStatement {
4636 public TryFinally (Statement stmt, Block fini, Location l)
4643 public override bool Resolve (EmitContext ec)
4647 ec.StartFlowBranching (this);
4649 if (!stmt.Resolve (ec))
4653 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4654 using (ec.With (EmitContext.Flags.InFinally, true)) {
4655 if (!fini.Resolve (ec))
4659 ec.EndFlowBranching ();
4661 ResolveReachability (ec);
4666 protected override void EmitPreTryBody (EmitContext ec)
4670 protected override void EmitTryBody (EmitContext ec)
4675 protected override void EmitFinallyBody (EmitContext ec)
4680 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4682 stmt.MutateHoistedGenericType (storey);
4683 fini.MutateHoistedGenericType (storey);
4686 protected override void CloneTo (CloneContext clonectx, Statement t)
4688 TryFinally target = (TryFinally) t;
4690 target.stmt = (Statement) stmt.Clone (clonectx);
4692 target.fini = clonectx.LookupBlock (fini);
4696 public class TryCatch : Statement {
4698 public ArrayList Specific;
4699 public Catch General;
4700 bool inside_try_finally, code_follows;
4702 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4705 this.Specific = catch_clauses;
4706 this.General = null;
4707 this.inside_try_finally = inside_try_finally;
4709 for (int i = 0; i < catch_clauses.Count; ++i) {
4710 Catch c = (Catch) catch_clauses [i];
4712 if (i != catch_clauses.Count - 1)
4713 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4715 catch_clauses.RemoveAt (i);
4723 public override bool Resolve (EmitContext ec)
4727 ec.StartFlowBranching (this);
4729 if (!Block.Resolve (ec))
4732 Type[] prev_catches = new Type [Specific.Count];
4734 foreach (Catch c in Specific){
4735 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4737 if (c.Name != null) {
4738 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4740 throw new Exception ();
4742 vi.VariableInfo = null;
4745 if (!c.Resolve (ec))
4748 Type resolved_type = c.CatchType;
4749 for (int ii = 0; ii < last_index; ++ii) {
4750 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4751 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4756 prev_catches [last_index++] = resolved_type;
4759 if (General != null) {
4760 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4761 foreach (Catch c in Specific){
4762 if (c.CatchType == TypeManager.exception_type) {
4763 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'");
4768 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4770 if (!General.Resolve (ec))
4774 ec.EndFlowBranching ();
4776 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4777 // So, ensure there's some IL code after this statement
4778 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4779 ec.NeedReturnLabel ();
4784 public void SomeCodeFollows ()
4786 code_follows = true;
4789 protected override void DoEmit (EmitContext ec)
4791 ILGenerator ig = ec.ig;
4793 if (!inside_try_finally)
4794 ig.BeginExceptionBlock ();
4798 foreach (Catch c in Specific)
4801 if (General != null)
4804 if (!inside_try_finally)
4805 ig.EndExceptionBlock ();
4808 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4810 Block.MutateHoistedGenericType (storey);
4812 if (General != null)
4813 General.MutateHoistedGenericType (storey);
4814 if (Specific != null) {
4815 foreach (Catch c in Specific)
4816 c.MutateHoistedGenericType (storey);
4820 protected override void CloneTo (CloneContext clonectx, Statement t)
4822 TryCatch target = (TryCatch) t;
4824 target.Block = clonectx.LookupBlock (Block);
4825 if (General != null)
4826 target.General = (Catch) General.Clone (clonectx);
4827 if (Specific != null){
4828 target.Specific = new ArrayList ();
4829 foreach (Catch c in Specific)
4830 target.Specific.Add (c.Clone (clonectx));
4835 public class UsingTemporary : ExceptionStatement {
4836 TemporaryVariable local_copy;
4837 public Statement Statement;
4841 public UsingTemporary (Expression expr, Statement stmt, Location l)
4848 public override bool Resolve (EmitContext ec)
4850 expr = expr.Resolve (ec);
4854 expr_type = expr.Type;
4856 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4857 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4858 Using.Error_IsNotConvertibleToIDisposable (expr);
4863 local_copy = new TemporaryVariable (expr_type, loc);
4864 local_copy.Resolve (ec);
4866 ec.StartFlowBranching (this);
4868 bool ok = Statement.Resolve (ec);
4870 ec.EndFlowBranching ();
4872 ResolveReachability (ec);
4874 if (TypeManager.void_dispose_void == null) {
4875 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4876 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4882 protected override void EmitPreTryBody (EmitContext ec)
4884 local_copy.EmitAssign (ec, expr);
4887 protected override void EmitTryBody (EmitContext ec)
4889 Statement.Emit (ec);
4892 protected override void EmitFinallyBody (EmitContext ec)
4894 ILGenerator ig = ec.ig;
4895 if (!expr_type.IsValueType) {
4896 Label skip = ig.DefineLabel ();
4897 local_copy.Emit (ec);
4898 ig.Emit (OpCodes.Brfalse, skip);
4899 local_copy.Emit (ec);
4900 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4901 ig.MarkLabel (skip);
4905 Expression ml = Expression.MemberLookup (
4906 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4907 "Dispose", Location.Null);
4909 if (!(ml is MethodGroupExpr)) {
4910 local_copy.Emit (ec);
4911 ig.Emit (OpCodes.Box, expr_type);
4912 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4916 MethodInfo mi = null;
4918 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4919 if (TypeManager.GetParameterData (mk).Count == 0) {
4926 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4930 local_copy.AddressOf (ec, AddressOp.Load);
4931 ig.Emit (OpCodes.Call, mi);
4934 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4936 expr_type = storey.MutateType (expr_type);
4937 local_copy.MutateHoistedGenericType (storey);
4938 Statement.MutateHoistedGenericType (storey);
4941 protected override void CloneTo (CloneContext clonectx, Statement t)
4943 UsingTemporary target = (UsingTemporary) t;
4945 target.expr = expr.Clone (clonectx);
4946 target.Statement = Statement.Clone (clonectx);
4950 public class Using : ExceptionStatement {
4952 public Statement EmbeddedStatement {
4953 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4959 Expression converted_var;
4960 ExpressionStatement assign;
4962 public Using (Expression var, Expression init, Statement stmt, Location l)
4970 bool ResolveVariable (EmitContext ec)
4972 ExpressionStatement a = new SimpleAssign (var, init, loc);
4973 a = a.ResolveStatement (ec);
4979 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4980 converted_var = var;
4984 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4986 Error_IsNotConvertibleToIDisposable (var);
4995 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4997 Report.SymbolRelatedToPreviousError (expr.Type);
4998 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4999 expr.GetSignatureForError ());
5002 protected override void EmitPreTryBody (EmitContext ec)
5004 assign.EmitStatement (ec);
5007 protected override void EmitTryBody (EmitContext ec)
5012 protected override void EmitFinallyBody (EmitContext ec)
5014 ILGenerator ig = ec.ig;
5016 if (!var.Type.IsValueType) {
5017 Label skip = ig.DefineLabel ();
5019 ig.Emit (OpCodes.Brfalse, skip);
5020 converted_var.Emit (ec);
5021 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5022 ig.MarkLabel (skip);
5024 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5026 if (!(ml is MethodGroupExpr)) {
5028 ig.Emit (OpCodes.Box, var.Type);
5029 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5031 MethodInfo mi = null;
5033 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5034 if (TypeManager.GetParameterData (mk).Count == 0) {
5041 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5045 IMemoryLocation mloc = (IMemoryLocation) var;
5047 mloc.AddressOf (ec, AddressOp.Load);
5048 ig.Emit (OpCodes.Call, mi);
5053 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5055 assign.MutateHoistedGenericType (storey);
5056 var.MutateHoistedGenericType (storey);
5057 stmt.MutateHoistedGenericType (storey);
5060 public override bool Resolve (EmitContext ec)
5062 if (!ResolveVariable (ec))
5065 ec.StartFlowBranching (this);
5067 bool ok = stmt.Resolve (ec);
5069 ec.EndFlowBranching ();
5071 ResolveReachability (ec);
5073 if (TypeManager.void_dispose_void == null) {
5074 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5075 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5081 protected override void CloneTo (CloneContext clonectx, Statement t)
5083 Using target = (Using) t;
5085 target.var = var.Clone (clonectx);
5086 target.init = init.Clone (clonectx);
5087 target.stmt = stmt.Clone (clonectx);
5092 /// Implementation of the foreach C# statement
5094 public class Foreach : Statement {
5096 Expression variable;
5098 Statement statement;
5100 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5101 Statement stmt, Location l)
5104 this.variable = var;
5110 public Statement Statement {
5111 get { return statement; }
5114 public override bool Resolve (EmitContext ec)
5116 expr = expr.Resolve (ec);
5121 Report.Error (186, loc, "Use of null is not valid in this context");
5125 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5126 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5127 expr.ExprClassName);
5131 if (expr.Type.IsArray) {
5132 statement = new ArrayForeach (type, variable, expr, statement, loc);
5134 statement = new CollectionForeach (type, variable, expr, statement, loc);
5137 return statement.Resolve (ec);
5140 protected override void DoEmit (EmitContext ec)
5142 ILGenerator ig = ec.ig;
5144 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5145 ec.LoopBegin = ig.DefineLabel ();
5146 ec.LoopEnd = ig.DefineLabel ();
5148 statement.Emit (ec);
5150 ec.LoopBegin = old_begin;
5151 ec.LoopEnd = old_end;
5154 protected class ArrayCounter : TemporaryVariable
5156 StatementExpression increment;
5158 public ArrayCounter (Location loc)
5159 : base (TypeManager.int32_type, loc)
5163 public void ResolveIncrement (EmitContext ec)
5165 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5166 increment.Resolve (ec);
5169 public void EmitIncrement (EmitContext ec)
5171 increment.Emit (ec);
5175 protected class ArrayForeach : Statement
5177 Expression variable, expr, conv;
5178 Statement statement;
5180 Expression var_type;
5181 TemporaryVariable[] lengths;
5182 ArrayCounter[] counter;
5185 TemporaryVariable copy;
5187 Expression[] length_exprs;
5189 public ArrayForeach (Expression var_type, Expression var,
5190 Expression expr, Statement stmt, Location l)
5192 this.var_type = var_type;
5193 this.variable = var;
5199 protected override void CloneTo (CloneContext clonectx, Statement target)
5201 throw new NotImplementedException ();
5204 public override bool Resolve (EmitContext ec)
5206 array_type = expr.Type;
5207 rank = array_type.GetArrayRank ();
5209 copy = new TemporaryVariable (array_type, loc);
5212 counter = new ArrayCounter [rank];
5213 lengths = new TemporaryVariable [rank];
5214 length_exprs = new Expression [rank];
5216 ArrayList list = new ArrayList (rank);
5217 for (int i = 0; i < rank; i++) {
5218 counter [i] = new ArrayCounter (loc);
5219 counter [i].ResolveIncrement (ec);
5221 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5222 lengths [i].Resolve (ec);
5225 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5227 ArrayList args = new ArrayList (1);
5228 args.Add (new Argument (new IntConstant (i, loc)));
5229 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5232 list.Add (counter [i]);
5235 access = new ElementAccess (copy, list).Resolve (ec);
5239 VarExpr ve = var_type as VarExpr;
5241 // Infer implicitly typed local variable from foreach array type
5242 var_type = new TypeExpression (access.Type, ve.Location);
5245 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5246 if (var_type == null)
5249 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5255 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5256 ec.CurrentBranching.CreateSibling ();
5258 variable = variable.ResolveLValue (ec, conv, loc);
5259 if (variable == null)
5262 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5263 if (!statement.Resolve (ec))
5265 ec.EndFlowBranching ();
5267 // There's no direct control flow from the end of the embedded statement to the end of the loop
5268 ec.CurrentBranching.CurrentUsageVector.Goto ();
5270 ec.EndFlowBranching ();
5275 protected override void DoEmit (EmitContext ec)
5277 ILGenerator ig = ec.ig;
5279 copy.EmitAssign (ec, expr);
5281 Label[] test = new Label [rank];
5282 Label[] loop = new Label [rank];
5284 for (int i = 0; i < rank; i++) {
5285 test [i] = ig.DefineLabel ();
5286 loop [i] = ig.DefineLabel ();
5288 lengths [i].EmitAssign (ec, length_exprs [i]);
5291 IntConstant zero = new IntConstant (0, loc);
5292 for (int i = 0; i < rank; i++) {
5293 counter [i].EmitAssign (ec, zero);
5295 ig.Emit (OpCodes.Br, test [i]);
5296 ig.MarkLabel (loop [i]);
5299 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5301 statement.Emit (ec);
5303 ig.MarkLabel (ec.LoopBegin);
5305 for (int i = rank - 1; i >= 0; i--){
5306 counter [i].EmitIncrement (ec);
5308 ig.MarkLabel (test [i]);
5309 counter [i].Emit (ec);
5310 lengths [i].Emit (ec);
5311 ig.Emit (OpCodes.Blt, loop [i]);
5314 ig.MarkLabel (ec.LoopEnd);
5317 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5319 copy.MutateHoistedGenericType (storey);
5320 conv.MutateHoistedGenericType (storey);
5321 variable.MutateHoistedGenericType (storey);
5322 statement.MutateHoistedGenericType (storey);
5324 for (int i = 0; i < rank; i++) {
5325 counter [i].MutateHoistedGenericType (storey);
5326 lengths [i].MutateHoistedGenericType (storey);
5331 protected class CollectionForeach : Statement
5333 Expression variable, expr;
5334 Statement statement;
5336 TemporaryVariable enumerator;
5341 MethodGroupExpr get_enumerator;
5342 PropertyExpr get_current;
5343 MethodInfo move_next;
5344 Expression var_type;
5345 Type enumerator_type;
5346 bool enumerator_found;
5348 public CollectionForeach (Expression var_type, Expression var,
5349 Expression expr, Statement stmt, Location l)
5351 this.var_type = var_type;
5352 this.variable = var;
5358 protected override void CloneTo (CloneContext clonectx, Statement target)
5360 throw new NotImplementedException ();
5363 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5365 Type return_type = mi.ReturnType;
5367 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5369 // Apply the same optimization as MS: skip the GetEnumerator
5370 // returning an IEnumerator, and use the one returning a
5371 // CharEnumerator instead. This allows us to avoid the
5372 // try-finally block and the boxing.
5377 // Ok, we can access it, now make sure that we can do something
5378 // with this `GetEnumerator'
5381 if (return_type == TypeManager.ienumerator_type ||
5382 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5383 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5385 // If it is not an interface, lets try to find the methods ourselves.
5386 // For example, if we have:
5387 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5388 // We can avoid the iface call. This is a runtime perf boost.
5389 // even bigger if we have a ValueType, because we avoid the cost
5392 // We have to make sure that both methods exist for us to take
5393 // this path. If one of the methods does not exist, we will just
5394 // use the interface. Sadly, this complex if statement is the only
5395 // way I could do this without a goto
5398 if (TypeManager.bool_movenext_void == null) {
5399 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5400 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5403 if (TypeManager.ienumerator_getcurrent == null) {
5404 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5405 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5410 // Prefer a generic enumerator over a non-generic one.
5412 if (return_type.IsInterface && return_type.IsGenericType) {
5413 enumerator_type = return_type;
5414 if (!FetchGetCurrent (ec, return_type))
5415 get_current = new PropertyExpr (
5416 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5417 if (!FetchMoveNext (return_type))
5418 move_next = TypeManager.bool_movenext_void;
5423 if (return_type.IsInterface ||
5424 !FetchMoveNext (return_type) ||
5425 !FetchGetCurrent (ec, return_type)) {
5426 enumerator_type = return_type;
5427 move_next = TypeManager.bool_movenext_void;
5428 get_current = new PropertyExpr (
5429 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5434 // Ok, so they dont return an IEnumerable, we will have to
5435 // find if they support the GetEnumerator pattern.
5438 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5439 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",
5440 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5445 enumerator_type = return_type;
5451 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5453 bool FetchMoveNext (Type t)
5455 MemberList move_next_list;
5457 move_next_list = TypeContainer.FindMembers (
5458 t, MemberTypes.Method,
5459 BindingFlags.Public | BindingFlags.Instance,
5460 Type.FilterName, "MoveNext");
5461 if (move_next_list.Count == 0)
5464 foreach (MemberInfo m in move_next_list){
5465 MethodInfo mi = (MethodInfo) m;
5467 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5468 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5478 // Retrieves a `public T get_Current ()' method from the Type `t'
5480 bool FetchGetCurrent (EmitContext ec, Type t)
5482 PropertyExpr pe = Expression.MemberLookup (
5483 ec.ContainerType, t, "Current", MemberTypes.Property,
5484 Expression.AllBindingFlags, loc) as PropertyExpr;
5493 // Retrieves a `public void Dispose ()' method from the Type `t'
5495 static MethodInfo FetchMethodDispose (Type t)
5497 MemberList dispose_list;
5499 dispose_list = TypeContainer.FindMembers (
5500 t, MemberTypes.Method,
5501 BindingFlags.Public | BindingFlags.Instance,
5502 Type.FilterName, "Dispose");
5503 if (dispose_list.Count == 0)
5506 foreach (MemberInfo m in dispose_list){
5507 MethodInfo mi = (MethodInfo) m;
5509 if (TypeManager.GetParameterData (mi).Count == 0){
5510 if (mi.ReturnType == TypeManager.void_type)
5517 void Error_Enumerator ()
5519 if (enumerator_found) {
5523 Report.Error (1579, loc,
5524 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5525 TypeManager.CSharpName (expr.Type));
5528 bool IsOverride (MethodInfo m)
5530 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5532 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5534 if (m is MethodBuilder)
5537 MethodInfo base_method = m.GetBaseDefinition ();
5538 return base_method != m;
5541 bool TryType (EmitContext ec, Type t)
5543 MethodGroupExpr mg = Expression.MemberLookup (
5544 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5545 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5549 MethodInfo result = null;
5550 MethodInfo tmp_move_next = null;
5551 PropertyExpr tmp_get_cur = null;
5552 Type tmp_enumerator_type = enumerator_type;
5553 foreach (MethodInfo mi in mg.Methods) {
5554 if (TypeManager.GetParameterData (mi).Count != 0)
5557 // Check whether GetEnumerator is public
5558 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5561 if (IsOverride (mi))
5564 enumerator_found = true;
5566 if (!GetEnumeratorFilter (ec, mi))
5569 if (result != null) {
5570 if (TypeManager.IsGenericType (result.ReturnType)) {
5571 if (!TypeManager.IsGenericType (mi.ReturnType))
5574 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5575 Report.SymbolRelatedToPreviousError (t);
5576 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5577 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5578 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5582 // Always prefer generics enumerators
5583 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5584 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5585 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5588 Report.SymbolRelatedToPreviousError (result);
5589 Report.SymbolRelatedToPreviousError (mi);
5590 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5591 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5596 tmp_move_next = move_next;
5597 tmp_get_cur = get_current;
5598 tmp_enumerator_type = enumerator_type;
5599 if (mi.DeclaringType == t)
5603 if (result != null) {
5604 move_next = tmp_move_next;
5605 get_current = tmp_get_cur;
5606 enumerator_type = tmp_enumerator_type;
5607 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5608 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5610 if (t != expr.Type) {
5611 expr = Convert.ExplicitConversion (
5614 throw new InternalErrorException ();
5617 get_enumerator.InstanceExpression = expr;
5618 get_enumerator.IsBase = t != expr.Type;
5626 bool ProbeCollectionType (EmitContext ec, Type t)
5628 int errors = Report.Errors;
5629 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5630 if (TryType (ec, tt))
5635 if (Report.Errors > errors)
5639 // Now try to find the method in the interfaces
5641 Type [] ifaces = TypeManager.GetInterfaces (t);
5642 foreach (Type i in ifaces){
5643 if (TryType (ec, i))
5650 public override bool Resolve (EmitContext ec)
5652 enumerator_type = TypeManager.ienumerator_type;
5654 if (!ProbeCollectionType (ec, expr.Type)) {
5655 Error_Enumerator ();
5659 bool is_disposable = !enumerator_type.IsSealed ||
5660 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5662 VarExpr ve = var_type as VarExpr;
5664 // Infer implicitly typed local variable from foreach enumerable type
5665 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5668 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5669 if (var_type == null)
5672 enumerator = new TemporaryVariable (enumerator_type, loc);
5673 enumerator.Resolve (ec);
5675 init = new Invocation (get_enumerator, null);
5676 init = init.Resolve (ec);
5680 Expression move_next_expr;
5682 MemberInfo[] mi = new MemberInfo[] { move_next };
5683 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5684 mg.InstanceExpression = enumerator;
5686 move_next_expr = new Invocation (mg, null);
5689 get_current.InstanceExpression = enumerator;
5691 Statement block = new CollectionForeachStatement (
5692 var_type.Type, variable, get_current, statement, loc);
5694 loop = new While (move_next_expr, block, loc);
5696 wrapper = is_disposable ?
5697 (Statement) new DisposableWrapper (this) :
5698 (Statement) new NonDisposableWrapper (this);
5699 return wrapper.Resolve (ec);
5702 protected override void DoEmit (EmitContext ec)
5707 class NonDisposableWrapper : Statement {
5708 CollectionForeach parent;
5710 internal NonDisposableWrapper (CollectionForeach parent)
5712 this.parent = parent;
5715 protected override void CloneTo (CloneContext clonectx, Statement target)
5717 throw new NotSupportedException ();
5720 public override bool Resolve (EmitContext ec)
5722 return parent.ResolveLoop (ec);
5725 protected override void DoEmit (EmitContext ec)
5727 parent.EmitLoopInit (ec);
5728 parent.EmitLoopBody (ec);
5731 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5733 throw new NotSupportedException ();
5737 class DisposableWrapper : ExceptionStatement {
5738 CollectionForeach parent;
5740 internal DisposableWrapper (CollectionForeach parent)
5742 this.parent = parent;
5745 protected override void CloneTo (CloneContext clonectx, Statement target)
5747 throw new NotSupportedException ();
5750 public override bool Resolve (EmitContext ec)
5754 ec.StartFlowBranching (this);
5756 if (!parent.ResolveLoop (ec))
5759 ec.EndFlowBranching ();
5761 ResolveReachability (ec);
5763 if (TypeManager.void_dispose_void == null) {
5764 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5765 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5770 protected override void EmitPreTryBody (EmitContext ec)
5772 parent.EmitLoopInit (ec);
5775 protected override void EmitTryBody (EmitContext ec)
5777 parent.EmitLoopBody (ec);
5780 protected override void EmitFinallyBody (EmitContext ec)
5782 parent.EmitFinallyBody (ec);
5785 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5787 throw new NotSupportedException ();
5791 bool ResolveLoop (EmitContext ec)
5793 return loop.Resolve (ec);
5796 void EmitLoopInit (EmitContext ec)
5798 enumerator.EmitAssign (ec, init);
5801 void EmitLoopBody (EmitContext ec)
5806 void EmitFinallyBody (EmitContext ec)
5808 ILGenerator ig = ec.ig;
5810 if (enumerator_type.IsValueType) {
5811 MethodInfo mi = FetchMethodDispose (enumerator_type);
5813 enumerator.AddressOf (ec, AddressOp.Load);
5814 ig.Emit (OpCodes.Call, mi);
5816 enumerator.Emit (ec);
5817 ig.Emit (OpCodes.Box, enumerator_type);
5818 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5821 Label call_dispose = ig.DefineLabel ();
5823 enumerator.Emit (ec);
5824 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5825 ig.Emit (OpCodes.Dup);
5826 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5828 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5829 // (Partition III, Section 3.35)
5830 ig.Emit (OpCodes.Endfinally);
5832 ig.MarkLabel (call_dispose);
5833 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5837 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5839 enumerator_type = storey.MutateType (enumerator_type);
5840 init.MutateHoistedGenericType (storey);
5841 loop.MutateHoistedGenericType (storey);
5845 protected class CollectionForeachStatement : Statement
5848 Expression variable, current, conv;
5849 Statement statement;
5852 public CollectionForeachStatement (Type type, Expression variable,
5853 Expression current, Statement statement,
5857 this.variable = variable;
5858 this.current = current;
5859 this.statement = statement;
5863 protected override void CloneTo (CloneContext clonectx, Statement target)
5865 throw new NotImplementedException ();
5868 public override bool Resolve (EmitContext ec)
5870 current = current.Resolve (ec);
5871 if (current == null)
5874 conv = Convert.ExplicitConversion (ec, current, type, loc);
5878 assign = new SimpleAssign (variable, conv, loc);
5879 if (assign.Resolve (ec) == null)
5882 if (!statement.Resolve (ec))
5888 protected override void DoEmit (EmitContext ec)
5890 assign.EmitStatement (ec);
5891 statement.Emit (ec);
5894 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5896 assign.MutateHoistedGenericType (storey);
5897 statement.MutateHoistedGenericType (storey);
5901 protected override void CloneTo (CloneContext clonectx, Statement t)
5903 Foreach target = (Foreach) t;
5905 target.type = type.Clone (clonectx);
5906 target.variable = variable.Clone (clonectx);
5907 target.expr = expr.Clone (clonectx);
5908 target.statement = statement.Clone (clonectx);
5911 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5913 statement.MutateHoistedGenericType (storey);