2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections;
19 using System.Collections.Specialized;
21 namespace Mono.CSharp {
23 public abstract class Statement {
27 /// Resolves the statement, true means that all sub-statements
30 public virtual bool Resolve (EmitContext ec)
36 /// We already know that the statement is unreachable, but we still
37 /// need to resolve it to catch errors.
39 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
42 // This conflicts with csc's way of doing this, but IMHO it's
43 // the right thing to do.
45 // If something is unreachable, we still check whether it's
46 // correct. This means that you cannot use unassigned variables
47 // in unreachable code, for instance.
51 Report.Warning (162, 2, loc, "Unreachable code detected");
53 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
54 bool ok = Resolve (ec);
55 ec.KillFlowBranching ();
61 /// Return value indicates whether all code paths emitted return.
63 protected abstract void DoEmit (EmitContext ec);
66 /// Utility wrapper routine for Error, just to beautify the code
68 public void Error (int error, string format, params object[] args)
70 Error (error, String.Format (format, args));
73 public void Error (int error, string s)
76 Report.Error (error, loc, s);
78 Report.Error (error, s);
82 /// Return value indicates whether all code paths emitted return.
84 public virtual void Emit (EmitContext ec)
91 // This routine must be overrided in derived classes and make copies
92 // of all the data that might be modified if resolved
94 protected abstract void CloneTo (CloneContext clonectx, Statement target);
96 public Statement Clone (CloneContext clonectx)
98 Statement s = (Statement) this.MemberwiseClone ();
99 CloneTo (clonectx, s);
103 public virtual Expression CreateExpressionTree (EmitContext ec)
105 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
109 public Statement PerformClone ()
111 CloneContext clonectx = new CloneContext ();
113 return Clone (clonectx);
116 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
120 // This class is used during the Statement.Clone operation
121 // to remap objects that have been cloned.
123 // Since blocks are cloned by Block.Clone, we need a way for
124 // expressions that must reference the block to be cloned
125 // pointing to the new cloned block.
127 public class CloneContext {
128 Hashtable block_map = new Hashtable ();
129 Hashtable variable_map;
131 public void AddBlockMap (Block from, Block to)
133 if (block_map.Contains (from))
135 block_map [from] = to;
138 public Block LookupBlock (Block from)
140 Block result = (Block) block_map [from];
143 result = (Block) from.Clone (this);
144 block_map [from] = result;
151 /// Remaps block to cloned copy if one exists.
153 public Block RemapBlockCopy (Block from)
155 Block mapped_to = (Block)block_map[from];
156 if (mapped_to == null)
162 public void AddVariableMap (LocalInfo from, LocalInfo to)
164 if (variable_map == null)
165 variable_map = new Hashtable ();
167 if (variable_map.Contains (from))
169 variable_map [from] = to;
172 public LocalInfo LookupVariable (LocalInfo from)
174 LocalInfo result = (LocalInfo) variable_map [from];
177 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
183 public sealed class EmptyStatement : Statement {
185 private EmptyStatement () {}
187 public static readonly EmptyStatement Value = new EmptyStatement ();
189 public override bool Resolve (EmitContext ec)
194 public override bool ResolveUnreachable (EmitContext ec, bool warn)
199 protected override void DoEmit (EmitContext ec)
203 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
207 protected override void CloneTo (CloneContext clonectx, Statement target)
213 public class If : Statement {
215 public Statement TrueStatement;
216 public Statement FalseStatement;
220 public If (Expression expr, Statement true_statement, Location l)
223 TrueStatement = true_statement;
227 public If (Expression expr,
228 Statement true_statement,
229 Statement false_statement,
233 TrueStatement = true_statement;
234 FalseStatement = false_statement;
238 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
240 expr.MutateHoistedGenericType (storey);
241 TrueStatement.MutateHoistedGenericType (storey);
242 if (FalseStatement != null)
243 FalseStatement.MutateHoistedGenericType (storey);
246 public override bool Resolve (EmitContext ec)
250 Report.Debug (1, "START IF BLOCK", loc);
252 expr = Expression.ResolveBoolean (ec, expr, loc);
258 Assign ass = expr as Assign;
259 if (ass != null && ass.Source is Constant) {
260 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
264 // Dead code elimination
266 if (expr is Constant){
267 bool take = !((Constant) expr).IsDefaultValue;
270 if (!TrueStatement.Resolve (ec))
273 if ((FalseStatement != null) &&
274 !FalseStatement.ResolveUnreachable (ec, true))
276 FalseStatement = null;
278 if (!TrueStatement.ResolveUnreachable (ec, true))
280 TrueStatement = null;
282 if ((FalseStatement != null) &&
283 !FalseStatement.Resolve (ec))
290 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
292 ok &= TrueStatement.Resolve (ec);
294 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
296 ec.CurrentBranching.CreateSibling ();
298 if (FalseStatement != null)
299 ok &= FalseStatement.Resolve (ec);
301 ec.EndFlowBranching ();
303 Report.Debug (1, "END IF BLOCK", loc);
308 protected override void DoEmit (EmitContext ec)
310 ILGenerator ig = ec.ig;
311 Label false_target = ig.DefineLabel ();
315 // If we're a boolean constant, Resolve() already
316 // eliminated dead code for us.
318 Constant c = expr as Constant;
320 c.EmitSideEffect (ec);
322 if (!c.IsDefaultValue)
323 TrueStatement.Emit (ec);
324 else if (FalseStatement != null)
325 FalseStatement.Emit (ec);
330 expr.EmitBranchable (ec, false_target, false);
332 TrueStatement.Emit (ec);
334 if (FalseStatement != null){
335 bool branch_emitted = false;
337 end = ig.DefineLabel ();
339 ig.Emit (OpCodes.Br, end);
340 branch_emitted = true;
343 ig.MarkLabel (false_target);
344 FalseStatement.Emit (ec);
349 ig.MarkLabel (false_target);
353 protected override void CloneTo (CloneContext clonectx, Statement t)
357 target.expr = expr.Clone (clonectx);
358 target.TrueStatement = TrueStatement.Clone (clonectx);
359 if (FalseStatement != null)
360 target.FalseStatement = FalseStatement.Clone (clonectx);
364 public class Do : Statement {
365 public Expression expr;
366 public Statement EmbeddedStatement;
368 public Do (Statement statement, Expression bool_expr, Location l)
371 EmbeddedStatement = statement;
375 public override bool Resolve (EmitContext ec)
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!EmbeddedStatement.Resolve (ec))
386 ec.EndFlowBranching ();
388 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
389 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
391 expr = Expression.ResolveBoolean (ec, expr, loc);
394 else if (expr is Constant){
395 bool infinite = !((Constant) expr).IsDefaultValue;
397 ec.CurrentBranching.CurrentUsageVector.Goto ();
400 ec.EndFlowBranching ();
405 protected override void DoEmit (EmitContext ec)
407 ILGenerator ig = ec.ig;
408 Label loop = ig.DefineLabel ();
409 Label old_begin = ec.LoopBegin;
410 Label old_end = ec.LoopEnd;
412 ec.LoopBegin = ig.DefineLabel ();
413 ec.LoopEnd = ig.DefineLabel ();
416 EmbeddedStatement.Emit (ec);
417 ig.MarkLabel (ec.LoopBegin);
420 // Dead code elimination
422 if (expr is Constant){
423 bool res = !((Constant) expr).IsDefaultValue;
425 expr.EmitSideEffect (ec);
427 ec.ig.Emit (OpCodes.Br, loop);
429 expr.EmitBranchable (ec, loop, true);
431 ig.MarkLabel (ec.LoopEnd);
433 ec.LoopBegin = old_begin;
434 ec.LoopEnd = old_end;
437 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
439 expr.MutateHoistedGenericType (storey);
440 EmbeddedStatement.MutateHoistedGenericType (storey);
443 protected override void CloneTo (CloneContext clonectx, Statement t)
447 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
448 target.expr = expr.Clone (clonectx);
452 public class While : Statement {
453 public Expression expr;
454 public Statement Statement;
455 bool infinite, empty;
457 public While (Expression bool_expr, Statement statement, Location l)
459 this.expr = bool_expr;
460 Statement = statement;
464 public override bool Resolve (EmitContext ec)
468 expr = Expression.ResolveBoolean (ec, expr, loc);
473 // Inform whether we are infinite or not
475 if (expr is Constant){
476 bool value = !((Constant) expr).IsDefaultValue;
479 if (!Statement.ResolveUnreachable (ec, true))
487 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
489 ec.CurrentBranching.CreateSibling ();
491 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
492 if (!Statement.Resolve (ec))
494 ec.EndFlowBranching ();
496 // There's no direct control flow from the end of the embedded statement to the end of the loop
497 ec.CurrentBranching.CurrentUsageVector.Goto ();
499 ec.EndFlowBranching ();
504 protected override void DoEmit (EmitContext ec)
507 expr.EmitSideEffect (ec);
511 ILGenerator ig = ec.ig;
512 Label old_begin = ec.LoopBegin;
513 Label old_end = ec.LoopEnd;
515 ec.LoopBegin = ig.DefineLabel ();
516 ec.LoopEnd = ig.DefineLabel ();
519 // Inform whether we are infinite or not
521 if (expr is Constant){
522 // expr is 'true', since the 'empty' case above handles the 'false' case
523 ig.MarkLabel (ec.LoopBegin);
524 expr.EmitSideEffect (ec);
526 ig.Emit (OpCodes.Br, ec.LoopBegin);
529 // Inform that we are infinite (ie, `we return'), only
530 // if we do not `break' inside the code.
532 ig.MarkLabel (ec.LoopEnd);
534 Label while_loop = ig.DefineLabel ();
536 ig.Emit (OpCodes.Br, ec.LoopBegin);
537 ig.MarkLabel (while_loop);
541 ig.MarkLabel (ec.LoopBegin);
544 expr.EmitBranchable (ec, while_loop, true);
546 ig.MarkLabel (ec.LoopEnd);
549 ec.LoopBegin = old_begin;
550 ec.LoopEnd = old_end;
553 public override void Emit (EmitContext ec)
558 protected override void CloneTo (CloneContext clonectx, Statement t)
560 While target = (While) t;
562 target.expr = expr.Clone (clonectx);
563 target.Statement = Statement.Clone (clonectx);
566 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
568 expr.MutateHoistedGenericType (storey);
569 Statement.MutateHoistedGenericType (storey);
573 public class For : Statement {
575 Statement InitStatement;
577 public Statement Statement;
578 bool infinite, empty;
580 public For (Statement init_statement,
586 InitStatement = init_statement;
588 Increment = increment;
589 Statement = statement;
593 public override bool Resolve (EmitContext ec)
597 if (InitStatement != null){
598 if (!InitStatement.Resolve (ec))
603 Test = Expression.ResolveBoolean (ec, Test, loc);
606 else if (Test is Constant){
607 bool value = !((Constant) Test).IsDefaultValue;
610 if (!Statement.ResolveUnreachable (ec, true))
612 if ((Increment != null) &&
613 !Increment.ResolveUnreachable (ec, false))
623 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
625 ec.CurrentBranching.CreateSibling ();
627 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
629 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
630 if (!Statement.Resolve (ec))
632 ec.EndFlowBranching ();
634 if (Increment != null){
635 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
636 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
639 if (!Increment.Resolve (ec))
644 // There's no direct control flow from the end of the embedded statement to the end of the loop
645 ec.CurrentBranching.CurrentUsageVector.Goto ();
647 ec.EndFlowBranching ();
652 protected override void DoEmit (EmitContext ec)
654 if (InitStatement != null && InitStatement != EmptyStatement.Value)
655 InitStatement.Emit (ec);
658 Test.EmitSideEffect (ec);
662 ILGenerator ig = ec.ig;
663 Label old_begin = ec.LoopBegin;
664 Label old_end = ec.LoopEnd;
665 Label loop = ig.DefineLabel ();
666 Label test = ig.DefineLabel ();
668 ec.LoopBegin = ig.DefineLabel ();
669 ec.LoopEnd = ig.DefineLabel ();
671 ig.Emit (OpCodes.Br, test);
675 ig.MarkLabel (ec.LoopBegin);
676 if (Increment != EmptyStatement.Value)
681 // If test is null, there is no test, and we are just
686 // The Resolve code already catches the case for
687 // Test == Constant (false) so we know that
690 if (Test is Constant) {
691 Test.EmitSideEffect (ec);
692 ig.Emit (OpCodes.Br, loop);
694 Test.EmitBranchable (ec, loop, true);
698 ig.Emit (OpCodes.Br, loop);
699 ig.MarkLabel (ec.LoopEnd);
701 ec.LoopBegin = old_begin;
702 ec.LoopEnd = old_end;
705 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
707 if (InitStatement != null)
708 InitStatement.MutateHoistedGenericType (storey);
710 Test.MutateHoistedGenericType (storey);
711 if (Increment != null)
712 Increment.MutateHoistedGenericType (storey);
714 Statement.MutateHoistedGenericType (storey);
717 protected override void CloneTo (CloneContext clonectx, Statement t)
719 For target = (For) t;
721 if (InitStatement != null)
722 target.InitStatement = InitStatement.Clone (clonectx);
724 target.Test = Test.Clone (clonectx);
725 if (Increment != null)
726 target.Increment = Increment.Clone (clonectx);
727 target.Statement = Statement.Clone (clonectx);
731 public class StatementExpression : Statement {
732 ExpressionStatement expr;
734 public StatementExpression (ExpressionStatement expr)
740 public override bool Resolve (EmitContext ec)
743 expr = expr.ResolveStatement (ec);
747 protected override void DoEmit (EmitContext ec)
749 expr.EmitStatement (ec);
752 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
754 expr.MutateHoistedGenericType (storey);
757 public override string ToString ()
759 return "StatementExpression (" + expr + ")";
762 protected override void CloneTo (CloneContext clonectx, Statement t)
764 StatementExpression target = (StatementExpression) t;
766 target.expr = (ExpressionStatement) expr.Clone (clonectx);
770 // A 'return' or a 'yield break'
771 public abstract class ExitStatement : Statement
773 protected bool unwind_protect;
774 protected abstract bool DoResolve (EmitContext ec);
776 public virtual void Error_FinallyClause ()
778 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
781 public sealed override bool Resolve (EmitContext ec)
786 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
788 ec.NeedReturnLabel ();
789 ec.CurrentBranching.CurrentUsageVector.Goto ();
795 /// Implements the return statement
797 public class Return : ExitStatement {
798 protected Expression Expr;
799 public Return (Expression expr, Location l)
805 protected override bool DoResolve (EmitContext ec)
808 if (ec.ReturnType == TypeManager.void_type)
811 Error (126, "An object of a type convertible to `{0}' is required " +
812 "for the return statement",
813 TypeManager.CSharpName (ec.ReturnType));
817 if (ec.CurrentBlock.Toplevel.IsIterator) {
818 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
819 "statement to return a value, or yield break to end the iteration");
822 AnonymousExpression am = ec.CurrentAnonymousMethod;
823 if (am == null && ec.ReturnType == TypeManager.void_type) {
824 MemberCore mc = ec.ResolveContext as MemberCore;
825 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
826 mc.GetSignatureForError ());
829 Expr = Expr.Resolve (ec);
833 if (Expr.Type != ec.ReturnType) {
834 if (ec.InferReturnType) {
836 // void cannot be used in contextual return
838 if (Expr.Type == TypeManager.void_type)
841 ec.ReturnType = Expr.Type;
843 Expr = Convert.ImplicitConversionRequired (
844 ec, Expr, ec.ReturnType, loc);
848 Report.Error (1662, loc,
849 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
850 am.ContainerType, am.GetSignatureForError ());
860 protected override void DoEmit (EmitContext ec)
866 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
870 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
872 ec.ig.Emit (OpCodes.Ret);
875 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
878 Expr.MutateHoistedGenericType (storey);
881 protected override void CloneTo (CloneContext clonectx, Statement t)
883 Return target = (Return) t;
884 // It's null for simple return;
886 target.Expr = Expr.Clone (clonectx);
890 public class Goto : Statement {
892 LabeledStatement label;
895 public override bool Resolve (EmitContext ec)
897 int errors = Report.Errors;
898 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
899 ec.CurrentBranching.CurrentUsageVector.Goto ();
900 return errors == Report.Errors;
903 public Goto (string label, Location l)
909 public string Target {
910 get { return target; }
913 public void SetResolvedTarget (LabeledStatement label)
916 label.AddReference ();
919 protected override void CloneTo (CloneContext clonectx, Statement target)
924 protected override void DoEmit (EmitContext ec)
927 throw new InternalErrorException ("goto emitted before target resolved");
928 Label l = label.LabelTarget (ec);
929 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
932 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
937 public class LabeledStatement : Statement {
944 FlowBranching.UsageVector vectors;
946 public LabeledStatement (string name, Location l)
952 public Label LabelTarget (EmitContext ec)
957 label = ec.ig.DefineLabel ();
967 public bool IsDefined {
968 get { return defined; }
971 public bool HasBeenReferenced {
972 get { return referenced; }
975 public FlowBranching.UsageVector JumpOrigins {
976 get { return vectors; }
979 public void AddUsageVector (FlowBranching.UsageVector vector)
981 vector = vector.Clone ();
982 vector.Next = vectors;
986 protected override void CloneTo (CloneContext clonectx, Statement target)
991 public override bool Resolve (EmitContext ec)
993 // this flow-branching will be terminated when the surrounding block ends
994 ec.StartFlowBranching (this);
998 protected override void DoEmit (EmitContext ec)
1000 if (ig != null && ig != ec.ig)
1001 throw new InternalErrorException ("cannot happen");
1003 ec.ig.MarkLabel (label);
1006 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1010 public void AddReference ()
1018 /// `goto default' statement
1020 public class GotoDefault : Statement {
1022 public GotoDefault (Location l)
1027 protected override void CloneTo (CloneContext clonectx, Statement target)
1032 public override bool Resolve (EmitContext ec)
1034 ec.CurrentBranching.CurrentUsageVector.Goto ();
1038 protected override void DoEmit (EmitContext ec)
1040 if (ec.Switch == null){
1041 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1045 if (!ec.Switch.GotDefault){
1046 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1049 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1052 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1058 /// `goto case' statement
1060 public class GotoCase : Statement {
1064 public GotoCase (Expression e, Location l)
1070 public override bool Resolve (EmitContext ec)
1072 if (ec.Switch == null){
1073 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1077 expr = expr.Resolve (ec);
1081 Constant c = expr as Constant;
1083 Error (150, "A constant value is expected");
1087 Type type = ec.Switch.SwitchType;
1088 if (!Convert.ImplicitStandardConversionExists (c, type))
1089 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1090 "convertible to type `{0}'", TypeManager.CSharpName (type));
1093 object val = c.GetValue ();
1094 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1095 val = TypeManager.ChangeType (val, type, out fail);
1098 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1099 c.GetSignatureForError (), TypeManager.CSharpName (type));
1104 val = SwitchLabel.NullStringCase;
1106 sl = (SwitchLabel) ec.Switch.Elements [val];
1109 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1110 (c.GetValue () == null ? "null" : val.ToString ()));
1114 ec.CurrentBranching.CurrentUsageVector.Goto ();
1118 protected override void DoEmit (EmitContext ec)
1120 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1123 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1125 expr.MutateHoistedGenericType (storey);
1128 protected override void CloneTo (CloneContext clonectx, Statement t)
1130 GotoCase target = (GotoCase) t;
1132 target.expr = expr.Clone (clonectx);
1136 public class Throw : Statement {
1139 public Throw (Expression expr, Location l)
1145 public override bool Resolve (EmitContext ec)
1148 ec.CurrentBranching.CurrentUsageVector.Goto ();
1149 return ec.CurrentBranching.CheckRethrow (loc);
1152 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1153 ec.CurrentBranching.CurrentUsageVector.Goto ();
1160 if ((t != TypeManager.exception_type) &&
1161 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1162 !(expr is NullLiteral)) {
1163 Error (155, "The type caught or thrown must be derived from System.Exception");
1169 protected override void DoEmit (EmitContext ec)
1172 ec.ig.Emit (OpCodes.Rethrow);
1176 ec.ig.Emit (OpCodes.Throw);
1180 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1183 expr.MutateHoistedGenericType (storey);
1186 protected override void CloneTo (CloneContext clonectx, Statement t)
1188 Throw target = (Throw) t;
1191 target.expr = expr.Clone (clonectx);
1195 public class Break : Statement {
1197 public Break (Location l)
1202 bool unwind_protect;
1204 public override bool Resolve (EmitContext ec)
1206 int errors = Report.Errors;
1207 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1208 ec.CurrentBranching.CurrentUsageVector.Goto ();
1209 return errors == Report.Errors;
1212 protected override void DoEmit (EmitContext ec)
1214 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1217 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1221 protected override void CloneTo (CloneContext clonectx, Statement t)
1227 public class Continue : Statement {
1229 public Continue (Location l)
1234 bool unwind_protect;
1236 public override bool Resolve (EmitContext ec)
1238 int errors = Report.Errors;
1239 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1240 ec.CurrentBranching.CurrentUsageVector.Goto ();
1241 return errors == Report.Errors;
1244 protected override void DoEmit (EmitContext ec)
1246 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1249 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1253 protected override void CloneTo (CloneContext clonectx, Statement t)
1259 public interface ILocalVariable
1261 void Emit (EmitContext ec);
1262 void EmitAssign (EmitContext ec);
1263 void EmitAddressOf (EmitContext ec);
1266 public interface IKnownVariable {
1267 Block Block { get; }
1268 Location Location { get; }
1272 // The information about a user-perceived local variable
1274 public class LocalInfo : IKnownVariable, ILocalVariable {
1275 public readonly Expression Type;
1277 public Type VariableType;
1278 public readonly string Name;
1279 public readonly Location Location;
1280 public readonly Block Block;
1282 public VariableInfo VariableInfo;
1283 public HoistedVariable HoistedVariableReference;
1292 CompilerGenerated = 64,
1296 public enum ReadOnlyContext: byte {
1303 ReadOnlyContext ro_context;
1304 LocalBuilder builder;
1306 public LocalInfo (Expression type, string name, Block block, Location l)
1314 public LocalInfo (DeclSpace ds, Block block, Location l)
1316 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1321 public void ResolveVariable (EmitContext ec)
1323 if (HoistedVariableReference != null)
1326 if (builder == null) {
1329 // This is needed to compile on both .NET 1.x and .NET 2.x
1330 // the later introduced `DeclareLocal (Type t, bool pinned)'
1332 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1334 builder = ec.ig.DeclareLocal (VariableType);
1338 public void Emit (EmitContext ec)
1340 ec.ig.Emit (OpCodes.Ldloc, builder);
1343 public void EmitAssign (EmitContext ec)
1345 ec.ig.Emit (OpCodes.Stloc, builder);
1348 public void EmitAddressOf (EmitContext ec)
1350 ec.ig.Emit (OpCodes.Ldloca, builder);
1353 public void EmitSymbolInfo (EmitContext ec)
1355 if (builder != null)
1356 ec.DefineLocalVariable (Name, builder);
1359 public bool IsThisAssigned (EmitContext ec)
1361 if (VariableInfo == null)
1362 throw new Exception ();
1364 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1367 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1370 public bool IsAssigned (EmitContext ec)
1372 if (VariableInfo == null)
1373 throw new Exception ();
1375 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1378 public bool Resolve (EmitContext ec)
1380 if (VariableType != null)
1383 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1387 VariableType = texpr.Type;
1389 if (TypeManager.IsGenericParameter (VariableType))
1392 if (VariableType.IsAbstract && VariableType.IsSealed) {
1393 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1397 if (VariableType.IsPointer && !ec.InUnsafe)
1398 Expression.UnsafeError (Location);
1403 public bool IsConstant {
1404 get { return (flags & Flags.IsConstant) != 0; }
1405 set { flags |= Flags.IsConstant; }
1408 public bool AddressTaken {
1409 get { return (flags & Flags.AddressTaken) != 0; }
1410 set { flags |= Flags.AddressTaken; }
1413 public bool CompilerGenerated {
1414 get { return (flags & Flags.CompilerGenerated) != 0; }
1415 set { flags |= Flags.CompilerGenerated; }
1418 public override string ToString ()
1420 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1421 Name, Type, VariableInfo, Location);
1425 get { return (flags & Flags.Used) != 0; }
1426 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1429 public bool ReadOnly {
1430 get { return (flags & Flags.ReadOnly) != 0; }
1433 public void SetReadOnlyContext (ReadOnlyContext context)
1435 flags |= Flags.ReadOnly;
1436 ro_context = context;
1439 public string GetReadOnlyContext ()
1442 throw new InternalErrorException ("Variable is not readonly");
1444 switch (ro_context) {
1445 case ReadOnlyContext.Fixed:
1446 return "fixed variable";
1447 case ReadOnlyContext.Foreach:
1448 return "foreach iteration variable";
1449 case ReadOnlyContext.Using:
1450 return "using variable";
1452 throw new NotImplementedException ();
1456 // Whether the variable is pinned, if Pinned the variable has been
1457 // allocated in a pinned slot with DeclareLocal.
1459 public bool Pinned {
1460 get { return (flags & Flags.Pinned) != 0; }
1461 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1464 public bool IsThis {
1465 get { return (flags & Flags.IsThis) != 0; }
1466 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1469 Block IKnownVariable.Block {
1470 get { return Block; }
1473 Location IKnownVariable.Location {
1474 get { return Location; }
1477 public LocalInfo Clone (CloneContext clonectx)
1480 // Variables in anonymous block are not resolved yet
1482 if (VariableType == null)
1483 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1486 // Variables in method block are resolved
1488 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1489 li.VariableType = VariableType;
1495 /// Block represents a C# block.
1499 /// This class is used in a number of places: either to represent
1500 /// explicit blocks that the programmer places or implicit blocks.
1502 /// Implicit blocks are used as labels or to introduce variable
1505 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1506 /// they contain extra information that is not necessary on normal blocks.
1508 public class Block : Statement {
1509 public Block Parent;
1510 public Location StartLocation;
1511 public Location EndLocation = Location.Null;
1513 public ExplicitBlock Explicit;
1514 public ToplevelBlock Toplevel; // TODO: Use Explicit
1517 public enum Flags : byte {
1520 VariablesInitialized = 4,
1525 HasStoreyAccess = 128
1527 protected Flags flags;
1529 public bool Unchecked {
1530 get { return (flags & Flags.Unchecked) != 0; }
1531 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1534 public bool Unsafe {
1535 get { return (flags & Flags.Unsafe) != 0; }
1536 set { flags |= Flags.Unsafe; }
1540 // The statements in this block
1542 protected ArrayList statements;
1545 // An array of Blocks. We keep track of children just
1546 // to generate the local variable declarations.
1548 // Statements and child statements are handled through the
1554 // Labels. (label, block) pairs.
1556 protected HybridDictionary labels;
1559 // Keeps track of (name, type) pairs
1561 IDictionary variables;
1562 protected IDictionary range_variables;
1565 // Keeps track of constants
1566 HybridDictionary constants;
1569 // Temporary variables.
1571 ArrayList temporary_variables;
1574 // If this is a switch section, the enclosing switch block.
1578 ArrayList scope_initializers;
1580 ArrayList anonymous_children;
1582 protected static int id;
1586 int assignable_slots;
1587 bool unreachable_shown;
1590 public Block (Block parent)
1591 : this (parent, (Flags) 0, Location.Null, Location.Null)
1594 public Block (Block parent, Flags flags)
1595 : this (parent, flags, Location.Null, Location.Null)
1598 public Block (Block parent, Location start, Location end)
1599 : this (parent, (Flags) 0, start, end)
1602 public Block (Block parent, Flags flags, Location start, Location end)
1604 if (parent != null) {
1605 parent.AddChild (this);
1607 // the appropriate constructors will fixup these fields
1608 Toplevel = parent.Toplevel;
1609 Explicit = parent.Explicit;
1612 this.Parent = parent;
1614 this.StartLocation = start;
1615 this.EndLocation = end;
1618 statements = new ArrayList (4);
1621 public Block CreateSwitchBlock (Location start)
1623 // FIXME: should this be implicit?
1624 Block new_block = new ExplicitBlock (this, start, start);
1625 new_block.switch_block = this;
1630 get { return this_id; }
1633 public IDictionary Variables {
1635 if (variables == null)
1636 variables = new ListDictionary ();
1641 void AddChild (Block b)
1643 if (children == null)
1644 children = new ArrayList (1);
1649 public void SetEndLocation (Location loc)
1654 protected static void Error_158 (string name, Location loc)
1656 Report.Error (158, loc, "The label `{0}' shadows another label " +
1657 "by the same name in a contained scope", name);
1661 /// Adds a label to the current block.
1665 /// false if the name already exists in this block. true
1669 public bool AddLabel (LabeledStatement target)
1671 if (switch_block != null)
1672 return switch_block.AddLabel (target);
1674 string name = target.Name;
1677 while (cur != null) {
1678 LabeledStatement s = cur.DoLookupLabel (name);
1680 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1681 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1685 if (this == Explicit)
1691 while (cur != null) {
1692 if (cur.DoLookupLabel (name) != null) {
1693 Error_158 (name, target.loc);
1697 if (children != null) {
1698 foreach (Block b in children) {
1699 LabeledStatement s = b.DoLookupLabel (name);
1703 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1704 Error_158 (name, target.loc);
1712 Toplevel.CheckError158 (name, target.loc);
1715 labels = new HybridDictionary();
1717 labels.Add (name, target);
1721 public LabeledStatement LookupLabel (string name)
1723 LabeledStatement s = DoLookupLabel (name);
1727 if (children == null)
1730 foreach (Block child in children) {
1731 if (Explicit != child.Explicit)
1734 s = child.LookupLabel (name);
1742 LabeledStatement DoLookupLabel (string name)
1744 if (switch_block != null)
1745 return switch_block.LookupLabel (name);
1748 if (labels.Contains (name))
1749 return ((LabeledStatement) labels [name]);
1754 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1757 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1758 while (kvi == null) {
1759 b = b.Explicit.Parent;
1762 kvi = b.Explicit.GetKnownVariable (name);
1768 // Is kvi.Block nested inside 'b'
1769 if (b.Explicit != kvi.Block.Explicit) {
1771 // If a variable by the same name it defined in a nested block of this
1772 // block, we violate the invariant meaning in a block.
1775 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1776 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1781 // It's ok if the definition is in a nested subblock of b, but not
1782 // nested inside this block -- a definition in a sibling block
1783 // should not affect us.
1789 // Block 'b' and kvi.Block are the same textual block.
1790 // However, different variables are extant.
1792 // Check if the variable is in scope in both blocks. We use
1793 // an indirect check that depends on AddVariable doing its
1794 // part in maintaining the invariant-meaning-in-block property.
1796 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1799 if (this is ToplevelBlock) {
1800 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1801 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1806 // Even though we detected the error when the name is used, we
1807 // treat it as if the variable declaration was in error.
1809 Report.SymbolRelatedToPreviousError (loc, name);
1810 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1814 public LocalInfo AddVariable (Expression type, string name, Location l)
1816 LocalInfo vi = GetLocalInfo (name);
1818 Report.SymbolRelatedToPreviousError (vi.Location, name);
1819 if (Explicit == vi.Block.Explicit) {
1820 if (type is Linq.ImplicitQueryParameter.ImplicitType && type == vi.Type)
1821 Error_AlreadyDeclared (l, name);
1823 Error_AlreadyDeclared (l, name, null);
1825 Error_AlreadyDeclared (l, name, "parent");
1830 Expression e = Toplevel.GetParameterReference (name, Location.Null);
1832 //Report.SymbolRelatedToPreviousError (pi.Parameter.Location, name);
1833 Error_AlreadyDeclared (loc, name, "parent or current");
1837 if (Toplevel.GenericMethod != null) {
1838 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1839 if (tp.Name == name) {
1840 Report.SymbolRelatedToPreviousError (tp);
1841 Error_AlreadyDeclaredTypeParameter (loc, name);
1847 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1849 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1850 Error_AlreadyDeclared (l, name, "child");
1854 vi = new LocalInfo (type, name, this, l);
1857 if ((flags & Flags.VariablesInitialized) != 0)
1858 throw new InternalErrorException ("block has already been resolved");
1863 protected virtual void AddVariable (LocalInfo li)
1865 Variables.Add (li.Name, li);
1866 Explicit.AddKnownVariable (li.Name, li);
1869 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1871 if (reason == null) {
1872 Error_AlreadyDeclared (loc, var);
1876 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1877 "in this scope because it would give a different meaning " +
1878 "to `{0}', which is already used in a `{1}' scope " +
1879 "to denote something else", var, reason);
1882 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1884 Report.Error (128, loc,
1885 "A local variable named `{0}' is already defined in this scope", name);
1888 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1890 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1893 public bool AddConstant (Expression type, string name, Expression value, Location l)
1895 if (AddVariable (type, name, l) == null)
1898 if (constants == null)
1899 constants = new HybridDictionary();
1901 constants.Add (name, value);
1903 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1908 static int next_temp_id = 0;
1910 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1912 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1914 if (temporary_variables == null)
1915 temporary_variables = new ArrayList ();
1917 int id = ++next_temp_id;
1918 string name = "$s_" + id.ToString ();
1920 LocalInfo li = new LocalInfo (te, name, this, loc);
1921 li.CompilerGenerated = true;
1922 temporary_variables.Add (li);
1926 public LocalInfo GetLocalInfo (string name)
1929 for (Block b = this; b != null; b = b.Parent) {
1930 if (b.variables != null) {
1931 ret = (LocalInfo) b.variables [name];
1936 if (b.range_variables != null) {
1937 ret = (LocalInfo) b.range_variables [name];
1946 public Expression GetVariableType (string name)
1948 LocalInfo vi = GetLocalInfo (name);
1949 return vi == null ? null : vi.Type;
1952 public Expression GetConstantExpression (string name)
1954 for (Block b = this; b != null; b = b.Parent) {
1955 if (b.constants != null) {
1956 Expression ret = b.constants [name] as Expression;
1965 // It should be used by expressions which require to
1966 // register a statement during resolve process.
1968 public void AddScopeStatement (Statement s)
1970 if (scope_initializers == null)
1971 scope_initializers = new ArrayList ();
1973 scope_initializers.Add (s);
1976 public void AddStatement (Statement s)
1979 flags |= Flags.BlockUsed;
1983 get { return (flags & Flags.BlockUsed) != 0; }
1988 flags |= Flags.BlockUsed;
1991 public bool HasRet {
1992 get { return (flags & Flags.HasRet) != 0; }
1995 public bool IsDestructor {
1996 get { return (flags & Flags.IsDestructor) != 0; }
1999 public void SetDestructor ()
2001 flags |= Flags.IsDestructor;
2004 public int AssignableSlots {
2007 // if ((flags & Flags.VariablesInitialized) == 0)
2008 // throw new Exception ("Variables have not been initialized yet");
2009 return assignable_slots;
2013 public ArrayList AnonymousChildren {
2014 get { return anonymous_children; }
2017 public void AddAnonymousChild (ToplevelBlock b)
2019 if (anonymous_children == null)
2020 anonymous_children = new ArrayList ();
2022 anonymous_children.Add (b);
2025 void DoResolveConstants (EmitContext ec)
2027 if (constants == null)
2030 if (variables == null)
2031 throw new InternalErrorException ("cannot happen");
2033 foreach (DictionaryEntry de in variables) {
2034 string name = (string) de.Key;
2035 LocalInfo vi = (LocalInfo) de.Value;
2036 Type variable_type = vi.VariableType;
2038 if (variable_type == null) {
2039 if (vi.Type is VarExpr)
2040 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2045 Expression cv = (Expression) constants [name];
2049 // Don't let 'const int Foo = Foo;' succeed.
2050 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2051 // which in turn causes the 'must be constant' error to be triggered.
2052 constants.Remove (name);
2054 if (!Const.IsConstantTypeValid (variable_type)) {
2055 Const.Error_InvalidConstantType (variable_type, loc);
2059 ec.CurrentBlock = this;
2061 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2062 e = cv.Resolve (ec);
2067 Constant ce = e as Constant;
2069 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2073 e = ce.ConvertImplicitly (variable_type);
2075 if (TypeManager.IsReferenceType (variable_type))
2076 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2078 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2082 constants.Add (name, e);
2083 vi.IsConstant = true;
2087 protected void ResolveMeta (EmitContext ec, int offset)
2089 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2091 // If some parent block was unsafe, we remain unsafe even if this block
2092 // isn't explicitly marked as such.
2093 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2094 flags |= Flags.VariablesInitialized;
2096 if (variables != null) {
2097 foreach (LocalInfo li in variables.Values) {
2098 if (!li.Resolve (ec))
2100 li.VariableInfo = new VariableInfo (li, offset);
2101 offset += li.VariableInfo.Length;
2104 assignable_slots = offset;
2106 DoResolveConstants (ec);
2108 if (children == null)
2110 foreach (Block b in children)
2111 b.ResolveMeta (ec, offset);
2116 // Emits the local variable declarations for a block
2118 public virtual void EmitMeta (EmitContext ec)
2120 if (variables != null){
2121 foreach (LocalInfo vi in variables.Values)
2122 vi.ResolveVariable (ec);
2125 if (temporary_variables != null) {
2126 for (int i = 0; i < temporary_variables.Count; i++)
2127 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2130 if (children != null) {
2131 for (int i = 0; i < children.Count; i++)
2132 ((Block)children[i]).EmitMeta(ec);
2136 void UsageWarning ()
2138 if (variables == null || Report.WarningLevel < 3)
2141 foreach (DictionaryEntry de in variables) {
2142 LocalInfo vi = (LocalInfo) de.Value;
2145 string name = (string) de.Key;
2147 // vi.VariableInfo can be null for 'catch' variables
2148 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2149 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2151 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2156 static void CheckPossibleMistakenEmptyStatement (Statement s)
2160 // Some statements are wrapped by a Block. Since
2161 // others' internal could be changed, here I treat
2162 // them as possibly wrapped by Block equally.
2163 Block b = s as Block;
2164 if (b != null && b.statements.Count == 1)
2165 s = (Statement) b.statements [0];
2168 body = ((Lock) s).Statement;
2170 body = ((For) s).Statement;
2171 else if (s is Foreach)
2172 body = ((Foreach) s).Statement;
2173 else if (s is While)
2174 body = ((While) s).Statement;
2175 else if (s is Fixed)
2176 body = ((Fixed) s).Statement;
2177 else if (s is Using)
2178 body = ((Using) s).EmbeddedStatement;
2179 else if (s is UsingTemporary)
2180 body = ((UsingTemporary) s).Statement;
2184 if (body == null || body is EmptyStatement)
2185 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2188 public override bool Resolve (EmitContext ec)
2190 Block prev_block = ec.CurrentBlock;
2193 int errors = Report.Errors;
2195 ec.CurrentBlock = this;
2196 ec.StartFlowBranching (this);
2198 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2201 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2202 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2203 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2204 // responsible for handling the situation.
2206 int statement_count = statements.Count;
2207 for (int ix = 0; ix < statement_count; ix++){
2208 Statement s = (Statement) statements [ix];
2209 // Check possible empty statement (CS0642)
2210 if (Report.WarningLevel >= 3 &&
2211 ix + 1 < statement_count &&
2212 statements [ix + 1] is ExplicitBlock)
2213 CheckPossibleMistakenEmptyStatement (s);
2216 // Warn if we detect unreachable code.
2219 if (s is EmptyStatement)
2222 if (!unreachable_shown && !(s is LabeledStatement)) {
2223 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2224 unreachable_shown = true;
2227 Block c_block = s as Block;
2228 if (c_block != null)
2229 c_block.unreachable = c_block.unreachable_shown = true;
2233 // Note that we're not using ResolveUnreachable() for unreachable
2234 // statements here. ResolveUnreachable() creates a temporary
2235 // flow branching and kills it afterwards. This leads to problems
2236 // if you have two unreachable statements where the first one
2237 // assigns a variable and the second one tries to access it.
2240 if (!s.Resolve (ec)) {
2242 if (ec.IsInProbingMode)
2245 statements [ix] = EmptyStatement.Value;
2249 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2250 statements [ix] = EmptyStatement.Value;
2252 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2253 if (unreachable && s is LabeledStatement)
2254 throw new InternalErrorException ("should not happen");
2257 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2258 ec.CurrentBranching, statement_count);
2260 while (ec.CurrentBranching is FlowBranchingLabeled)
2261 ec.EndFlowBranching ();
2263 bool flow_unreachable = ec.EndFlowBranching ();
2265 ec.CurrentBlock = prev_block;
2267 if (flow_unreachable)
2268 flags |= Flags.HasRet;
2270 // If we're a non-static `struct' constructor which doesn't have an
2271 // initializer, then we must initialize all of the struct's fields.
2272 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2275 if ((labels != null) && (Report.WarningLevel >= 2)) {
2276 foreach (LabeledStatement label in labels.Values)
2277 if (!label.HasBeenReferenced)
2278 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2281 if (ok && errors == Report.Errors)
2287 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2289 unreachable_shown = true;
2293 Report.Warning (162, 2, loc, "Unreachable code detected");
2295 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2296 bool ok = Resolve (ec);
2297 ec.KillFlowBranching ();
2302 protected override void DoEmit (EmitContext ec)
2304 for (int ix = 0; ix < statements.Count; ix++){
2305 Statement s = (Statement) statements [ix];
2310 public override void Emit (EmitContext ec)
2312 Block prev_block = ec.CurrentBlock;
2313 ec.CurrentBlock = this;
2315 if (scope_initializers != null) {
2316 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2318 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2319 foreach (Statement s in scope_initializers)
2323 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2326 ec.Mark (StartLocation);
2329 if (SymbolWriter.HasSymbolWriter)
2330 EmitSymbolInfo (ec);
2332 ec.CurrentBlock = prev_block;
2335 protected virtual void EmitSymbolInfo (EmitContext ec)
2337 if (variables != null) {
2338 foreach (LocalInfo vi in variables.Values) {
2339 vi.EmitSymbolInfo (ec);
2344 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2346 MutateVariables (storey);
2348 if (scope_initializers != null) {
2349 foreach (Statement s in scope_initializers)
2350 s.MutateHoistedGenericType (storey);
2353 foreach (Statement s in statements)
2354 s.MutateHoistedGenericType (storey);
2357 void MutateVariables (AnonymousMethodStorey storey)
2359 if (variables != null) {
2360 foreach (LocalInfo vi in variables.Values) {
2361 vi.VariableType = storey.MutateType (vi.VariableType);
2365 if (temporary_variables != null) {
2366 foreach (LocalInfo vi in temporary_variables)
2367 vi.VariableType = storey.MutateType (vi.VariableType);
2371 public override string ToString ()
2373 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2376 protected override void CloneTo (CloneContext clonectx, Statement t)
2378 Block target = (Block) t;
2380 clonectx.AddBlockMap (this, target);
2382 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2383 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2385 target.Parent = clonectx.RemapBlockCopy (Parent);
2387 if (variables != null){
2388 target.variables = new Hashtable ();
2390 foreach (DictionaryEntry de in variables){
2391 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2392 target.variables [de.Key] = newlocal;
2393 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2397 target.statements = new ArrayList (statements.Count);
2398 foreach (Statement s in statements)
2399 target.statements.Add (s.Clone (clonectx));
2401 if (target.children != null){
2402 target.children = new ArrayList (children.Count);
2403 foreach (Block b in children){
2404 target.children.Add (clonectx.LookupBlock (b));
2409 // TODO: labels, switch_block, constants (?), anonymous_children
2414 public class ExplicitBlock : Block {
2415 HybridDictionary known_variables;
2416 protected AnonymousMethodStorey am_storey;
2418 public ExplicitBlock (Block parent, Location start, Location end)
2419 : this (parent, (Flags) 0, start, end)
2423 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2424 : base (parent, flags, start, end)
2426 this.Explicit = this;
2430 // Marks a variable with name @name as being used in this or a child block.
2431 // If a variable name has been used in a child block, it's illegal to
2432 // declare a variable with the same name in the current block.
2434 internal void AddKnownVariable (string name, IKnownVariable info)
2436 if (known_variables == null)
2437 known_variables = new HybridDictionary();
2439 known_variables [name] = info;
2442 Parent.Explicit.AddKnownVariable (name, info);
2445 public AnonymousMethodStorey AnonymousMethodStorey {
2446 get { return am_storey; }
2450 // Creates anonymous method storey in current block
2452 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2455 // When referencing a variable in iterator storey from children anonymous method
2457 if (Toplevel.am_storey is IteratorStorey) {
2458 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2459 return Toplevel.am_storey;
2463 // An iterator has only 1 storey block
2465 if (ec.CurrentIterator != null)
2466 return ec.CurrentIterator.Storey;
2468 if (am_storey == null) {
2469 MemberBase mc = ec.ResolveContext as MemberBase;
2470 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2473 // Create anonymous method storey for this block
2475 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2479 // Creates a link between this block and the anonymous method
2481 // An anonymous method can reference variables from any outer block, but they are
2482 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2483 // need to create another link between those variable storeys
2485 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2489 public override void Emit (EmitContext ec)
2491 if (am_storey != null)
2492 am_storey.EmitStoreyInstantiation (ec);
2494 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2495 if (emit_debug_info)
2500 if (emit_debug_info)
2504 public override void EmitMeta (EmitContext ec)
2507 // Creates anonymous method storey
2509 if (am_storey != null) {
2510 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2511 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2514 am_storey.DefineType ();
2515 am_storey.ResolveType ();
2516 am_storey.Define ();
2517 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2523 internal IKnownVariable GetKnownVariable (string name)
2525 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2528 public void PropagateStoreyReference (AnonymousMethodStorey s)
2530 if (Parent != null && am_storey != s) {
2531 if (am_storey != null)
2532 am_storey.AddParentStoreyReference (s);
2534 Parent.Explicit.PropagateStoreyReference (s);
2538 public override bool Resolve (EmitContext ec)
2540 bool ok = base.Resolve (ec);
2543 // Discard an anonymous method storey when this block has no hoisted variables
2545 if (am_storey != null) {
2546 if (am_storey.HasHoistedVariables) {
2547 AddScopeStatement (new AnonymousMethodStorey.ThisInitializer (am_storey));
2557 protected override void CloneTo (CloneContext clonectx, Statement t)
2559 ExplicitBlock target = (ExplicitBlock) t;
2560 target.known_variables = null;
2561 base.CloneTo (clonectx, t);
2565 public class ToplevelParameterInfo : IKnownVariable {
2566 public readonly ToplevelBlock Block;
2567 public readonly int Index;
2568 public VariableInfo VariableInfo;
2570 Block IKnownVariable.Block {
2571 get { return Block; }
2573 public Parameter Parameter {
2574 get { return Block.Parameters [Index]; }
2577 public Type ParameterType {
2578 get { return Block.Parameters.Types [Index]; }
2581 public Location Location {
2582 get { return Parameter.Location; }
2585 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2593 // A toplevel block contains extra information, the split is done
2594 // only to separate information that would otherwise bloat the more
2595 // lightweight Block.
2597 // In particular, this was introduced when the support for Anonymous
2598 // Methods was implemented.
2600 public class ToplevelBlock : ExplicitBlock {
2601 GenericMethod generic;
2602 FlowBranchingToplevel top_level_branching;
2603 Parameters parameters;
2604 ToplevelParameterInfo[] parameter_info;
2605 LocalInfo this_variable;
2607 public HoistedVariable HoistedThisVariable;
2610 // The parameters for the block.
2612 public Parameters Parameters {
2613 get { return parameters; }
2616 public GenericMethod GenericMethod {
2617 get { return generic; }
2620 public bool HasStoreyAccess {
2621 set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
2622 get { return (flags & Flags.HasStoreyAccess) != 0; }
2625 public ToplevelBlock Container {
2626 get { return Parent == null ? null : Parent.Toplevel; }
2629 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2630 this (parent, (Flags) 0, parameters, start)
2634 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2635 this (parent, parameters, start)
2637 this.generic = generic;
2640 public ToplevelBlock (Parameters parameters, Location start) :
2641 this (null, (Flags) 0, parameters, start)
2645 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2646 this (null, flags, parameters, start)
2650 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2651 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2652 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2653 base (null, flags, start, Location.Null)
2655 this.Toplevel = this;
2657 this.parameters = parameters;
2658 this.Parent = parent;
2660 parent.AddAnonymousChild (this);
2662 if (!this.parameters.IsEmpty)
2663 ProcessParameters ();
2666 public ToplevelBlock (Location loc)
2667 : this (null, (Flags) 0, Parameters.EmptyReadOnlyParameters, loc)
2671 protected override void CloneTo (CloneContext clonectx, Statement t)
2673 ToplevelBlock target = (ToplevelBlock) t;
2674 base.CloneTo (clonectx, t);
2676 if (parameters.Count != 0)
2677 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2678 for (int i = 0; i < parameters.Count; ++i)
2679 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2682 public bool CheckError158 (string name, Location loc)
2684 if (AnonymousChildren != null) {
2685 foreach (ToplevelBlock child in AnonymousChildren) {
2686 if (!child.CheckError158 (name, loc))
2691 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2692 if (!c.DoCheckError158 (name, loc))
2699 public virtual Expression GetTransparentIdentifier (string name)
2704 void ProcessParameters ()
2706 int n = parameters.Count;
2707 parameter_info = new ToplevelParameterInfo [n];
2708 for (int i = 0; i < n; ++i) {
2709 parameter_info [i] = new ToplevelParameterInfo (this, i);
2711 Parameter p = parameters [i];
2715 string name = p.Name;
2716 LocalInfo vi = GetLocalInfo (name);
2718 Report.SymbolRelatedToPreviousError (vi.Location, name);
2719 Error_AlreadyDeclared (loc, name, "parent or current");
2723 if (Parent != null) {
2724 Expression e = Parent.Toplevel.GetParameterReference (name, loc);
2726 //Report.SymbolRelatedToPreviousError (pi.Location, name);
2727 Error_AlreadyDeclared (loc, name, "parent or current");
2732 AddKnownVariable (name, parameter_info [i]);
2735 // mark this block as "used" so that we create local declarations in a sub-block
2736 // FIXME: This appears to uncover a lot of bugs
2740 bool DoCheckError158 (string name, Location loc)
2742 LabeledStatement s = LookupLabel (name);
2744 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2745 Error_158 (name, loc);
2752 public override Expression CreateExpressionTree (EmitContext ec)
2754 if (statements.Count == 1)
2755 return ((Statement) statements [0]).CreateExpressionTree (ec);
2757 return base.CreateExpressionTree (ec);
2761 // Reformats this block to be top-level iterator block
2763 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2765 // Create block with original statements
2766 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2769 // TODO: Change to iter_block.statements = statements;
2770 foreach (Statement stmt in source.statements)
2771 iter_block.AddStatement (stmt);
2772 labels = source.labels;
2774 AddStatement (new IteratorStatement (iterator, iter_block));
2776 source.statements = new ArrayList (1);
2777 source.AddStatement (new Return (iterator, iterator.Location));
2778 source.IsIterator = false;
2780 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2781 source.am_storey = iterator_storey;
2782 return iterator_storey;
2785 public FlowBranchingToplevel TopLevelBranching {
2786 get { return top_level_branching; }
2790 // Returns a `ParameterReference' for the given name, or null if there
2791 // is no such parameter
2793 public Expression GetParameterReference (string name, Location loc)
2795 for (ToplevelBlock t = this; t != null; t = t.Container) {
2796 Expression expr = t.GetParameterReferenceExpression (name, loc);
2804 Expression GetParameterReferenceExpression (string name, Location loc)
2806 int idx = parameters.GetParameterIndexByName (name);
2808 null : new ParameterReference (parameter_info[idx], loc);
2812 // Returns the "this" instance variable of this block.
2813 // See AddThisVariable() for more information.
2815 public LocalInfo ThisVariable {
2816 get { return this_variable; }
2820 // This is used by non-static `struct' constructors which do not have an
2821 // initializer - in this case, the constructor must initialize all of the
2822 // struct's fields. To do this, we add a "this" variable and use the flow
2823 // analysis code to ensure that it's been fully initialized before control
2824 // leaves the constructor.
2826 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2828 if (this_variable == null) {
2829 this_variable = new LocalInfo (ds, this, l);
2830 this_variable.Used = true;
2831 this_variable.IsThis = true;
2833 Variables.Add ("this", this_variable);
2836 return this_variable;
2839 public bool IsIterator {
2840 get { return (flags & Flags.IsIterator) != 0; }
2841 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2844 public bool IsThisAssigned (EmitContext ec)
2846 return this_variable == null || this_variable.IsThisAssigned (ec);
2849 public bool ResolveMeta (EmitContext ec, Parameters ip)
2851 int errors = Report.Errors;
2852 int orig_count = parameters.Count;
2854 if (top_level_branching != null)
2860 // Assert: orig_count != parameter.Count => orig_count == 0
2861 if (orig_count != 0 && orig_count != parameters.Count)
2862 throw new InternalErrorException ("parameter information mismatch");
2864 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2866 for (int i = 0; i < orig_count; ++i) {
2867 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2869 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2872 VariableInfo vi = new VariableInfo (ip, i, offset);
2873 parameter_info [i].VariableInfo = vi;
2874 offset += vi.Length;
2877 ResolveMeta (ec, offset);
2879 top_level_branching = ec.StartFlowBranching (this);
2881 return Report.Errors == errors;
2885 // Check whether all `out' parameters have been assigned.
2887 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2889 if (vector.IsUnreachable)
2892 int n = parameter_info == null ? 0 : parameter_info.Length;
2894 for (int i = 0; i < n; i++) {
2895 VariableInfo var = parameter_info [i].VariableInfo;
2900 if (vector.IsAssigned (var, false))
2903 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2908 public override void EmitMeta (EmitContext ec)
2910 parameters.ResolveVariable ();
2912 // Avoid declaring an IL variable for this_variable since it is not accessed
2913 // from the generated IL
2914 if (this_variable != null)
2915 Variables.Remove ("this");
2919 protected override void EmitSymbolInfo (EmitContext ec)
2921 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2922 if ((ae != null) && (ae.Storey != null))
2923 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2925 base.EmitSymbolInfo (ec);
2928 public override void Emit (EmitContext ec)
2931 ec.Mark (EndLocation);
2935 public class SwitchLabel {
2942 Label il_label_code;
2943 bool il_label_code_set;
2945 public static readonly object NullStringCase = new object ();
2948 // if expr == null, then it is the default case.
2950 public SwitchLabel (Expression expr, Location l)
2956 public Expression Label {
2962 public Location Location {
2966 public object Converted {
2972 public Label GetILLabel (EmitContext ec)
2975 il_label = ec.ig.DefineLabel ();
2976 il_label_set = true;
2981 public Label GetILLabelCode (EmitContext ec)
2983 if (!il_label_code_set){
2984 il_label_code = ec.ig.DefineLabel ();
2985 il_label_code_set = true;
2987 return il_label_code;
2991 // Resolves the expression, reduces it to a literal if possible
2992 // and then converts it to the requested type.
2994 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2996 Expression e = label.Resolve (ec);
3001 Constant c = e as Constant;
3003 Report.Error (150, loc, "A constant value is expected");
3007 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3008 converted = NullStringCase;
3012 if (allow_nullable && c.GetValue () == null) {
3013 converted = NullStringCase;
3017 c = c.ImplicitConversionRequired (ec, required_type, loc);
3021 converted = c.GetValue ();
3025 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3028 if (converted == null)
3030 else if (converted == NullStringCase)
3033 label = converted.ToString ();
3035 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3036 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3039 public SwitchLabel Clone (CloneContext clonectx)
3041 return new SwitchLabel (label.Clone (clonectx), loc);
3045 public class SwitchSection {
3046 // An array of SwitchLabels.
3047 public readonly ArrayList Labels;
3048 public readonly Block Block;
3050 public SwitchSection (ArrayList labels, Block block)
3056 public SwitchSection Clone (CloneContext clonectx)
3058 ArrayList cloned_labels = new ArrayList ();
3060 foreach (SwitchLabel sl in cloned_labels)
3061 cloned_labels.Add (sl.Clone (clonectx));
3063 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3067 public class Switch : Statement {
3068 public ArrayList Sections;
3069 public Expression Expr;
3072 /// Maps constants whose type type SwitchType to their SwitchLabels.
3074 public IDictionary Elements;
3077 /// The governing switch type
3079 public Type SwitchType;
3084 Label default_target;
3086 Expression new_expr;
3089 SwitchSection constant_section;
3090 SwitchSection default_section;
3092 ExpressionStatement string_dictionary;
3093 FieldExpr switch_cache_field;
3094 static int unique_counter;
3098 // Nullable Types support for GMCS.
3100 Nullable.Unwrap unwrap;
3102 protected bool HaveUnwrap {
3103 get { return unwrap != null; }
3106 protected bool HaveUnwrap {
3107 get { return false; }
3112 // The types allowed to be implicitly cast from
3113 // on the governing type
3115 static Type [] allowed_types;
3117 public Switch (Expression e, ArrayList sects, Location l)
3124 public bool GotDefault {
3126 return default_section != null;
3130 public Label DefaultTarget {
3132 return default_target;
3137 // Determines the governing type for a switch. The returned
3138 // expression might be the expression from the switch, or an
3139 // expression that includes any potential conversions to the
3140 // integral types or to string.
3142 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3146 if (t == TypeManager.byte_type ||
3147 t == TypeManager.sbyte_type ||
3148 t == TypeManager.ushort_type ||
3149 t == TypeManager.short_type ||
3150 t == TypeManager.uint32_type ||
3151 t == TypeManager.int32_type ||
3152 t == TypeManager.uint64_type ||
3153 t == TypeManager.int64_type ||
3154 t == TypeManager.char_type ||
3155 t == TypeManager.string_type ||
3156 t == TypeManager.bool_type ||
3157 TypeManager.IsEnumType (t))
3160 if (allowed_types == null){
3161 allowed_types = new Type [] {
3162 TypeManager.sbyte_type,
3163 TypeManager.byte_type,
3164 TypeManager.short_type,
3165 TypeManager.ushort_type,
3166 TypeManager.int32_type,
3167 TypeManager.uint32_type,
3168 TypeManager.int64_type,
3169 TypeManager.uint64_type,
3170 TypeManager.char_type,
3171 TypeManager.string_type
3176 // Try to find a *user* defined implicit conversion.
3178 // If there is no implicit conversion, or if there are multiple
3179 // conversions, we have to report an error
3181 Expression converted = null;
3182 foreach (Type tt in allowed_types){
3185 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3190 // Ignore over-worked ImplicitUserConversions that do
3191 // an implicit conversion in addition to the user conversion.
3193 if (!(e is UserCast))
3196 if (converted != null){
3197 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3207 // Performs the basic sanity checks on the switch statement
3208 // (looks for duplicate keys and non-constant expressions).
3210 // It also returns a hashtable with the keys that we will later
3211 // use to compute the switch tables
3213 bool CheckSwitch (EmitContext ec)
3216 Elements = Sections.Count > 10 ?
3217 (IDictionary)new Hashtable () :
3218 (IDictionary)new ListDictionary ();
3220 foreach (SwitchSection ss in Sections){
3221 foreach (SwitchLabel sl in ss.Labels){
3222 if (sl.Label == null){
3223 if (default_section != null){
3224 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3227 default_section = ss;
3231 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3236 object key = sl.Converted;
3237 if (key == SwitchLabel.NullStringCase)
3238 has_null_case = true;
3241 Elements.Add (key, sl);
3242 } catch (ArgumentException) {
3243 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3251 void EmitObjectInteger (ILGenerator ig, object k)
3254 IntConstant.EmitInt (ig, (int) k);
3255 else if (k is Constant) {
3256 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3259 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3262 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3264 IntConstant.EmitInt (ig, (int) (long) k);
3265 ig.Emit (OpCodes.Conv_I8);
3268 LongConstant.EmitLong (ig, (long) k);
3270 else if (k is ulong)
3272 ulong ul = (ulong) k;
3275 IntConstant.EmitInt (ig, unchecked ((int) ul));
3276 ig.Emit (OpCodes.Conv_U8);
3280 LongConstant.EmitLong (ig, unchecked ((long) ul));
3284 IntConstant.EmitInt (ig, (int) ((char) k));
3285 else if (k is sbyte)
3286 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3288 IntConstant.EmitInt (ig, (int) ((byte) k));
3289 else if (k is short)
3290 IntConstant.EmitInt (ig, (int) ((short) k));
3291 else if (k is ushort)
3292 IntConstant.EmitInt (ig, (int) ((ushort) k));
3294 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3296 throw new Exception ("Unhandled case");
3299 // structure used to hold blocks of keys while calculating table switch
3300 class KeyBlock : IComparable
3302 public KeyBlock (long _first)
3304 first = last = _first;
3308 public ArrayList element_keys = null;
3309 // how many items are in the bucket
3310 public int Size = 1;
3313 get { return (int) (last - first + 1); }
3315 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3317 return kb_last.last - kb_first.first + 1;
3319 public int CompareTo (object obj)
3321 KeyBlock kb = (KeyBlock) obj;
3322 int nLength = Length;
3323 int nLengthOther = kb.Length;
3324 if (nLengthOther == nLength)
3325 return (int) (kb.first - first);
3326 return nLength - nLengthOther;
3331 /// This method emits code for a lookup-based switch statement (non-string)
3332 /// Basically it groups the cases into blocks that are at least half full,
3333 /// and then spits out individual lookup opcodes for each block.
3334 /// It emits the longest blocks first, and short blocks are just
3335 /// handled with direct compares.
3337 /// <param name="ec"></param>
3338 /// <param name="val"></param>
3339 /// <returns></returns>
3340 void TableSwitchEmit (EmitContext ec, Expression val)
3342 int element_count = Elements.Count;
3343 object [] element_keys = new object [element_count];
3344 Elements.Keys.CopyTo (element_keys, 0);
3345 Array.Sort (element_keys);
3347 // initialize the block list with one element per key
3348 ArrayList key_blocks = new ArrayList (element_count);
3349 foreach (object key in element_keys)
3350 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3352 KeyBlock current_kb;
3353 // iteratively merge the blocks while they are at least half full
3354 // there's probably a really cool way to do this with a tree...
3355 while (key_blocks.Count > 1)
3357 ArrayList key_blocks_new = new ArrayList ();
3358 current_kb = (KeyBlock) key_blocks [0];
3359 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3361 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3362 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3365 current_kb.last = kb.last;
3366 current_kb.Size += kb.Size;
3370 // start a new block
3371 key_blocks_new.Add (current_kb);
3375 key_blocks_new.Add (current_kb);
3376 if (key_blocks.Count == key_blocks_new.Count)
3378 key_blocks = key_blocks_new;
3381 // initialize the key lists
3382 foreach (KeyBlock kb in key_blocks)
3383 kb.element_keys = new ArrayList ();
3385 // fill the key lists
3387 if (key_blocks.Count > 0) {
3388 current_kb = (KeyBlock) key_blocks [0];
3389 foreach (object key in element_keys)
3391 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3392 System.Convert.ToInt64 (key) > current_kb.last;
3394 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3395 current_kb.element_keys.Add (key);
3399 // sort the blocks so we can tackle the largest ones first
3402 // okay now we can start...
3403 ILGenerator ig = ec.ig;
3404 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3405 Label lbl_default = default_target;
3407 Type type_keys = null;
3408 if (element_keys.Length > 0)
3409 type_keys = element_keys [0].GetType (); // used for conversions
3413 if (TypeManager.IsEnumType (SwitchType))
3414 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3416 compare_type = SwitchType;
3418 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3420 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3421 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3424 foreach (object key in kb.element_keys) {
3425 SwitchLabel sl = (SwitchLabel) Elements [key];
3426 if (key is int && (int) key == 0) {
3427 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3430 EmitObjectInteger (ig, key);
3431 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3437 // TODO: if all the keys in the block are the same and there are
3438 // no gaps/defaults then just use a range-check.
3439 if (compare_type == TypeManager.int64_type ||
3440 compare_type == TypeManager.uint64_type)
3442 // TODO: optimize constant/I4 cases
3444 // check block range (could be > 2^31)
3446 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3447 ig.Emit (OpCodes.Blt, lbl_default);
3449 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3450 ig.Emit (OpCodes.Bgt, lbl_default);
3456 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3457 ig.Emit (OpCodes.Sub);
3459 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3465 int first = (int) kb.first;
3468 IntConstant.EmitInt (ig, first);
3469 ig.Emit (OpCodes.Sub);
3473 IntConstant.EmitInt (ig, -first);
3474 ig.Emit (OpCodes.Add);
3478 // first, build the list of labels for the switch
3480 int cJumps = kb.Length;
3481 Label [] switch_labels = new Label [cJumps];
3482 for (int iJump = 0; iJump < cJumps; iJump++)
3484 object key = kb.element_keys [iKey];
3485 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3487 SwitchLabel sl = (SwitchLabel) Elements [key];
3488 switch_labels [iJump] = sl.GetILLabel (ec);
3492 switch_labels [iJump] = lbl_default;
3494 // emit the switch opcode
3495 ig.Emit (OpCodes.Switch, switch_labels);
3498 // mark the default for this block
3500 ig.MarkLabel (lbl_default);
3503 // TODO: find the default case and emit it here,
3504 // to prevent having to do the following jump.
3505 // make sure to mark other labels in the default section
3507 // the last default just goes to the end
3508 if (element_keys.Length > 0)
3509 ig.Emit (OpCodes.Br, lbl_default);
3511 // now emit the code for the sections
3512 bool found_default = false;
3514 foreach (SwitchSection ss in Sections) {
3515 foreach (SwitchLabel sl in ss.Labels) {
3516 if (sl.Converted == SwitchLabel.NullStringCase) {
3517 ig.MarkLabel (null_target);
3518 } else if (sl.Label == null) {
3519 ig.MarkLabel (lbl_default);
3520 found_default = true;
3522 ig.MarkLabel (null_target);
3524 ig.MarkLabel (sl.GetILLabel (ec));
3525 ig.MarkLabel (sl.GetILLabelCode (ec));
3530 if (!found_default) {
3531 ig.MarkLabel (lbl_default);
3532 if (!has_null_case) {
3533 ig.MarkLabel (null_target);
3537 ig.MarkLabel (lbl_end);
3540 SwitchSection FindSection (SwitchLabel label)
3542 foreach (SwitchSection ss in Sections){
3543 foreach (SwitchLabel sl in ss.Labels){
3552 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3554 foreach (SwitchSection ss in Sections)
3555 ss.Block.MutateHoistedGenericType (storey);
3558 public static void Reset ()
3563 public override bool Resolve (EmitContext ec)
3565 Expr = Expr.Resolve (ec);
3569 new_expr = SwitchGoverningType (ec, Expr);
3572 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3573 unwrap = Nullable.Unwrap.Create (Expr, ec);
3577 new_expr = SwitchGoverningType (ec, unwrap);
3581 if (new_expr == null){
3582 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3587 SwitchType = new_expr.Type;
3589 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3590 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3594 if (!CheckSwitch (ec))
3598 Elements.Remove (SwitchLabel.NullStringCase);
3600 Switch old_switch = ec.Switch;
3602 ec.Switch.SwitchType = SwitchType;
3604 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3605 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3607 is_constant = new_expr is Constant;
3609 object key = ((Constant) new_expr).GetValue ();
3610 SwitchLabel label = (SwitchLabel) Elements [key];
3612 constant_section = FindSection (label);
3613 if (constant_section == null)
3614 constant_section = default_section;
3619 foreach (SwitchSection ss in Sections){
3621 ec.CurrentBranching.CreateSibling (
3622 null, FlowBranching.SiblingType.SwitchSection);
3626 if (is_constant && (ss != constant_section)) {
3627 // If we're a constant switch, we're only emitting
3628 // one single section - mark all the others as
3630 ec.CurrentBranching.CurrentUsageVector.Goto ();
3631 if (!ss.Block.ResolveUnreachable (ec, true)) {
3635 if (!ss.Block.Resolve (ec))
3640 if (default_section == null)
3641 ec.CurrentBranching.CreateSibling (
3642 null, FlowBranching.SiblingType.SwitchSection);
3644 ec.EndFlowBranching ();
3645 ec.Switch = old_switch;
3647 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3652 if (SwitchType == TypeManager.string_type && !is_constant) {
3653 // TODO: Optimize single case, and single+default case
3654 ResolveStringSwitchMap (ec);
3660 void ResolveStringSwitchMap (EmitContext ec)
3662 FullNamedExpression string_dictionary_type;
3664 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3665 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3667 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3669 new TypeExpression (TypeManager.string_type, loc),
3670 new TypeExpression (TypeManager.int32_type, loc)), loc);
3672 MemberAccess system_collections_generic = new MemberAccess (
3673 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3675 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3677 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3678 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3679 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3680 if (!field.Define ())
3682 ec.TypeContainer.PartialContainer.AddField (field);
3684 ArrayList init = new ArrayList ();
3687 string value = null;
3688 foreach (SwitchSection section in Sections) {
3689 foreach (SwitchLabel sl in section.Labels) {
3690 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3695 value = (string) sl.Converted;
3696 ArrayList init_args = new ArrayList (2);
3697 init_args.Add (new StringLiteral (value, sl.Location));
3698 init_args.Add (new IntConstant (counter, loc));
3699 init.Add (new CollectionElementInitializer (init_args, loc));
3705 Elements.Add (counter, section.Labels [0]);
3709 ArrayList args = new ArrayList (1);
3710 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3711 Expression initializer = new NewInitialize (string_dictionary_type, args,
3712 new CollectionOrObjectInitializers (init, loc), loc);
3714 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3715 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3718 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3720 ILGenerator ig = ec.ig;
3721 Label l_initialized = ig.DefineLabel ();
3724 // Skip initialization when value is null
3726 value.EmitBranchable (ec, null_target, false);
3729 // Check if string dictionary is initialized and initialize
3731 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3732 string_dictionary.EmitStatement (ec);
3733 ig.MarkLabel (l_initialized);
3735 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3738 ArrayList get_value_args = new ArrayList (2);
3739 get_value_args.Add (new Argument (value));
3740 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3741 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3742 if (get_item == null)
3746 // A value was not found, go to default case
3748 get_item.EmitBranchable (ec, default_target, false);
3750 ArrayList get_value_args = new ArrayList (1);
3751 get_value_args.Add (value);
3753 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3754 if (get_item == null)
3757 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3758 get_item_object.EmitAssign (ec, get_item, true, false);
3759 ec.ig.Emit (OpCodes.Brfalse, default_target);
3761 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3762 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3764 get_item_int.EmitStatement (ec);
3765 get_item_object.Release (ec);
3767 TableSwitchEmit (ec, string_switch_variable);
3768 string_switch_variable.Release (ec);
3771 protected override void DoEmit (EmitContext ec)
3773 ILGenerator ig = ec.ig;
3775 default_target = ig.DefineLabel ();
3776 null_target = ig.DefineLabel ();
3778 // Store variable for comparission purposes
3779 // TODO: Don't duplicate non-captured VariableReference
3780 LocalTemporary value;
3782 value = new LocalTemporary (SwitchType);
3784 unwrap.EmitCheck (ec);
3785 ig.Emit (OpCodes.Brfalse, null_target);
3789 } else if (!is_constant) {
3790 value = new LocalTemporary (SwitchType);
3797 // Setup the codegen context
3799 Label old_end = ec.LoopEnd;
3800 Switch old_switch = ec.Switch;
3802 ec.LoopEnd = ig.DefineLabel ();
3807 if (constant_section != null)
3808 constant_section.Block.Emit (ec);
3809 } else if (string_dictionary != null) {
3810 DoEmitStringSwitch (value, ec);
3812 TableSwitchEmit (ec, value);
3818 // Restore context state.
3819 ig.MarkLabel (ec.LoopEnd);
3822 // Restore the previous context
3824 ec.LoopEnd = old_end;
3825 ec.Switch = old_switch;
3828 protected override void CloneTo (CloneContext clonectx, Statement t)
3830 Switch target = (Switch) t;
3832 target.Expr = Expr.Clone (clonectx);
3833 target.Sections = new ArrayList ();
3834 foreach (SwitchSection ss in Sections){
3835 target.Sections.Add (ss.Clone (clonectx));
3840 // A place where execution can restart in an iterator
3841 public abstract class ResumableStatement : Statement
3844 protected Label resume_point;
3846 public Label PrepareForEmit (EmitContext ec)
3850 resume_point = ec.ig.DefineLabel ();
3852 return resume_point;
3855 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3859 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3864 // Base class for statements that are implemented in terms of try...finally
3865 public abstract class ExceptionStatement : ResumableStatement
3869 protected abstract void EmitPreTryBody (EmitContext ec);
3870 protected abstract void EmitTryBody (EmitContext ec);
3871 protected abstract void EmitFinallyBody (EmitContext ec);
3873 protected sealed override void DoEmit (EmitContext ec)
3875 ILGenerator ig = ec.ig;
3877 EmitPreTryBody (ec);
3879 if (resume_points != null) {
3880 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3881 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3884 ig.BeginExceptionBlock ();
3886 if (resume_points != null) {
3887 ig.MarkLabel (resume_point);
3889 // For normal control flow, we want to fall-through the Switch
3890 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3891 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3892 IntConstant.EmitInt (ig, first_resume_pc);
3893 ig.Emit (OpCodes.Sub);
3895 Label [] labels = new Label [resume_points.Count];
3896 for (int i = 0; i < resume_points.Count; ++i)
3897 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3898 ig.Emit (OpCodes.Switch, labels);
3903 ig.BeginFinallyBlock ();
3905 Label start_finally = ec.ig.DefineLabel ();
3906 if (resume_points != null) {
3907 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3908 ig.Emit (OpCodes.Brfalse_S, start_finally);
3909 ig.Emit (OpCodes.Endfinally);
3912 ig.MarkLabel (start_finally);
3913 EmitFinallyBody (ec);
3915 ig.EndExceptionBlock ();
3918 public void SomeCodeFollows ()
3920 code_follows = true;
3923 protected void ResolveReachability (EmitContext ec)
3925 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3926 // So, ensure there's some IL code after this statement.
3927 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3928 ec.NeedReturnLabel ();
3932 ArrayList resume_points;
3933 int first_resume_pc;
3934 public void AddResumePoint (ResumableStatement stmt, int pc)
3936 if (resume_points == null) {
3937 resume_points = new ArrayList ();
3938 first_resume_pc = pc;
3941 if (pc != first_resume_pc + resume_points.Count)
3942 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3944 resume_points.Add (stmt);
3947 Label dispose_try_block;
3948 bool prepared_for_dispose, emitted_dispose;
3949 public override Label PrepareForDispose (EmitContext ec, Label end)
3951 if (!prepared_for_dispose) {
3952 prepared_for_dispose = true;
3953 dispose_try_block = ec.ig.DefineLabel ();
3955 return dispose_try_block;
3958 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3960 if (emitted_dispose)
3963 emitted_dispose = true;
3965 ILGenerator ig = ec.ig;
3967 Label end_of_try = ig.DefineLabel ();
3969 // Ensure that the only way we can get into this code is through a dispatcher
3970 if (have_dispatcher)
3971 ig.Emit (OpCodes.Br, end);
3973 ig.BeginExceptionBlock ();
3975 ig.MarkLabel (dispose_try_block);
3977 Label [] labels = null;
3978 for (int i = 0; i < resume_points.Count; ++i) {
3979 ResumableStatement s = (ResumableStatement) resume_points [i];
3980 Label ret = s.PrepareForDispose (ec, end_of_try);
3981 if (ret.Equals (end_of_try) && labels == null)
3983 if (labels == null) {
3984 labels = new Label [resume_points.Count];
3985 for (int j = 0; j < i; ++j)
3986 labels [j] = end_of_try;
3991 if (labels != null) {
3993 for (j = 1; j < labels.Length; ++j)
3994 if (!labels [0].Equals (labels [j]))
3996 bool emit_dispatcher = j < labels.Length;
3998 if (emit_dispatcher) {
3999 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4000 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4001 IntConstant.EmitInt (ig, first_resume_pc);
4002 ig.Emit (OpCodes.Sub);
4003 ig.Emit (OpCodes.Switch, labels);
4004 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4007 foreach (ResumableStatement s in resume_points)
4008 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4011 ig.MarkLabel (end_of_try);
4013 ig.BeginFinallyBlock ();
4015 EmitFinallyBody (ec);
4017 ig.EndExceptionBlock ();
4021 public class Lock : ExceptionStatement {
4023 public Statement Statement;
4024 TemporaryVariable temp;
4026 public Lock (Expression expr, Statement stmt, Location l)
4033 public override bool Resolve (EmitContext ec)
4035 expr = expr.Resolve (ec);
4039 if (expr.Type.IsValueType){
4040 Report.Error (185, loc,
4041 "`{0}' is not a reference type as required by the lock statement",
4042 TypeManager.CSharpName (expr.Type));
4046 ec.StartFlowBranching (this);
4047 bool ok = Statement.Resolve (ec);
4048 ec.EndFlowBranching ();
4050 ResolveReachability (ec);
4052 // Avoid creating libraries that reference the internal
4055 if (t == TypeManager.null_type)
4056 t = TypeManager.object_type;
4058 temp = new TemporaryVariable (t, loc);
4061 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4062 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4063 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4064 monitor_type, "Enter", loc, TypeManager.object_type);
4065 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4066 monitor_type, "Exit", loc, TypeManager.object_type);
4072 protected override void EmitPreTryBody (EmitContext ec)
4074 ILGenerator ig = ec.ig;
4076 temp.EmitAssign (ec, expr);
4078 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4081 protected override void EmitTryBody (EmitContext ec)
4083 Statement.Emit (ec);
4086 protected override void EmitFinallyBody (EmitContext ec)
4089 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4092 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4094 expr.MutateHoistedGenericType (storey);
4095 temp.MutateHoistedGenericType (storey);
4096 Statement.MutateHoistedGenericType (storey);
4099 protected override void CloneTo (CloneContext clonectx, Statement t)
4101 Lock target = (Lock) t;
4103 target.expr = expr.Clone (clonectx);
4104 target.Statement = Statement.Clone (clonectx);
4108 public class Unchecked : Statement {
4111 public Unchecked (Block b)
4117 public override bool Resolve (EmitContext ec)
4119 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4120 return Block.Resolve (ec);
4123 protected override void DoEmit (EmitContext ec)
4125 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4129 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4131 Block.MutateHoistedGenericType (storey);
4134 protected override void CloneTo (CloneContext clonectx, Statement t)
4136 Unchecked target = (Unchecked) t;
4138 target.Block = clonectx.LookupBlock (Block);
4142 public class Checked : Statement {
4145 public Checked (Block b)
4148 b.Unchecked = false;
4151 public override bool Resolve (EmitContext ec)
4153 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4154 return Block.Resolve (ec);
4157 protected override void DoEmit (EmitContext ec)
4159 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4163 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4165 Block.MutateHoistedGenericType (storey);
4168 protected override void CloneTo (CloneContext clonectx, Statement t)
4170 Checked target = (Checked) t;
4172 target.Block = clonectx.LookupBlock (Block);
4176 public class Unsafe : Statement {
4179 public Unsafe (Block b)
4182 Block.Unsafe = true;
4185 public override bool Resolve (EmitContext ec)
4187 using (ec.With (EmitContext.Flags.InUnsafe, true))
4188 return Block.Resolve (ec);
4191 protected override void DoEmit (EmitContext ec)
4193 using (ec.With (EmitContext.Flags.InUnsafe, true))
4197 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4199 Block.MutateHoistedGenericType (storey);
4202 protected override void CloneTo (CloneContext clonectx, Statement t)
4204 Unsafe target = (Unsafe) t;
4206 target.Block = clonectx.LookupBlock (Block);
4213 public class Fixed : Statement {
4215 ArrayList declarators;
4216 Statement statement;
4221 abstract class Emitter
4223 protected LocalInfo vi;
4224 protected Expression converted;
4226 protected Emitter (Expression expr, LocalInfo li)
4232 public abstract void Emit (EmitContext ec);
4233 public abstract void EmitExit (EmitContext ec);
4236 class ExpressionEmitter : Emitter {
4237 public ExpressionEmitter (Expression converted, LocalInfo li) :
4238 base (converted, li)
4242 public override void Emit (EmitContext ec) {
4244 // Store pointer in pinned location
4246 converted.Emit (ec);
4250 public override void EmitExit (EmitContext ec)
4252 ec.ig.Emit (OpCodes.Ldc_I4_0);
4253 ec.ig.Emit (OpCodes.Conv_U);
4258 class StringEmitter : Emitter {
4259 class StringPtr : Expression
4263 public StringPtr (LocalBuilder b, Location l)
4266 eclass = ExprClass.Value;
4267 type = TypeManager.char_ptr_type;
4271 public override Expression CreateExpressionTree (EmitContext ec)
4273 throw new NotSupportedException ("ET");
4276 public override Expression DoResolve (EmitContext ec)
4278 // This should never be invoked, we are born in fully
4279 // initialized state.
4284 public override void Emit (EmitContext ec)
4286 if (TypeManager.int_get_offset_to_string_data == null) {
4287 // TODO: Move to resolve !!
4288 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4289 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4292 ILGenerator ig = ec.ig;
4294 ig.Emit (OpCodes.Ldloc, b);
4295 ig.Emit (OpCodes.Conv_I);
4296 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4297 ig.Emit (OpCodes.Add);
4301 LocalBuilder pinned_string;
4304 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4310 public override void Emit (EmitContext ec)
4312 ILGenerator ig = ec.ig;
4313 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4315 converted.Emit (ec);
4316 ig.Emit (OpCodes.Stloc, pinned_string);
4318 Expression sptr = new StringPtr (pinned_string, loc);
4319 converted = Convert.ImplicitConversionRequired (
4320 ec, sptr, vi.VariableType, loc);
4322 if (converted == null)
4325 converted.Emit (ec);
4329 public override void EmitExit (EmitContext ec)
4331 ec.ig.Emit (OpCodes.Ldnull);
4332 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4336 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4339 declarators = decls;
4344 public Statement Statement {
4345 get { return statement; }
4348 public override bool Resolve (EmitContext ec)
4351 Expression.UnsafeError (loc);
4355 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4356 if (texpr == null) {
4357 if (type is VarExpr)
4358 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4363 expr_type = texpr.Type;
4365 data = new Emitter [declarators.Count];
4367 if (!expr_type.IsPointer){
4368 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4373 foreach (Pair p in declarators){
4374 LocalInfo vi = (LocalInfo) p.First;
4375 Expression e = (Expression) p.Second;
4377 vi.VariableInfo.SetAssigned (ec);
4378 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4381 // The rules for the possible declarators are pretty wise,
4382 // but the production on the grammar is more concise.
4384 // So we have to enforce these rules here.
4386 // We do not resolve before doing the case 1 test,
4387 // because the grammar is explicit in that the token &
4388 // is present, so we need to test for this particular case.
4392 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4396 ec.InFixedInitializer = true;
4398 ec.InFixedInitializer = false;
4405 if (e.Type.IsArray){
4406 Type array_type = TypeManager.GetElementType (e.Type);
4409 // Provided that array_type is unmanaged,
4411 if (!TypeManager.VerifyUnManaged (array_type, loc))
4415 // and T* is implicitly convertible to the
4416 // pointer type given in the fixed statement.
4418 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4420 Expression converted = Convert.ImplicitConversionRequired (
4421 ec, array_ptr, vi.VariableType, loc);
4422 if (converted == null)
4426 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4428 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4429 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4430 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4431 new NullPointer (loc),
4434 converted = converted.Resolve (ec);
4436 data [i] = new ExpressionEmitter (converted, vi);
4445 if (e.Type == TypeManager.string_type){
4446 data [i] = new StringEmitter (e, vi, loc);
4451 // Case 4: fixed buffer
4452 if (e is FixedBufferPtr) {
4453 data [i++] = new ExpressionEmitter (e, vi);
4458 // Case 1: & object.
4460 Unary u = e as Unary;
4461 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4462 IVariableReference vr = u.Expr as IVariableReference;
4463 if (vr == null || !vr.IsFixed) {
4464 data [i] = new ExpressionEmitter (e, vi);
4468 if (data [i++] == null)
4469 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4471 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4474 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4475 bool ok = statement.Resolve (ec);
4476 bool flow_unreachable = ec.EndFlowBranching ();
4477 has_ret = flow_unreachable;
4482 protected override void DoEmit (EmitContext ec)
4484 for (int i = 0; i < data.Length; i++) {
4488 statement.Emit (ec);
4494 // Clear the pinned variable
4496 for (int i = 0; i < data.Length; i++) {
4497 data [i].EmitExit (ec);
4501 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4503 // Fixed statement cannot be used inside anonymous methods or lambdas
4504 throw new NotSupportedException ();
4507 protected override void CloneTo (CloneContext clonectx, Statement t)
4509 Fixed target = (Fixed) t;
4511 target.type = type.Clone (clonectx);
4512 target.declarators = new ArrayList (declarators.Count);
4513 foreach (Pair p in declarators) {
4514 LocalInfo vi = (LocalInfo) p.First;
4515 Expression e = (Expression) p.Second;
4517 target.declarators.Add (
4518 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4521 target.statement = statement.Clone (clonectx);
4525 public class Catch : Statement {
4526 public readonly string Name;
4528 public Block VarBlock;
4530 Expression type_expr;
4533 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4538 VarBlock = var_block;
4542 public Type CatchType {
4548 public bool IsGeneral {
4550 return type_expr == null;
4554 protected override void DoEmit (EmitContext ec)
4556 ILGenerator ig = ec.ig;
4558 if (CatchType != null)
4559 ig.BeginCatchBlock (CatchType);
4561 ig.BeginCatchBlock (TypeManager.object_type);
4563 if (VarBlock != null)
4567 // TODO: Move to resolve
4568 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4572 if (lvr.IsHoisted) {
4573 LocalTemporary lt = new LocalTemporary (lvr.Type);
4577 // Variable is at the top of the stack
4578 source = EmptyExpression.Null;
4581 lvr.EmitAssign (ec, source, false, false);
4583 ig.Emit (OpCodes.Pop);
4588 public override bool Resolve (EmitContext ec)
4590 using (ec.With (EmitContext.Flags.InCatch, true)) {
4591 if (type_expr != null) {
4592 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4598 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4599 Error (155, "The type caught or thrown must be derived from System.Exception");
4605 if (!Block.Resolve (ec))
4608 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4609 // emit the "unused variable" warnings.
4610 if (VarBlock != null)
4611 return VarBlock.Resolve (ec);
4617 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4620 type = storey.MutateType (type);
4621 if (VarBlock != null)
4622 VarBlock.MutateHoistedGenericType (storey);
4623 Block.MutateHoistedGenericType (storey);
4626 protected override void CloneTo (CloneContext clonectx, Statement t)
4628 Catch target = (Catch) t;
4630 if (type_expr != null)
4631 target.type_expr = type_expr.Clone (clonectx);
4632 if (VarBlock != null)
4633 target.VarBlock = clonectx.LookupBlock (VarBlock);
4634 target.Block = clonectx.LookupBlock (Block);
4638 public class TryFinally : ExceptionStatement {
4642 public TryFinally (Statement stmt, Block fini, Location l)
4649 public override bool Resolve (EmitContext ec)
4653 ec.StartFlowBranching (this);
4655 if (!stmt.Resolve (ec))
4659 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4660 using (ec.With (EmitContext.Flags.InFinally, true)) {
4661 if (!fini.Resolve (ec))
4665 ec.EndFlowBranching ();
4667 ResolveReachability (ec);
4672 protected override void EmitPreTryBody (EmitContext ec)
4676 protected override void EmitTryBody (EmitContext ec)
4681 protected override void EmitFinallyBody (EmitContext ec)
4686 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4688 stmt.MutateHoistedGenericType (storey);
4689 fini.MutateHoistedGenericType (storey);
4692 protected override void CloneTo (CloneContext clonectx, Statement t)
4694 TryFinally target = (TryFinally) t;
4696 target.stmt = (Statement) stmt.Clone (clonectx);
4698 target.fini = clonectx.LookupBlock (fini);
4702 public class TryCatch : Statement {
4704 public ArrayList Specific;
4705 public Catch General;
4706 bool inside_try_finally, code_follows;
4708 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4711 this.Specific = catch_clauses;
4712 this.General = null;
4713 this.inside_try_finally = inside_try_finally;
4715 for (int i = 0; i < catch_clauses.Count; ++i) {
4716 Catch c = (Catch) catch_clauses [i];
4718 if (i != catch_clauses.Count - 1)
4719 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4721 catch_clauses.RemoveAt (i);
4729 public override bool Resolve (EmitContext ec)
4733 ec.StartFlowBranching (this);
4735 if (!Block.Resolve (ec))
4738 Type[] prev_catches = new Type [Specific.Count];
4740 foreach (Catch c in Specific){
4741 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4743 if (c.Name != null) {
4744 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4746 throw new Exception ();
4748 vi.VariableInfo = null;
4751 if (!c.Resolve (ec))
4754 Type resolved_type = c.CatchType;
4755 for (int ii = 0; ii < last_index; ++ii) {
4756 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4757 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4762 prev_catches [last_index++] = resolved_type;
4765 if (General != null) {
4766 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4767 foreach (Catch c in Specific){
4768 if (c.CatchType == TypeManager.exception_type) {
4769 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'");
4774 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4776 if (!General.Resolve (ec))
4780 ec.EndFlowBranching ();
4782 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4783 // So, ensure there's some IL code after this statement
4784 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4785 ec.NeedReturnLabel ();
4790 public void SomeCodeFollows ()
4792 code_follows = true;
4795 protected override void DoEmit (EmitContext ec)
4797 ILGenerator ig = ec.ig;
4799 if (!inside_try_finally)
4800 ig.BeginExceptionBlock ();
4804 foreach (Catch c in Specific)
4807 if (General != null)
4810 if (!inside_try_finally)
4811 ig.EndExceptionBlock ();
4814 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4816 Block.MutateHoistedGenericType (storey);
4818 if (General != null)
4819 General.MutateHoistedGenericType (storey);
4820 if (Specific != null) {
4821 foreach (Catch c in Specific)
4822 c.MutateHoistedGenericType (storey);
4826 protected override void CloneTo (CloneContext clonectx, Statement t)
4828 TryCatch target = (TryCatch) t;
4830 target.Block = clonectx.LookupBlock (Block);
4831 if (General != null)
4832 target.General = (Catch) General.Clone (clonectx);
4833 if (Specific != null){
4834 target.Specific = new ArrayList ();
4835 foreach (Catch c in Specific)
4836 target.Specific.Add (c.Clone (clonectx));
4841 public class UsingTemporary : ExceptionStatement {
4842 TemporaryVariable local_copy;
4843 public Statement Statement;
4847 public UsingTemporary (Expression expr, Statement stmt, Location l)
4854 public override bool Resolve (EmitContext ec)
4856 expr = expr.Resolve (ec);
4860 expr_type = expr.Type;
4862 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4863 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4864 Using.Error_IsNotConvertibleToIDisposable (expr);
4869 local_copy = new TemporaryVariable (expr_type, loc);
4870 local_copy.Resolve (ec);
4872 ec.StartFlowBranching (this);
4874 bool ok = Statement.Resolve (ec);
4876 ec.EndFlowBranching ();
4878 ResolveReachability (ec);
4880 if (TypeManager.void_dispose_void == null) {
4881 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4882 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4888 protected override void EmitPreTryBody (EmitContext ec)
4890 local_copy.EmitAssign (ec, expr);
4893 protected override void EmitTryBody (EmitContext ec)
4895 Statement.Emit (ec);
4898 protected override void EmitFinallyBody (EmitContext ec)
4900 ILGenerator ig = ec.ig;
4901 if (!expr_type.IsValueType) {
4902 Label skip = ig.DefineLabel ();
4903 local_copy.Emit (ec);
4904 ig.Emit (OpCodes.Brfalse, skip);
4905 local_copy.Emit (ec);
4906 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4907 ig.MarkLabel (skip);
4911 Expression ml = Expression.MemberLookup (
4912 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4913 "Dispose", Location.Null);
4915 if (!(ml is MethodGroupExpr)) {
4916 local_copy.Emit (ec);
4917 ig.Emit (OpCodes.Box, expr_type);
4918 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4922 MethodInfo mi = null;
4924 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4925 if (TypeManager.GetParameterData (mk).Count == 0) {
4932 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4936 local_copy.AddressOf (ec, AddressOp.Load);
4937 ig.Emit (OpCodes.Call, mi);
4940 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4942 expr_type = storey.MutateType (expr_type);
4943 local_copy.MutateHoistedGenericType (storey);
4944 Statement.MutateHoistedGenericType (storey);
4947 protected override void CloneTo (CloneContext clonectx, Statement t)
4949 UsingTemporary target = (UsingTemporary) t;
4951 target.expr = expr.Clone (clonectx);
4952 target.Statement = Statement.Clone (clonectx);
4956 public class Using : ExceptionStatement {
4958 public Statement EmbeddedStatement {
4959 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4965 Expression converted_var;
4966 ExpressionStatement assign;
4968 public Using (Expression var, Expression init, Statement stmt, Location l)
4976 bool ResolveVariable (EmitContext ec)
4978 ExpressionStatement a = new SimpleAssign (var, init, loc);
4979 a = a.ResolveStatement (ec);
4985 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4986 converted_var = var;
4990 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4992 Error_IsNotConvertibleToIDisposable (var);
5001 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5003 Report.SymbolRelatedToPreviousError (expr.Type);
5004 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5005 expr.GetSignatureForError ());
5008 protected override void EmitPreTryBody (EmitContext ec)
5010 assign.EmitStatement (ec);
5013 protected override void EmitTryBody (EmitContext ec)
5018 protected override void EmitFinallyBody (EmitContext ec)
5020 ILGenerator ig = ec.ig;
5022 if (!var.Type.IsValueType) {
5023 Label skip = ig.DefineLabel ();
5025 ig.Emit (OpCodes.Brfalse, skip);
5026 converted_var.Emit (ec);
5027 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5028 ig.MarkLabel (skip);
5030 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5032 if (!(ml is MethodGroupExpr)) {
5034 ig.Emit (OpCodes.Box, var.Type);
5035 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5037 MethodInfo mi = null;
5039 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5040 if (TypeManager.GetParameterData (mk).Count == 0) {
5047 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5051 IMemoryLocation mloc = (IMemoryLocation) var;
5053 mloc.AddressOf (ec, AddressOp.Load);
5054 ig.Emit (OpCodes.Call, mi);
5059 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5061 assign.MutateHoistedGenericType (storey);
5062 var.MutateHoistedGenericType (storey);
5063 stmt.MutateHoistedGenericType (storey);
5066 public override bool Resolve (EmitContext ec)
5068 if (!ResolveVariable (ec))
5071 ec.StartFlowBranching (this);
5073 bool ok = stmt.Resolve (ec);
5075 ec.EndFlowBranching ();
5077 ResolveReachability (ec);
5079 if (TypeManager.void_dispose_void == null) {
5080 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5081 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5087 protected override void CloneTo (CloneContext clonectx, Statement t)
5089 Using target = (Using) t;
5091 target.var = var.Clone (clonectx);
5092 target.init = init.Clone (clonectx);
5093 target.stmt = stmt.Clone (clonectx);
5098 /// Implementation of the foreach C# statement
5100 public class Foreach : Statement {
5102 sealed class ArrayForeach : Statement
5104 class ArrayCounter : TemporaryVariable
5106 StatementExpression increment;
5108 public ArrayCounter (Location loc)
5109 : base (TypeManager.int32_type, loc)
5113 public void ResolveIncrement (EmitContext ec)
5115 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5116 increment.Resolve (ec);
5119 public void EmitIncrement (EmitContext ec)
5121 increment.Emit (ec);
5125 readonly Foreach for_each;
5126 readonly Statement statement;
5129 TemporaryVariable[] lengths;
5130 Expression [] length_exprs;
5131 ArrayCounter[] counter;
5133 TemporaryVariable copy;
5136 public ArrayForeach (Foreach @foreach, int rank)
5138 for_each = @foreach;
5139 statement = for_each.statement;
5142 counter = new ArrayCounter [rank];
5143 length_exprs = new Expression [rank];
5146 // Only use temporary length variables when dealing with
5147 // multi-dimensional arrays
5150 lengths = new TemporaryVariable [rank];
5153 protected override void CloneTo (CloneContext clonectx, Statement target)
5155 throw new NotImplementedException ();
5158 public override bool Resolve (EmitContext ec)
5160 copy = new TemporaryVariable (for_each.expr.Type, loc);
5163 int rank = length_exprs.Length;
5164 ArrayList list = new ArrayList (rank);
5165 for (int i = 0; i < rank; i++) {
5166 counter [i] = new ArrayCounter (loc);
5167 counter [i].ResolveIncrement (ec);
5170 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5172 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5173 lengths [i].Resolve (ec);
5175 ArrayList args = new ArrayList (1);
5176 args.Add (new Argument (new IntConstant (i, loc)));
5177 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5180 list.Add (counter [i]);
5183 access = new ElementAccess (copy, list).Resolve (ec);
5187 Expression var_type = for_each.type;
5188 VarExpr ve = var_type as VarExpr;
5190 // Infer implicitly typed local variable from foreach array type
5191 var_type = new TypeExpression (access.Type, ve.Location);
5194 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5195 if (var_type == null)
5198 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5204 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5205 ec.CurrentBranching.CreateSibling ();
5207 for_each.variable = for_each.variable.ResolveLValue (ec, conv, loc);
5208 if (for_each.variable == null)
5211 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5212 if (!statement.Resolve (ec))
5214 ec.EndFlowBranching ();
5216 // There's no direct control flow from the end of the embedded statement to the end of the loop
5217 ec.CurrentBranching.CurrentUsageVector.Goto ();
5219 ec.EndFlowBranching ();
5224 protected override void DoEmit (EmitContext ec)
5226 ILGenerator ig = ec.ig;
5228 copy.EmitAssign (ec, for_each.expr);
5230 int rank = length_exprs.Length;
5231 Label[] test = new Label [rank];
5232 Label[] loop = new Label [rank];
5234 for (int i = 0; i < rank; i++) {
5235 test [i] = ig.DefineLabel ();
5236 loop [i] = ig.DefineLabel ();
5238 if (lengths != null)
5239 lengths [i].EmitAssign (ec, length_exprs [i]);
5242 IntConstant zero = new IntConstant (0, loc);
5243 for (int i = 0; i < rank; i++) {
5244 counter [i].EmitAssign (ec, zero);
5246 ig.Emit (OpCodes.Br, test [i]);
5247 ig.MarkLabel (loop [i]);
5250 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5252 statement.Emit (ec);
5254 ig.MarkLabel (ec.LoopBegin);
5256 for (int i = rank - 1; i >= 0; i--){
5257 counter [i].EmitIncrement (ec);
5259 ig.MarkLabel (test [i]);
5260 counter [i].Emit (ec);
5262 if (lengths != null)
5263 lengths [i].Emit (ec);
5265 length_exprs [i].Emit (ec);
5267 ig.Emit (OpCodes.Blt, loop [i]);
5270 ig.MarkLabel (ec.LoopEnd);
5273 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5275 for_each.expr.MutateHoistedGenericType (storey);
5277 copy.MutateHoistedGenericType (storey);
5278 conv.MutateHoistedGenericType (storey);
5279 statement.MutateHoistedGenericType (storey);
5281 for (int i = 0; i < counter.Length; i++) {
5282 counter [i].MutateHoistedGenericType (storey);
5283 if (lengths != null)
5284 lengths [i].MutateHoistedGenericType (storey);
5289 sealed class CollectionForeach : Statement
5291 class CollectionForeachStatement : Statement
5294 Expression variable, current, conv;
5295 Statement statement;
5298 public CollectionForeachStatement (Type type, Expression variable,
5299 Expression current, Statement statement,
5303 this.variable = variable;
5304 this.current = current;
5305 this.statement = statement;
5309 protected override void CloneTo (CloneContext clonectx, Statement target)
5311 throw new NotImplementedException ();
5314 public override bool Resolve (EmitContext ec)
5316 current = current.Resolve (ec);
5317 if (current == null)
5320 conv = Convert.ExplicitConversion (ec, current, type, loc);
5324 assign = new SimpleAssign (variable, conv, loc);
5325 if (assign.Resolve (ec) == null)
5328 if (!statement.Resolve (ec))
5334 protected override void DoEmit (EmitContext ec)
5336 assign.EmitStatement (ec);
5337 statement.Emit (ec);
5340 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5342 assign.MutateHoistedGenericType (storey);
5343 statement.MutateHoistedGenericType (storey);
5347 Expression variable, expr;
5348 Statement statement;
5350 TemporaryVariable enumerator;
5355 MethodGroupExpr get_enumerator;
5356 PropertyExpr get_current;
5357 MethodInfo move_next;
5358 Expression var_type;
5359 Type enumerator_type;
5360 bool enumerator_found;
5362 public CollectionForeach (Expression var_type, Expression var,
5363 Expression expr, Statement stmt, Location l)
5365 this.var_type = var_type;
5366 this.variable = var;
5372 protected override void CloneTo (CloneContext clonectx, Statement target)
5374 throw new NotImplementedException ();
5377 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5379 Type return_type = mi.ReturnType;
5382 // Ok, we can access it, now make sure that we can do something
5383 // with this `GetEnumerator'
5386 if (return_type == TypeManager.ienumerator_type ||
5387 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5388 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5390 // If it is not an interface, lets try to find the methods ourselves.
5391 // For example, if we have:
5392 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5393 // We can avoid the iface call. This is a runtime perf boost.
5394 // even bigger if we have a ValueType, because we avoid the cost
5397 // We have to make sure that both methods exist for us to take
5398 // this path. If one of the methods does not exist, we will just
5399 // use the interface. Sadly, this complex if statement is the only
5400 // way I could do this without a goto
5403 if (TypeManager.bool_movenext_void == null) {
5404 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5405 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5408 if (TypeManager.ienumerator_getcurrent == null) {
5409 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5410 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5415 // Prefer a generic enumerator over a non-generic one.
5417 if (return_type.IsInterface && return_type.IsGenericType) {
5418 enumerator_type = return_type;
5419 if (!FetchGetCurrent (ec, return_type))
5420 get_current = new PropertyExpr (
5421 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5422 if (!FetchMoveNext (return_type))
5423 move_next = TypeManager.bool_movenext_void;
5428 if (return_type.IsInterface ||
5429 !FetchMoveNext (return_type) ||
5430 !FetchGetCurrent (ec, return_type)) {
5431 enumerator_type = return_type;
5432 move_next = TypeManager.bool_movenext_void;
5433 get_current = new PropertyExpr (
5434 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5439 // Ok, so they dont return an IEnumerable, we will have to
5440 // find if they support the GetEnumerator pattern.
5443 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5444 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",
5445 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5450 enumerator_type = return_type;
5456 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5458 bool FetchMoveNext (Type t)
5460 MemberList move_next_list;
5462 move_next_list = TypeContainer.FindMembers (
5463 t, MemberTypes.Method,
5464 BindingFlags.Public | BindingFlags.Instance,
5465 Type.FilterName, "MoveNext");
5466 if (move_next_list.Count == 0)
5469 foreach (MemberInfo m in move_next_list){
5470 MethodInfo mi = (MethodInfo) m;
5472 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5473 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5483 // Retrieves a `public T get_Current ()' method from the Type `t'
5485 bool FetchGetCurrent (EmitContext ec, Type t)
5487 PropertyExpr pe = Expression.MemberLookup (
5488 ec.ContainerType, t, "Current", MemberTypes.Property,
5489 Expression.AllBindingFlags, loc) as PropertyExpr;
5498 // Retrieves a `public void Dispose ()' method from the Type `t'
5500 static MethodInfo FetchMethodDispose (Type t)
5502 MemberList dispose_list;
5504 dispose_list = TypeContainer.FindMembers (
5505 t, MemberTypes.Method,
5506 BindingFlags.Public | BindingFlags.Instance,
5507 Type.FilterName, "Dispose");
5508 if (dispose_list.Count == 0)
5511 foreach (MemberInfo m in dispose_list){
5512 MethodInfo mi = (MethodInfo) m;
5514 if (TypeManager.GetParameterData (mi).Count == 0){
5515 if (mi.ReturnType == TypeManager.void_type)
5522 void Error_Enumerator ()
5524 if (enumerator_found) {
5528 Report.Error (1579, loc,
5529 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5530 TypeManager.CSharpName (expr.Type));
5533 bool IsOverride (MethodInfo m)
5535 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5537 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5539 if (m is MethodBuilder)
5542 MethodInfo base_method = m.GetBaseDefinition ();
5543 return base_method != m;
5546 bool TryType (EmitContext ec, Type t)
5548 MethodGroupExpr mg = Expression.MemberLookup (
5549 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5550 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5554 MethodInfo result = null;
5555 MethodInfo tmp_move_next = null;
5556 PropertyExpr tmp_get_cur = null;
5557 Type tmp_enumerator_type = enumerator_type;
5558 foreach (MethodInfo mi in mg.Methods) {
5559 if (TypeManager.GetParameterData (mi).Count != 0)
5562 // Check whether GetEnumerator is public
5563 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5566 if (IsOverride (mi))
5569 enumerator_found = true;
5571 if (!GetEnumeratorFilter (ec, mi))
5574 if (result != null) {
5575 if (TypeManager.IsGenericType (result.ReturnType)) {
5576 if (!TypeManager.IsGenericType (mi.ReturnType))
5579 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5580 Report.SymbolRelatedToPreviousError (t);
5581 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5582 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5583 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5587 // Always prefer generics enumerators
5588 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5589 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5590 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5593 Report.SymbolRelatedToPreviousError (result);
5594 Report.SymbolRelatedToPreviousError (mi);
5595 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5596 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5601 tmp_move_next = move_next;
5602 tmp_get_cur = get_current;
5603 tmp_enumerator_type = enumerator_type;
5604 if (mi.DeclaringType == t)
5608 if (result != null) {
5609 move_next = tmp_move_next;
5610 get_current = tmp_get_cur;
5611 enumerator_type = tmp_enumerator_type;
5612 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5613 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5615 if (t != expr.Type) {
5616 expr = Convert.ExplicitConversion (
5619 throw new InternalErrorException ();
5622 get_enumerator.InstanceExpression = expr;
5623 get_enumerator.IsBase = t != expr.Type;
5631 bool ProbeCollectionType (EmitContext ec, Type t)
5633 int errors = Report.Errors;
5634 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5635 if (TryType (ec, tt))
5640 if (Report.Errors > errors)
5644 // Now try to find the method in the interfaces
5646 Type [] ifaces = TypeManager.GetInterfaces (t);
5647 foreach (Type i in ifaces){
5648 if (TryType (ec, i))
5655 public override bool Resolve (EmitContext ec)
5657 enumerator_type = TypeManager.ienumerator_type;
5659 if (!ProbeCollectionType (ec, expr.Type)) {
5660 Error_Enumerator ();
5664 bool is_disposable = !enumerator_type.IsSealed ||
5665 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5667 VarExpr ve = var_type as VarExpr;
5669 // Infer implicitly typed local variable from foreach enumerable type
5670 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5673 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5674 if (var_type == null)
5677 enumerator = new TemporaryVariable (enumerator_type, loc);
5678 enumerator.Resolve (ec);
5680 init = new Invocation (get_enumerator, null);
5681 init = init.Resolve (ec);
5685 Expression move_next_expr;
5687 MemberInfo[] mi = new MemberInfo[] { move_next };
5688 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5689 mg.InstanceExpression = enumerator;
5691 move_next_expr = new Invocation (mg, null);
5694 get_current.InstanceExpression = enumerator;
5696 Statement block = new CollectionForeachStatement (
5697 var_type.Type, variable, get_current, statement, loc);
5699 loop = new While (move_next_expr, block, loc);
5701 wrapper = is_disposable ?
5702 (Statement) new DisposableWrapper (this) :
5703 (Statement) new NonDisposableWrapper (this);
5704 return wrapper.Resolve (ec);
5707 protected override void DoEmit (EmitContext ec)
5712 class NonDisposableWrapper : Statement {
5713 CollectionForeach parent;
5715 internal NonDisposableWrapper (CollectionForeach parent)
5717 this.parent = parent;
5720 protected override void CloneTo (CloneContext clonectx, Statement target)
5722 throw new NotSupportedException ();
5725 public override bool Resolve (EmitContext ec)
5727 return parent.ResolveLoop (ec);
5730 protected override void DoEmit (EmitContext ec)
5732 parent.EmitLoopInit (ec);
5733 parent.EmitLoopBody (ec);
5736 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5738 throw new NotSupportedException ();
5742 class DisposableWrapper : ExceptionStatement {
5743 CollectionForeach parent;
5745 internal DisposableWrapper (CollectionForeach parent)
5747 this.parent = parent;
5750 protected override void CloneTo (CloneContext clonectx, Statement target)
5752 throw new NotSupportedException ();
5755 public override bool Resolve (EmitContext ec)
5759 ec.StartFlowBranching (this);
5761 if (!parent.ResolveLoop (ec))
5764 ec.EndFlowBranching ();
5766 ResolveReachability (ec);
5768 if (TypeManager.void_dispose_void == null) {
5769 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5770 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5775 protected override void EmitPreTryBody (EmitContext ec)
5777 parent.EmitLoopInit (ec);
5780 protected override void EmitTryBody (EmitContext ec)
5782 parent.EmitLoopBody (ec);
5785 protected override void EmitFinallyBody (EmitContext ec)
5787 parent.EmitFinallyBody (ec);
5790 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5792 throw new NotSupportedException ();
5796 bool ResolveLoop (EmitContext ec)
5798 return loop.Resolve (ec);
5801 void EmitLoopInit (EmitContext ec)
5803 enumerator.EmitAssign (ec, init);
5806 void EmitLoopBody (EmitContext ec)
5811 void EmitFinallyBody (EmitContext ec)
5813 ILGenerator ig = ec.ig;
5815 if (enumerator_type.IsValueType) {
5816 MethodInfo mi = FetchMethodDispose (enumerator_type);
5818 enumerator.AddressOf (ec, AddressOp.Load);
5819 ig.Emit (OpCodes.Call, mi);
5821 enumerator.Emit (ec);
5822 ig.Emit (OpCodes.Box, enumerator_type);
5823 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5826 Label call_dispose = ig.DefineLabel ();
5828 enumerator.Emit (ec);
5829 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5830 ig.Emit (OpCodes.Dup);
5831 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5833 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5834 // (Partition III, Section 3.35)
5835 ig.Emit (OpCodes.Endfinally);
5837 ig.MarkLabel (call_dispose);
5838 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5842 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5844 enumerator_type = storey.MutateType (enumerator_type);
5845 init.MutateHoistedGenericType (storey);
5846 loop.MutateHoistedGenericType (storey);
5851 Expression variable;
5853 Statement statement;
5855 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5856 Statement stmt, Location l)
5859 this.variable = var;
5865 public Statement Statement {
5866 get { return statement; }
5869 public override bool Resolve (EmitContext ec)
5871 expr = expr.Resolve (ec);
5876 Report.Error (186, loc, "Use of null is not valid in this context");
5880 if (expr.Type == TypeManager.string_type) {
5881 statement = new ArrayForeach (this, 1);
5882 } else if (expr.Type.IsArray) {
5883 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5885 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5886 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5887 expr.ExprClassName);
5891 statement = new CollectionForeach (type, variable, expr, statement, loc);
5894 return statement.Resolve (ec);
5897 protected override void DoEmit (EmitContext ec)
5899 ILGenerator ig = ec.ig;
5901 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5902 ec.LoopBegin = ig.DefineLabel ();
5903 ec.LoopEnd = ig.DefineLabel ();
5905 statement.Emit (ec);
5907 ec.LoopBegin = old_begin;
5908 ec.LoopEnd = old_end;
5911 protected override void CloneTo (CloneContext clonectx, Statement t)
5913 Foreach target = (Foreach) t;
5915 target.type = type.Clone (clonectx);
5916 target.variable = variable.Clone (clonectx);
5917 target.expr = expr.Clone (clonectx);
5918 target.statement = statement.Clone (clonectx);
5921 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5923 statement.MutateHoistedGenericType (storey);