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);
1153 ec.CurrentBranching.CurrentUsageVector.Goto ();
1158 ExprClass eclass = expr.eclass;
1160 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1161 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1162 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1168 if ((t != TypeManager.exception_type) &&
1169 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1170 !(expr is NullLiteral)) {
1171 Error (155, "The type caught or thrown must be derived from System.Exception");
1177 protected override void DoEmit (EmitContext ec)
1180 ec.ig.Emit (OpCodes.Rethrow);
1184 ec.ig.Emit (OpCodes.Throw);
1188 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1191 expr.MutateHoistedGenericType (storey);
1194 protected override void CloneTo (CloneContext clonectx, Statement t)
1196 Throw target = (Throw) t;
1199 target.expr = expr.Clone (clonectx);
1203 public class Break : Statement {
1205 public Break (Location l)
1210 bool unwind_protect;
1212 public override bool Resolve (EmitContext ec)
1214 int errors = Report.Errors;
1215 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1216 ec.CurrentBranching.CurrentUsageVector.Goto ();
1217 return errors == Report.Errors;
1220 protected override void DoEmit (EmitContext ec)
1222 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1225 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1229 protected override void CloneTo (CloneContext clonectx, Statement t)
1235 public class Continue : Statement {
1237 public Continue (Location l)
1242 bool unwind_protect;
1244 public override bool Resolve (EmitContext ec)
1246 int errors = Report.Errors;
1247 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1248 ec.CurrentBranching.CurrentUsageVector.Goto ();
1249 return errors == Report.Errors;
1252 protected override void DoEmit (EmitContext ec)
1254 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1257 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1261 protected override void CloneTo (CloneContext clonectx, Statement t)
1267 public interface ILocalVariable
1269 void Emit (EmitContext ec);
1270 void EmitAssign (EmitContext ec);
1271 void EmitAddressOf (EmitContext ec);
1274 public interface IKnownVariable {
1275 Block Block { get; }
1276 Location Location { get; }
1280 // The information about a user-perceived local variable
1282 public class LocalInfo : IKnownVariable, ILocalVariable {
1283 public readonly Expression Type;
1285 public Type VariableType;
1286 public readonly string Name;
1287 public readonly Location Location;
1288 public readonly Block Block;
1290 public VariableInfo VariableInfo;
1291 public HoistedVariable HoistedVariableReference;
1300 CompilerGenerated = 64,
1304 public enum ReadOnlyContext: byte {
1311 ReadOnlyContext ro_context;
1312 LocalBuilder builder;
1314 public LocalInfo (Expression type, string name, Block block, Location l)
1322 public LocalInfo (DeclSpace ds, Block block, Location l)
1324 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1329 public void ResolveVariable (EmitContext ec)
1331 if (HoistedVariableReference != null)
1334 if (builder == null) {
1337 // This is needed to compile on both .NET 1.x and .NET 2.x
1338 // the later introduced `DeclareLocal (Type t, bool pinned)'
1340 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1342 builder = ec.ig.DeclareLocal (VariableType);
1346 public void Emit (EmitContext ec)
1348 ec.ig.Emit (OpCodes.Ldloc, builder);
1351 public void EmitAssign (EmitContext ec)
1353 ec.ig.Emit (OpCodes.Stloc, builder);
1356 public void EmitAddressOf (EmitContext ec)
1358 ec.ig.Emit (OpCodes.Ldloca, builder);
1361 public void EmitSymbolInfo (EmitContext ec)
1363 if (builder != null)
1364 ec.DefineLocalVariable (Name, builder);
1367 public bool IsThisAssigned (EmitContext ec)
1369 if (VariableInfo == null)
1370 throw new Exception ();
1372 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1375 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1378 public bool IsAssigned (EmitContext ec)
1380 if (VariableInfo == null)
1381 throw new Exception ();
1383 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1386 public bool Resolve (EmitContext ec)
1388 if (VariableType != null)
1391 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1395 VariableType = texpr.Type;
1397 if (TypeManager.IsGenericParameter (VariableType))
1400 if (VariableType == TypeManager.void_type) {
1401 Expression.Error_VoidInvalidInTheContext (Location);
1405 if (VariableType.IsAbstract && VariableType.IsSealed) {
1406 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1410 if (VariableType.IsPointer && !ec.InUnsafe)
1411 Expression.UnsafeError (Location);
1416 public bool IsConstant {
1417 get { return (flags & Flags.IsConstant) != 0; }
1418 set { flags |= Flags.IsConstant; }
1421 public bool AddressTaken {
1422 get { return (flags & Flags.AddressTaken) != 0; }
1423 set { flags |= Flags.AddressTaken; }
1426 public bool CompilerGenerated {
1427 get { return (flags & Flags.CompilerGenerated) != 0; }
1428 set { flags |= Flags.CompilerGenerated; }
1431 public override string ToString ()
1433 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1434 Name, Type, VariableInfo, Location);
1438 get { return (flags & Flags.Used) != 0; }
1439 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1442 public bool ReadOnly {
1443 get { return (flags & Flags.ReadOnly) != 0; }
1446 public void SetReadOnlyContext (ReadOnlyContext context)
1448 flags |= Flags.ReadOnly;
1449 ro_context = context;
1452 public string GetReadOnlyContext ()
1455 throw new InternalErrorException ("Variable is not readonly");
1457 switch (ro_context) {
1458 case ReadOnlyContext.Fixed:
1459 return "fixed variable";
1460 case ReadOnlyContext.Foreach:
1461 return "foreach iteration variable";
1462 case ReadOnlyContext.Using:
1463 return "using variable";
1465 throw new NotImplementedException ();
1469 // Whether the variable is pinned, if Pinned the variable has been
1470 // allocated in a pinned slot with DeclareLocal.
1472 public bool Pinned {
1473 get { return (flags & Flags.Pinned) != 0; }
1474 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1477 public bool IsThis {
1478 get { return (flags & Flags.IsThis) != 0; }
1479 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1482 Block IKnownVariable.Block {
1483 get { return Block; }
1486 Location IKnownVariable.Location {
1487 get { return Location; }
1490 public LocalInfo Clone (CloneContext clonectx)
1493 // Variables in anonymous block are not resolved yet
1495 if (VariableType == null)
1496 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1499 // Variables in method block are resolved
1501 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1502 li.VariableType = VariableType;
1508 /// Block represents a C# block.
1512 /// This class is used in a number of places: either to represent
1513 /// explicit blocks that the programmer places or implicit blocks.
1515 /// Implicit blocks are used as labels or to introduce variable
1518 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1519 /// they contain extra information that is not necessary on normal blocks.
1521 public class Block : Statement {
1522 public Block Parent;
1523 public readonly Location StartLocation;
1524 public Location EndLocation = Location.Null;
1526 public ExplicitBlock Explicit;
1527 public ToplevelBlock Toplevel; // TODO: Use Explicit
1530 public enum Flags : byte {
1533 VariablesInitialized = 4,
1538 HasStoreyAccess = 128
1540 protected Flags flags;
1542 public bool Unchecked {
1543 get { return (flags & Flags.Unchecked) != 0; }
1544 set { flags |= Flags.Unchecked; }
1547 public bool Unsafe {
1548 get { return (flags & Flags.Unsafe) != 0; }
1549 set { flags |= Flags.Unsafe; }
1553 // The statements in this block
1555 protected ArrayList statements;
1558 // An array of Blocks. We keep track of children just
1559 // to generate the local variable declarations.
1561 // Statements and child statements are handled through the
1567 // Labels. (label, block) pairs.
1569 protected HybridDictionary labels;
1572 // Keeps track of (name, type) pairs
1574 IDictionary variables;
1575 protected IDictionary range_variables;
1578 // Keeps track of constants
1579 HybridDictionary constants;
1582 // Temporary variables.
1584 ArrayList temporary_variables;
1587 // If this is a switch section, the enclosing switch block.
1591 ArrayList scope_initializers;
1593 ArrayList anonymous_children;
1595 protected static int id;
1599 int assignable_slots;
1600 bool unreachable_shown;
1603 public Block (Block parent)
1604 : this (parent, (Flags) 0, Location.Null, Location.Null)
1607 public Block (Block parent, Flags flags)
1608 : this (parent, flags, Location.Null, Location.Null)
1611 public Block (Block parent, Location start, Location end)
1612 : this (parent, (Flags) 0, start, end)
1615 public Block (Block parent, Flags flags, Location start, Location end)
1617 if (parent != null) {
1618 parent.AddChild (this);
1620 // the appropriate constructors will fixup these fields
1621 Toplevel = parent.Toplevel;
1622 Explicit = parent.Explicit;
1625 this.Parent = parent;
1627 this.StartLocation = start;
1628 this.EndLocation = end;
1631 statements = new ArrayList (4);
1634 public Block CreateSwitchBlock (Location start)
1636 // FIXME: should this be implicit?
1637 Block new_block = new ExplicitBlock (this, start, start);
1638 new_block.switch_block = this;
1643 get { return this_id; }
1646 public IDictionary Variables {
1648 if (variables == null)
1649 variables = new ListDictionary ();
1654 void AddChild (Block b)
1656 if (children == null)
1657 children = new ArrayList (1);
1662 public void SetEndLocation (Location loc)
1667 protected static void Error_158 (string name, Location loc)
1669 Report.Error (158, loc, "The label `{0}' shadows another label " +
1670 "by the same name in a contained scope", name);
1674 /// Adds a label to the current block.
1678 /// false if the name already exists in this block. true
1682 public bool AddLabel (LabeledStatement target)
1684 if (switch_block != null)
1685 return switch_block.AddLabel (target);
1687 string name = target.Name;
1690 while (cur != null) {
1691 LabeledStatement s = cur.DoLookupLabel (name);
1693 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1694 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1698 if (this == Explicit)
1704 while (cur != null) {
1705 if (cur.DoLookupLabel (name) != null) {
1706 Error_158 (name, target.loc);
1710 if (children != null) {
1711 foreach (Block b in children) {
1712 LabeledStatement s = b.DoLookupLabel (name);
1716 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1717 Error_158 (name, target.loc);
1725 Toplevel.CheckError158 (name, target.loc);
1728 labels = new HybridDictionary();
1730 labels.Add (name, target);
1734 public LabeledStatement LookupLabel (string name)
1736 LabeledStatement s = DoLookupLabel (name);
1740 if (children == null)
1743 foreach (Block child in children) {
1744 if (Explicit != child.Explicit)
1747 s = child.LookupLabel (name);
1755 LabeledStatement DoLookupLabel (string name)
1757 if (switch_block != null)
1758 return switch_block.LookupLabel (name);
1761 if (labels.Contains (name))
1762 return ((LabeledStatement) labels [name]);
1767 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1770 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1771 while (kvi == null) {
1772 b = b.Explicit.Parent;
1775 kvi = b.Explicit.GetKnownVariable (name);
1781 // Is kvi.Block nested inside 'b'
1782 if (b.Explicit != kvi.Block.Explicit) {
1784 // If a variable by the same name it defined in a nested block of this
1785 // block, we violate the invariant meaning in a block.
1788 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1789 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1794 // It's ok if the definition is in a nested subblock of b, but not
1795 // nested inside this block -- a definition in a sibling block
1796 // should not affect us.
1802 // Block 'b' and kvi.Block are the same textual block.
1803 // However, different variables are extant.
1805 // Check if the variable is in scope in both blocks. We use
1806 // an indirect check that depends on AddVariable doing its
1807 // part in maintaining the invariant-meaning-in-block property.
1809 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1812 if (this is ToplevelBlock) {
1813 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1814 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1819 // Even though we detected the error when the name is used, we
1820 // treat it as if the variable declaration was in error.
1822 Report.SymbolRelatedToPreviousError (loc, name);
1823 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1827 public LocalInfo AddVariable (Expression type, string name, Location l)
1829 LocalInfo vi = GetLocalInfo (name);
1831 Report.SymbolRelatedToPreviousError (vi.Location, name);
1832 if (Explicit == vi.Block.Explicit) {
1833 if (type == Linq.ImplicitQueryParameter.ImplicitType.Instance && type == vi.Type)
1834 Error_AlreadyDeclared (l, name);
1836 Error_AlreadyDeclared (l, name, null);
1838 Error_AlreadyDeclared (l, name, "parent");
1843 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1845 Report.SymbolRelatedToPreviousError (pi.Location, name);
1846 Error_AlreadyDeclared (loc, name,
1847 pi.Block == Toplevel ? "method argument" : "parent or current");
1851 if (Toplevel.GenericMethod != null) {
1852 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1853 if (tp.Name == name) {
1854 Report.SymbolRelatedToPreviousError (tp);
1855 Error_AlreadyDeclaredTypeParameter (loc, name);
1861 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1863 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1864 Error_AlreadyDeclared (l, name, "child");
1868 vi = new LocalInfo (type, name, this, l);
1871 if ((flags & Flags.VariablesInitialized) != 0)
1872 throw new InternalErrorException ("block has already been resolved");
1877 protected virtual void AddVariable (LocalInfo li)
1879 Variables.Add (li.Name, li);
1880 Explicit.AddKnownVariable (li.Name, li);
1883 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1885 if (reason == null) {
1886 Error_AlreadyDeclared (loc, var);
1890 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1891 "in this scope because it would give a different meaning " +
1892 "to `{0}', which is already used in a `{1}' scope " +
1893 "to denote something else", var, reason);
1896 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1898 Report.Error (128, loc,
1899 "A local variable named `{0}' is already defined in this scope", name);
1902 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1904 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1907 public bool AddConstant (Expression type, string name, Expression value, Location l)
1909 if (AddVariable (type, name, l) == null)
1912 if (constants == null)
1913 constants = new HybridDictionary();
1915 constants.Add (name, value);
1917 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1922 static int next_temp_id = 0;
1924 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1926 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1928 if (temporary_variables == null)
1929 temporary_variables = new ArrayList ();
1931 int id = ++next_temp_id;
1932 string name = "$s_" + id.ToString ();
1934 LocalInfo li = new LocalInfo (te, name, this, loc);
1935 li.CompilerGenerated = true;
1936 temporary_variables.Add (li);
1940 public LocalInfo GetLocalInfo (string name)
1943 for (Block b = this; b != null; b = b.Parent) {
1944 if (b.variables != null) {
1945 ret = (LocalInfo) b.variables [name];
1950 if (b.range_variables != null) {
1951 ret = (LocalInfo) b.range_variables [name];
1960 public Expression GetVariableType (string name)
1962 LocalInfo vi = GetLocalInfo (name);
1963 return vi == null ? null : vi.Type;
1966 public Expression GetConstantExpression (string name)
1968 for (Block b = this; b != null; b = b.Parent) {
1969 if (b.constants != null) {
1970 Expression ret = b.constants [name] as Expression;
1979 // It should be used by expressions which require to
1980 // register a statement during resolve process.
1982 public void AddScopeStatement (StatementExpression s)
1984 if (scope_initializers == null)
1985 scope_initializers = new ArrayList ();
1987 scope_initializers.Add (s);
1990 public void AddStatement (Statement s)
1993 flags |= Flags.BlockUsed;
1997 get { return (flags & Flags.BlockUsed) != 0; }
2002 flags |= Flags.BlockUsed;
2005 public bool HasRet {
2006 get { return (flags & Flags.HasRet) != 0; }
2009 public bool IsDestructor {
2010 get { return (flags & Flags.IsDestructor) != 0; }
2013 public void SetDestructor ()
2015 flags |= Flags.IsDestructor;
2018 public int AssignableSlots {
2021 // if ((flags & Flags.VariablesInitialized) == 0)
2022 // throw new Exception ("Variables have not been initialized yet");
2023 return assignable_slots;
2027 public ArrayList AnonymousChildren {
2028 get { return anonymous_children; }
2031 public void AddAnonymousChild (ToplevelBlock b)
2033 if (anonymous_children == null)
2034 anonymous_children = new ArrayList ();
2036 anonymous_children.Add (b);
2039 void DoResolveConstants (EmitContext ec)
2041 if (constants == null)
2044 if (variables == null)
2045 throw new InternalErrorException ("cannot happen");
2047 foreach (DictionaryEntry de in variables) {
2048 string name = (string) de.Key;
2049 LocalInfo vi = (LocalInfo) de.Value;
2050 Type variable_type = vi.VariableType;
2052 if (variable_type == null) {
2053 if (vi.Type is VarExpr)
2054 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2059 Expression cv = (Expression) constants [name];
2063 // Don't let 'const int Foo = Foo;' succeed.
2064 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2065 // which in turn causes the 'must be constant' error to be triggered.
2066 constants.Remove (name);
2068 if (!Const.IsConstantTypeValid (variable_type)) {
2069 Const.Error_InvalidConstantType (variable_type, loc);
2073 ec.CurrentBlock = this;
2075 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2076 e = cv.Resolve (ec);
2081 Constant ce = e as Constant;
2083 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2087 e = ce.ConvertImplicitly (variable_type);
2089 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2090 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2092 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2096 constants.Add (name, e);
2097 vi.IsConstant = true;
2101 protected void ResolveMeta (EmitContext ec, int offset)
2103 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2105 // If some parent block was unsafe, we remain unsafe even if this block
2106 // isn't explicitly marked as such.
2107 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2108 flags |= Flags.VariablesInitialized;
2110 if (variables != null) {
2111 foreach (LocalInfo li in variables.Values) {
2112 if (!li.Resolve (ec))
2114 li.VariableInfo = new VariableInfo (li, offset);
2115 offset += li.VariableInfo.Length;
2118 assignable_slots = offset;
2120 DoResolveConstants (ec);
2122 if (children == null)
2124 foreach (Block b in children)
2125 b.ResolveMeta (ec, offset);
2130 // Emits the local variable declarations for a block
2132 public virtual void EmitMeta (EmitContext ec)
2134 if (variables != null){
2135 foreach (LocalInfo vi in variables.Values)
2136 vi.ResolveVariable (ec);
2139 if (temporary_variables != null) {
2140 for (int i = 0; i < temporary_variables.Count; i++)
2141 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2144 if (children != null) {
2145 for (int i = 0; i < children.Count; i++)
2146 ((Block)children[i]).EmitMeta(ec);
2150 void UsageWarning ()
2152 if (variables == null || Report.WarningLevel < 3)
2155 foreach (DictionaryEntry de in variables) {
2156 LocalInfo vi = (LocalInfo) de.Value;
2159 string name = (string) de.Key;
2161 // vi.VariableInfo can be null for 'catch' variables
2162 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2163 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2165 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2170 static void CheckPossibleMistakenEmptyStatement (Statement s)
2174 // Some statements are wrapped by a Block. Since
2175 // others' internal could be changed, here I treat
2176 // them as possibly wrapped by Block equally.
2177 Block b = s as Block;
2178 if (b != null && b.statements.Count == 1)
2179 s = (Statement) b.statements [0];
2182 body = ((Lock) s).Statement;
2184 body = ((For) s).Statement;
2185 else if (s is Foreach)
2186 body = ((Foreach) s).Statement;
2187 else if (s is While)
2188 body = ((While) s).Statement;
2189 else if (s is Fixed)
2190 body = ((Fixed) s).Statement;
2191 else if (s is Using)
2192 body = ((Using) s).EmbeddedStatement;
2193 else if (s is UsingTemporary)
2194 body = ((UsingTemporary) s).Statement;
2198 if (body == null || body is EmptyStatement)
2199 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2202 public override bool Resolve (EmitContext ec)
2204 Block prev_block = ec.CurrentBlock;
2207 int errors = Report.Errors;
2209 ec.CurrentBlock = this;
2210 ec.StartFlowBranching (this);
2212 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2215 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2216 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2217 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2218 // responsible for handling the situation.
2220 int statement_count = statements.Count;
2221 for (int ix = 0; ix < statement_count; ix++){
2222 Statement s = (Statement) statements [ix];
2223 // Check possible empty statement (CS0642)
2224 if (Report.WarningLevel >= 3 &&
2225 ix + 1 < statement_count &&
2226 statements [ix + 1] is Block)
2227 CheckPossibleMistakenEmptyStatement (s);
2230 // Warn if we detect unreachable code.
2233 if (s is EmptyStatement)
2236 if (!unreachable_shown && !(s is LabeledStatement)) {
2237 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2238 unreachable_shown = true;
2241 Block c_block = s as Block;
2242 if (c_block != null)
2243 c_block.unreachable = c_block.unreachable_shown = true;
2247 // Note that we're not using ResolveUnreachable() for unreachable
2248 // statements here. ResolveUnreachable() creates a temporary
2249 // flow branching and kills it afterwards. This leads to problems
2250 // if you have two unreachable statements where the first one
2251 // assigns a variable and the second one tries to access it.
2254 if (!s.Resolve (ec)) {
2256 if (ec.IsInProbingMode)
2259 statements [ix] = EmptyStatement.Value;
2263 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2264 statements [ix] = EmptyStatement.Value;
2266 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2267 if (unreachable && s is LabeledStatement)
2268 throw new InternalErrorException ("should not happen");
2271 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2272 ec.CurrentBranching, statement_count);
2274 while (ec.CurrentBranching is FlowBranchingLabeled)
2275 ec.EndFlowBranching ();
2277 bool flow_unreachable = ec.EndFlowBranching ();
2279 ec.CurrentBlock = prev_block;
2281 if (flow_unreachable)
2282 flags |= Flags.HasRet;
2284 // If we're a non-static `struct' constructor which doesn't have an
2285 // initializer, then we must initialize all of the struct's fields.
2286 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2289 if ((labels != null) && (Report.WarningLevel >= 2)) {
2290 foreach (LabeledStatement label in labels.Values)
2291 if (!label.HasBeenReferenced)
2292 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2295 if (ok && errors == Report.Errors)
2301 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2303 unreachable_shown = true;
2307 Report.Warning (162, 2, loc, "Unreachable code detected");
2309 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2310 bool ok = Resolve (ec);
2311 ec.KillFlowBranching ();
2316 protected override void DoEmit (EmitContext ec)
2318 for (int ix = 0; ix < statements.Count; ix++){
2319 Statement s = (Statement) statements [ix];
2324 public override void Emit (EmitContext ec)
2326 Block prev_block = ec.CurrentBlock;
2327 ec.CurrentBlock = this;
2329 if (scope_initializers != null) {
2330 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2332 bool omit_debug_info = ec.OmitDebuggingInfo;
2333 ec.OmitDebuggingInfo = true;
2334 foreach (StatementExpression s in scope_initializers)
2336 ec.OmitDebuggingInfo = omit_debug_info;
2338 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2341 ec.Mark (StartLocation);
2344 if (SymbolWriter.HasSymbolWriter)
2345 EmitSymbolInfo (ec);
2347 ec.CurrentBlock = prev_block;
2350 protected virtual void EmitSymbolInfo (EmitContext ec)
2352 if (variables != null) {
2353 foreach (LocalInfo vi in variables.Values) {
2354 vi.EmitSymbolInfo (ec);
2359 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2361 MutateVariables (storey);
2363 foreach (Statement s in statements)
2364 s.MutateHoistedGenericType (storey);
2367 void MutateVariables (AnonymousMethodStorey storey)
2369 if (variables != null) {
2370 foreach (LocalInfo vi in variables.Values) {
2371 vi.VariableType = storey.MutateType (vi.VariableType);
2375 if (temporary_variables != null) {
2376 foreach (LocalInfo vi in temporary_variables)
2377 vi.VariableType = storey.MutateType (vi.VariableType);
2381 public override string ToString ()
2383 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2386 protected override void CloneTo (CloneContext clonectx, Statement t)
2388 Block target = (Block) t;
2390 clonectx.AddBlockMap (this, target);
2392 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2393 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2395 target.Parent = clonectx.RemapBlockCopy (Parent);
2397 if (variables != null){
2398 target.variables = new Hashtable ();
2400 foreach (DictionaryEntry de in variables){
2401 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2402 target.variables [de.Key] = newlocal;
2403 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2407 target.statements = new ArrayList (statements.Count);
2408 foreach (Statement s in statements)
2409 target.statements.Add (s.Clone (clonectx));
2411 if (target.children != null){
2412 target.children = new ArrayList (children.Count);
2413 foreach (Block b in children){
2414 target.children.Add (clonectx.LookupBlock (b));
2419 // TODO: labels, switch_block, constants (?), anonymous_children
2424 public class ExplicitBlock : Block {
2425 HybridDictionary known_variables;
2426 protected AnonymousMethodStorey am_storey;
2428 public ExplicitBlock (Block parent, Location start, Location end)
2429 : this (parent, (Flags) 0, start, end)
2433 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2434 : base (parent, flags, start, end)
2436 this.Explicit = this;
2440 // Marks a variable with name @name as being used in this or a child block.
2441 // If a variable name has been used in a child block, it's illegal to
2442 // declare a variable with the same name in the current block.
2444 internal void AddKnownVariable (string name, IKnownVariable info)
2446 if (known_variables == null)
2447 known_variables = new HybridDictionary();
2449 known_variables [name] = info;
2452 Parent.Explicit.AddKnownVariable (name, info);
2455 public AnonymousMethodStorey AnonymousMethodStorey {
2456 get { return am_storey; }
2460 // Creates anonymous method storey in current block
2462 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2465 // When referencing a variable in iterator storey from children anonymous method
2467 if (Toplevel.am_storey is IteratorStorey) {
2468 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2469 return Toplevel.am_storey;
2473 // An iterator has only 1 storey block
2475 if (ec.CurrentIterator != null)
2476 return ec.CurrentIterator.Storey;
2478 if (am_storey == null) {
2479 MemberBase mc = ec.ResolveContext as MemberBase;
2480 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2483 // Create anonymous method storey for this block
2485 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2489 // Creates a link between this block and the anonymous method
2491 // An anonymous method can reference variables from any outer block, but they are
2492 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2493 // need to create another link between those variable storeys
2495 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2499 public override void Emit (EmitContext ec)
2501 if (am_storey != null)
2502 am_storey.EmitHoistedVariables (ec);
2504 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2505 if (emit_debug_info)
2510 if (emit_debug_info)
2514 public override void EmitMeta (EmitContext ec)
2517 // Creates anonymous method storey
2519 if (am_storey != null) {
2520 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2521 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2524 am_storey.DefineType ();
2525 am_storey.DefineMembers ();
2526 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2532 protected override void EmitSymbolInfo (EmitContext ec)
2534 if (am_storey != null)
2535 SymbolWriter.DefineScopeVariable (am_storey.ID);
2537 base.EmitSymbolInfo (ec);
2540 internal IKnownVariable GetKnownVariable (string name)
2542 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2545 public void PropagateStoreyReference (AnonymousMethodStorey s)
2547 if (Parent != null && am_storey != s) {
2548 if (am_storey != null)
2549 am_storey.AddParentStoreyReference (s);
2551 Parent.Explicit.PropagateStoreyReference (s);
2555 public override bool Resolve (EmitContext ec)
2557 bool ok = base.Resolve (ec);
2560 // Discard an anonymous method storey when this block has no hoisted variables
2562 if (am_storey != null && !am_storey.HasHoistedVariables) {
2570 protected override void CloneTo (CloneContext clonectx, Statement t)
2572 ExplicitBlock target = (ExplicitBlock) t;
2573 target.known_variables = null;
2574 base.CloneTo (clonectx, t);
2578 public class ToplevelParameterInfo : IKnownVariable {
2579 public readonly ToplevelBlock Block;
2580 public readonly int Index;
2581 public VariableInfo VariableInfo;
2583 Block IKnownVariable.Block {
2584 get { return Block; }
2586 public Parameter Parameter {
2587 get { return Block.Parameters [Index]; }
2589 public Location Location {
2590 get { return Parameter.Location; }
2593 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2601 // A toplevel block contains extra information, the split is done
2602 // only to separate information that would otherwise bloat the more
2603 // lightweight Block.
2605 // In particular, this was introduced when the support for Anonymous
2606 // Methods was implemented.
2608 public class ToplevelBlock : ExplicitBlock {
2609 GenericMethod generic;
2610 FlowBranchingToplevel top_level_branching;
2611 Parameters parameters;
2612 ToplevelParameterInfo[] parameter_info;
2613 LocalInfo this_variable;
2615 public HoistedVariable HoistedThisVariable;
2618 // The parameters for the block.
2620 public Parameters Parameters {
2621 get { return parameters; }
2624 public GenericMethod GenericMethod {
2625 get { return generic; }
2628 public bool HasStoreyAccess {
2629 set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
2630 get { return (flags & Flags.HasStoreyAccess) != 0; }
2633 public ToplevelBlock Container {
2634 get { return Parent == null ? null : Parent.Toplevel; }
2637 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2638 this (parent, (Flags) 0, parameters, start)
2642 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2643 this (parent, parameters, start)
2645 this.generic = generic;
2648 public ToplevelBlock (Parameters parameters, Location start) :
2649 this (null, (Flags) 0, parameters, start)
2653 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2654 this (null, flags, parameters, start)
2658 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2659 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2660 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2661 base (null, flags, start, Location.Null)
2663 this.Toplevel = this;
2665 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2666 this.Parent = parent;
2668 parent.AddAnonymousChild (this);
2670 if (!this.parameters.Empty)
2671 ProcessParameters ();
2674 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2678 protected override void CloneTo (CloneContext clonectx, Statement t)
2680 ToplevelBlock target = (ToplevelBlock) t;
2681 base.CloneTo (clonectx, t);
2683 if (parameters.Count != 0)
2684 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2685 for (int i = 0; i < parameters.Count; ++i)
2686 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2689 public bool CheckError158 (string name, Location loc)
2691 if (AnonymousChildren != null) {
2692 foreach (ToplevelBlock child in AnonymousChildren) {
2693 if (!child.CheckError158 (name, loc))
2698 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2699 if (!c.DoCheckError158 (name, loc))
2706 public virtual Expression GetTransparentIdentifier (string name)
2711 void ProcessParameters ()
2713 int n = parameters.Count;
2714 parameter_info = new ToplevelParameterInfo [n];
2715 for (int i = 0; i < n; ++i) {
2716 parameter_info [i] = new ToplevelParameterInfo (this, i);
2718 Parameter p = parameters [i];
2722 string name = p.Name;
2723 LocalInfo vi = GetLocalInfo (name);
2725 Report.SymbolRelatedToPreviousError (vi.Location, name);
2726 Error_AlreadyDeclared (loc, name, "parent or current");
2730 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2732 Report.SymbolRelatedToPreviousError (pi.Location, name);
2733 Error_AlreadyDeclared (loc, name, "parent or current");
2737 AddKnownVariable (name, parameter_info [i]);
2740 // mark this block as "used" so that we create local declarations in a sub-block
2741 // FIXME: This appears to uncover a lot of bugs
2745 bool DoCheckError158 (string name, Location loc)
2747 LabeledStatement s = LookupLabel (name);
2749 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2750 Error_158 (name, loc);
2757 public override Expression CreateExpressionTree (EmitContext ec)
2759 return ((Statement) statements [0]).CreateExpressionTree (ec);
2763 // Reformats this block to be top-level iterator block
2765 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2767 // Create block with original statements
2768 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2771 // TODO: Change to iter_block.statements = statements;
2772 foreach (Statement stmt in source.statements)
2773 iter_block.AddStatement (stmt);
2774 labels = source.labels;
2776 AddStatement (new IteratorStatement (iterator, iter_block));
2778 source.statements = new ArrayList (1);
2779 source.AddStatement (new Return (iterator, iterator.Location));
2780 source.IsIterator = false;
2782 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2783 source.am_storey = iterator_storey;
2784 return iterator_storey;
2787 public FlowBranchingToplevel TopLevelBranching {
2788 get { return top_level_branching; }
2792 // Returns a `ParameterReference' for the given name, or null if there
2793 // is no such parameter
2795 public ParameterReference GetParameterReference (string name, Location loc)
2797 ToplevelParameterInfo p = GetParameterInfo (name);
2798 return p == null ? null : new ParameterReference (this, p, loc);
2801 public ToplevelParameterInfo GetParameterInfo (string name)
2804 for (ToplevelBlock t = this; t != null; t = t.Container) {
2805 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2807 return t.parameter_info [idx];
2813 // Returns the "this" instance variable of this block.
2814 // See AddThisVariable() for more information.
2816 public LocalInfo ThisVariable {
2817 get { return this_variable; }
2821 // This is used by non-static `struct' constructors which do not have an
2822 // initializer - in this case, the constructor must initialize all of the
2823 // struct's fields. To do this, we add a "this" variable and use the flow
2824 // analysis code to ensure that it's been fully initialized before control
2825 // leaves the constructor.
2827 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2829 if (this_variable == null) {
2830 this_variable = new LocalInfo (ds, this, l);
2831 this_variable.Used = true;
2832 this_variable.IsThis = true;
2834 Variables.Add ("this", this_variable);
2837 return this_variable;
2840 public bool IsIterator {
2841 get { return (flags & Flags.IsIterator) != 0; }
2842 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2845 public bool IsThisAssigned (EmitContext ec)
2847 return this_variable == null || this_variable.IsThisAssigned (ec);
2850 public bool ResolveMeta (EmitContext ec, Parameters ip)
2852 int errors = Report.Errors;
2853 int orig_count = parameters.Count;
2855 if (top_level_branching != null)
2861 // Assert: orig_count != parameter.Count => orig_count == 0
2862 if (orig_count != 0 && orig_count != parameters.Count)
2863 throw new InternalErrorException ("parameter information mismatch");
2865 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2867 for (int i = 0; i < orig_count; ++i) {
2868 Parameter.Modifier mod = parameters.ParameterModifier (i);
2870 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2873 VariableInfo vi = new VariableInfo (ip, i, offset);
2874 parameter_info [i].VariableInfo = vi;
2875 offset += vi.Length;
2878 ResolveMeta (ec, offset);
2880 top_level_branching = ec.StartFlowBranching (this);
2882 return Report.Errors == errors;
2886 // Check whether all `out' parameters have been assigned.
2888 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2890 if (vector.IsUnreachable)
2893 int n = parameter_info == null ? 0 : parameter_info.Length;
2895 for (int i = 0; i < n; i++) {
2896 VariableInfo var = parameter_info [i].VariableInfo;
2901 if (vector.IsAssigned (var, false))
2904 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2909 public override void EmitMeta (EmitContext ec)
2911 parameters.ResolveVariable ();
2913 // Avoid declaring an IL variable for this_variable since it is not accessed
2914 // from the generated IL
2915 if (this_variable != null)
2916 Variables.Remove ("this");
2920 public override void Emit (EmitContext ec)
2923 ec.Mark (EndLocation);
2927 public class SwitchLabel {
2934 Label il_label_code;
2935 bool il_label_code_set;
2937 public static readonly object NullStringCase = new object ();
2940 // if expr == null, then it is the default case.
2942 public SwitchLabel (Expression expr, Location l)
2948 public Expression Label {
2954 public Location Location {
2958 public object Converted {
2964 public Label GetILLabel (EmitContext ec)
2967 il_label = ec.ig.DefineLabel ();
2968 il_label_set = true;
2973 public Label GetILLabelCode (EmitContext ec)
2975 if (!il_label_code_set){
2976 il_label_code = ec.ig.DefineLabel ();
2977 il_label_code_set = true;
2979 return il_label_code;
2983 // Resolves the expression, reduces it to a literal if possible
2984 // and then converts it to the requested type.
2986 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2988 Expression e = label.Resolve (ec);
2993 Constant c = e as Constant;
2995 Report.Error (150, loc, "A constant value is expected");
2999 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3000 converted = NullStringCase;
3004 if (allow_nullable && c.GetValue () == null) {
3005 converted = NullStringCase;
3009 c = c.ImplicitConversionRequired (required_type, loc);
3013 converted = c.GetValue ();
3017 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3020 if (converted == null)
3022 else if (converted == NullStringCase)
3024 else if (TypeManager.IsEnumType (switch_type))
3025 label = TypeManager.CSharpEnumValue (switch_type, converted);
3027 label = converted.ToString ();
3029 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3030 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3033 public SwitchLabel Clone (CloneContext clonectx)
3035 return new SwitchLabel (label.Clone (clonectx), loc);
3039 public class SwitchSection {
3040 // An array of SwitchLabels.
3041 public readonly ArrayList Labels;
3042 public readonly Block Block;
3044 public SwitchSection (ArrayList labels, Block block)
3050 public SwitchSection Clone (CloneContext clonectx)
3052 ArrayList cloned_labels = new ArrayList ();
3054 foreach (SwitchLabel sl in cloned_labels)
3055 cloned_labels.Add (sl.Clone (clonectx));
3057 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3061 public class Switch : Statement {
3062 public ArrayList Sections;
3063 public Expression Expr;
3066 /// Maps constants whose type type SwitchType to their SwitchLabels.
3068 public IDictionary Elements;
3071 /// The governing switch type
3073 public Type SwitchType;
3078 Label default_target;
3080 Expression new_expr;
3083 SwitchSection constant_section;
3084 SwitchSection default_section;
3086 ExpressionStatement string_dictionary;
3087 FieldExpr switch_cache_field;
3088 static int unique_counter;
3092 // Nullable Types support for GMCS.
3094 Nullable.Unwrap unwrap;
3096 protected bool HaveUnwrap {
3097 get { return unwrap != null; }
3100 protected bool HaveUnwrap {
3101 get { return false; }
3106 // The types allowed to be implicitly cast from
3107 // on the governing type
3109 static Type [] allowed_types;
3111 public Switch (Expression e, ArrayList sects, Location l)
3118 public bool GotDefault {
3120 return default_section != null;
3124 public Label DefaultTarget {
3126 return default_target;
3131 // Determines the governing type for a switch. The returned
3132 // expression might be the expression from the switch, or an
3133 // expression that includes any potential conversions to the
3134 // integral types or to string.
3136 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3140 if (t == TypeManager.byte_type ||
3141 t == TypeManager.sbyte_type ||
3142 t == TypeManager.ushort_type ||
3143 t == TypeManager.short_type ||
3144 t == TypeManager.uint32_type ||
3145 t == TypeManager.int32_type ||
3146 t == TypeManager.uint64_type ||
3147 t == TypeManager.int64_type ||
3148 t == TypeManager.char_type ||
3149 t == TypeManager.string_type ||
3150 t == TypeManager.bool_type ||
3151 TypeManager.IsEnumType (t))
3154 if (allowed_types == null){
3155 allowed_types = new Type [] {
3156 TypeManager.sbyte_type,
3157 TypeManager.byte_type,
3158 TypeManager.short_type,
3159 TypeManager.ushort_type,
3160 TypeManager.int32_type,
3161 TypeManager.uint32_type,
3162 TypeManager.int64_type,
3163 TypeManager.uint64_type,
3164 TypeManager.char_type,
3165 TypeManager.string_type
3170 // Try to find a *user* defined implicit conversion.
3172 // If there is no implicit conversion, or if there are multiple
3173 // conversions, we have to report an error
3175 Expression converted = null;
3176 foreach (Type tt in allowed_types){
3179 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3184 // Ignore over-worked ImplicitUserConversions that do
3185 // an implicit conversion in addition to the user conversion.
3187 if (!(e is UserCast))
3190 if (converted != null){
3191 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3201 // Performs the basic sanity checks on the switch statement
3202 // (looks for duplicate keys and non-constant expressions).
3204 // It also returns a hashtable with the keys that we will later
3205 // use to compute the switch tables
3207 bool CheckSwitch (EmitContext ec)
3210 Elements = Sections.Count > 10 ?
3211 (IDictionary)new Hashtable () :
3212 (IDictionary)new ListDictionary ();
3214 foreach (SwitchSection ss in Sections){
3215 foreach (SwitchLabel sl in ss.Labels){
3216 if (sl.Label == null){
3217 if (default_section != null){
3218 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3221 default_section = ss;
3225 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3230 object key = sl.Converted;
3231 if (key == SwitchLabel.NullStringCase)
3232 has_null_case = true;
3235 Elements.Add (key, sl);
3236 } catch (ArgumentException) {
3237 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3245 void EmitObjectInteger (ILGenerator ig, object k)
3248 IntConstant.EmitInt (ig, (int) k);
3249 else if (k is Constant) {
3250 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3253 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3256 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3258 IntConstant.EmitInt (ig, (int) (long) k);
3259 ig.Emit (OpCodes.Conv_I8);
3262 LongConstant.EmitLong (ig, (long) k);
3264 else if (k is ulong)
3266 ulong ul = (ulong) k;
3269 IntConstant.EmitInt (ig, unchecked ((int) ul));
3270 ig.Emit (OpCodes.Conv_U8);
3274 LongConstant.EmitLong (ig, unchecked ((long) ul));
3278 IntConstant.EmitInt (ig, (int) ((char) k));
3279 else if (k is sbyte)
3280 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3282 IntConstant.EmitInt (ig, (int) ((byte) k));
3283 else if (k is short)
3284 IntConstant.EmitInt (ig, (int) ((short) k));
3285 else if (k is ushort)
3286 IntConstant.EmitInt (ig, (int) ((ushort) k));
3288 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3290 throw new Exception ("Unhandled case");
3293 // structure used to hold blocks of keys while calculating table switch
3294 class KeyBlock : IComparable
3296 public KeyBlock (long _first)
3298 first = last = _first;
3302 public ArrayList element_keys = null;
3303 // how many items are in the bucket
3304 public int Size = 1;
3307 get { return (int) (last - first + 1); }
3309 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3311 return kb_last.last - kb_first.first + 1;
3313 public int CompareTo (object obj)
3315 KeyBlock kb = (KeyBlock) obj;
3316 int nLength = Length;
3317 int nLengthOther = kb.Length;
3318 if (nLengthOther == nLength)
3319 return (int) (kb.first - first);
3320 return nLength - nLengthOther;
3325 /// This method emits code for a lookup-based switch statement (non-string)
3326 /// Basically it groups the cases into blocks that are at least half full,
3327 /// and then spits out individual lookup opcodes for each block.
3328 /// It emits the longest blocks first, and short blocks are just
3329 /// handled with direct compares.
3331 /// <param name="ec"></param>
3332 /// <param name="val"></param>
3333 /// <returns></returns>
3334 void TableSwitchEmit (EmitContext ec, Expression val)
3336 int element_count = Elements.Count;
3337 object [] element_keys = new object [element_count];
3338 Elements.Keys.CopyTo (element_keys, 0);
3339 Array.Sort (element_keys);
3341 // initialize the block list with one element per key
3342 ArrayList key_blocks = new ArrayList (element_count);
3343 foreach (object key in element_keys)
3344 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3346 KeyBlock current_kb;
3347 // iteratively merge the blocks while they are at least half full
3348 // there's probably a really cool way to do this with a tree...
3349 while (key_blocks.Count > 1)
3351 ArrayList key_blocks_new = new ArrayList ();
3352 current_kb = (KeyBlock) key_blocks [0];
3353 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3355 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3356 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3359 current_kb.last = kb.last;
3360 current_kb.Size += kb.Size;
3364 // start a new block
3365 key_blocks_new.Add (current_kb);
3369 key_blocks_new.Add (current_kb);
3370 if (key_blocks.Count == key_blocks_new.Count)
3372 key_blocks = key_blocks_new;
3375 // initialize the key lists
3376 foreach (KeyBlock kb in key_blocks)
3377 kb.element_keys = new ArrayList ();
3379 // fill the key lists
3381 if (key_blocks.Count > 0) {
3382 current_kb = (KeyBlock) key_blocks [0];
3383 foreach (object key in element_keys)
3385 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3386 System.Convert.ToInt64 (key) > current_kb.last;
3388 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3389 current_kb.element_keys.Add (key);
3393 // sort the blocks so we can tackle the largest ones first
3396 // okay now we can start...
3397 ILGenerator ig = ec.ig;
3398 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3399 Label lbl_default = default_target;
3401 Type type_keys = null;
3402 if (element_keys.Length > 0)
3403 type_keys = element_keys [0].GetType (); // used for conversions
3407 if (TypeManager.IsEnumType (SwitchType))
3408 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3410 compare_type = SwitchType;
3412 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3414 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3415 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3418 foreach (object key in kb.element_keys) {
3419 SwitchLabel sl = (SwitchLabel) Elements [key];
3420 if (key is int && (int) key == 0) {
3421 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3424 EmitObjectInteger (ig, key);
3425 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3431 // TODO: if all the keys in the block are the same and there are
3432 // no gaps/defaults then just use a range-check.
3433 if (compare_type == TypeManager.int64_type ||
3434 compare_type == TypeManager.uint64_type)
3436 // TODO: optimize constant/I4 cases
3438 // check block range (could be > 2^31)
3440 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3441 ig.Emit (OpCodes.Blt, lbl_default);
3443 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3444 ig.Emit (OpCodes.Bgt, lbl_default);
3450 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3451 ig.Emit (OpCodes.Sub);
3453 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3459 int first = (int) kb.first;
3462 IntConstant.EmitInt (ig, first);
3463 ig.Emit (OpCodes.Sub);
3467 IntConstant.EmitInt (ig, -first);
3468 ig.Emit (OpCodes.Add);
3472 // first, build the list of labels for the switch
3474 int cJumps = kb.Length;
3475 Label [] switch_labels = new Label [cJumps];
3476 for (int iJump = 0; iJump < cJumps; iJump++)
3478 object key = kb.element_keys [iKey];
3479 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3481 SwitchLabel sl = (SwitchLabel) Elements [key];
3482 switch_labels [iJump] = sl.GetILLabel (ec);
3486 switch_labels [iJump] = lbl_default;
3488 // emit the switch opcode
3489 ig.Emit (OpCodes.Switch, switch_labels);
3492 // mark the default for this block
3494 ig.MarkLabel (lbl_default);
3497 // TODO: find the default case and emit it here,
3498 // to prevent having to do the following jump.
3499 // make sure to mark other labels in the default section
3501 // the last default just goes to the end
3502 if (element_keys.Length > 0)
3503 ig.Emit (OpCodes.Br, lbl_default);
3505 // now emit the code for the sections
3506 bool found_default = false;
3508 foreach (SwitchSection ss in Sections) {
3509 foreach (SwitchLabel sl in ss.Labels) {
3510 if (sl.Converted == SwitchLabel.NullStringCase) {
3511 ig.MarkLabel (null_target);
3512 } else if (sl.Label == null) {
3513 ig.MarkLabel (lbl_default);
3514 found_default = true;
3516 ig.MarkLabel (null_target);
3518 ig.MarkLabel (sl.GetILLabel (ec));
3519 ig.MarkLabel (sl.GetILLabelCode (ec));
3524 if (!found_default) {
3525 ig.MarkLabel (lbl_default);
3526 if (!has_null_case) {
3527 ig.MarkLabel (null_target);
3531 ig.MarkLabel (lbl_end);
3534 SwitchSection FindSection (SwitchLabel label)
3536 foreach (SwitchSection ss in Sections){
3537 foreach (SwitchLabel sl in ss.Labels){
3546 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3548 foreach (SwitchSection ss in Sections)
3549 ss.Block.MutateHoistedGenericType (storey);
3552 public static void Reset ()
3557 public override bool Resolve (EmitContext ec)
3559 Expr = Expr.Resolve (ec);
3563 new_expr = SwitchGoverningType (ec, Expr);
3566 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3567 unwrap = Nullable.Unwrap.Create (Expr, ec);
3571 new_expr = SwitchGoverningType (ec, unwrap);
3575 if (new_expr == null){
3576 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3581 SwitchType = new_expr.Type;
3583 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3584 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3588 if (!CheckSwitch (ec))
3592 Elements.Remove (SwitchLabel.NullStringCase);
3594 Switch old_switch = ec.Switch;
3596 ec.Switch.SwitchType = SwitchType;
3598 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3599 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3601 is_constant = new_expr is Constant;
3603 object key = ((Constant) new_expr).GetValue ();
3604 SwitchLabel label = (SwitchLabel) Elements [key];
3606 constant_section = FindSection (label);
3607 if (constant_section == null)
3608 constant_section = default_section;
3613 foreach (SwitchSection ss in Sections){
3615 ec.CurrentBranching.CreateSibling (
3616 null, FlowBranching.SiblingType.SwitchSection);
3620 if (is_constant && (ss != constant_section)) {
3621 // If we're a constant switch, we're only emitting
3622 // one single section - mark all the others as
3624 ec.CurrentBranching.CurrentUsageVector.Goto ();
3625 if (!ss.Block.ResolveUnreachable (ec, true)) {
3629 if (!ss.Block.Resolve (ec))
3634 if (default_section == null)
3635 ec.CurrentBranching.CreateSibling (
3636 null, FlowBranching.SiblingType.SwitchSection);
3638 ec.EndFlowBranching ();
3639 ec.Switch = old_switch;
3641 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3646 if (SwitchType == TypeManager.string_type && !is_constant) {
3647 // TODO: Optimize single case, and single+default case
3648 ResolveStringSwitchMap (ec);
3654 void ResolveStringSwitchMap (EmitContext ec)
3656 FullNamedExpression string_dictionary_type;
3658 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3659 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3661 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3662 new TypeArguments (loc,
3663 new TypeExpression (TypeManager.string_type, loc),
3664 new TypeExpression (TypeManager.int32_type, loc)), loc);
3666 MemberAccess system_collections_generic = new MemberAccess (
3667 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3669 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3671 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3672 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3673 CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), null, loc);
3674 if (!field.Define ())
3676 ec.TypeContainer.PartialContainer.AddField (field);
3678 ArrayList init = new ArrayList ();
3681 string value = null;
3682 foreach (SwitchSection section in Sections) {
3683 foreach (SwitchLabel sl in section.Labels) {
3684 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3689 value = (string) sl.Converted;
3690 ArrayList init_args = new ArrayList (2);
3691 init_args.Add (new StringLiteral (value, sl.Location));
3692 init_args.Add (new IntConstant (counter, loc));
3693 init.Add (new CollectionElementInitializer (init_args, loc));
3699 Elements.Add (counter, section.Labels [0]);
3703 ArrayList args = new ArrayList (1);
3704 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3705 Expression initializer = new NewInitialize (string_dictionary_type, args,
3706 new CollectionOrObjectInitializers (init, loc), loc);
3708 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3709 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3712 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3714 ILGenerator ig = ec.ig;
3715 Label l_initialized = ig.DefineLabel ();
3718 // Skip initialization when value is null
3720 value.EmitBranchable (ec, null_target, false);
3723 // Check if string dictionary is initialized and initialize
3725 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3726 string_dictionary.EmitStatement (ec);
3727 ig.MarkLabel (l_initialized);
3729 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3732 ArrayList get_value_args = new ArrayList (2);
3733 get_value_args.Add (new Argument (value));
3734 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3735 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3736 if (get_item == null)
3740 // A value was not found, go to default case
3742 get_item.EmitBranchable (ec, default_target, false);
3744 ArrayList get_value_args = new ArrayList (1);
3745 get_value_args.Add (value);
3747 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3748 if (get_item == null)
3751 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3752 get_item_object.EmitAssign (ec, get_item, true, false);
3753 ec.ig.Emit (OpCodes.Brfalse, default_target);
3755 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3756 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3758 get_item_int.EmitStatement (ec);
3759 get_item_object.Release (ec);
3761 TableSwitchEmit (ec, string_switch_variable);
3762 string_switch_variable.Release (ec);
3765 protected override void DoEmit (EmitContext ec)
3767 ILGenerator ig = ec.ig;
3769 default_target = ig.DefineLabel ();
3770 null_target = ig.DefineLabel ();
3772 // Store variable for comparission purposes
3773 // TODO: Don't duplicate non-captured VariableReference
3774 LocalTemporary value;
3776 value = new LocalTemporary (SwitchType);
3778 unwrap.EmitCheck (ec);
3779 ig.Emit (OpCodes.Brfalse, null_target);
3783 } else if (!is_constant) {
3784 value = new LocalTemporary (SwitchType);
3791 // Setup the codegen context
3793 Label old_end = ec.LoopEnd;
3794 Switch old_switch = ec.Switch;
3796 ec.LoopEnd = ig.DefineLabel ();
3801 if (constant_section != null)
3802 constant_section.Block.Emit (ec);
3803 } else if (string_dictionary != null) {
3804 DoEmitStringSwitch (value, ec);
3806 TableSwitchEmit (ec, value);
3812 // Restore context state.
3813 ig.MarkLabel (ec.LoopEnd);
3816 // Restore the previous context
3818 ec.LoopEnd = old_end;
3819 ec.Switch = old_switch;
3822 protected override void CloneTo (CloneContext clonectx, Statement t)
3824 Switch target = (Switch) t;
3826 target.Expr = Expr.Clone (clonectx);
3827 target.Sections = new ArrayList ();
3828 foreach (SwitchSection ss in Sections){
3829 target.Sections.Add (ss.Clone (clonectx));
3834 // A place where execution can restart in an iterator
3835 public abstract class ResumableStatement : Statement
3838 protected Label resume_point;
3840 public Label PrepareForEmit (EmitContext ec)
3844 resume_point = ec.ig.DefineLabel ();
3846 return resume_point;
3849 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3853 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3858 // Base class for statements that are implemented in terms of try...finally
3859 public abstract class ExceptionStatement : ResumableStatement
3863 protected abstract void EmitPreTryBody (EmitContext ec);
3864 protected abstract void EmitTryBody (EmitContext ec);
3865 protected abstract void EmitFinallyBody (EmitContext ec);
3867 protected sealed override void DoEmit (EmitContext ec)
3869 ILGenerator ig = ec.ig;
3871 EmitPreTryBody (ec);
3873 if (resume_points != null) {
3874 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3875 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3878 ig.BeginExceptionBlock ();
3880 if (resume_points != null) {
3881 ig.MarkLabel (resume_point);
3883 // For normal control flow, we want to fall-through the Switch
3884 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3885 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3886 IntConstant.EmitInt (ig, first_resume_pc);
3887 ig.Emit (OpCodes.Sub);
3889 Label [] labels = new Label [resume_points.Count];
3890 for (int i = 0; i < resume_points.Count; ++i)
3891 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3892 ig.Emit (OpCodes.Switch, labels);
3897 ig.BeginFinallyBlock ();
3899 Label start_finally = ec.ig.DefineLabel ();
3900 if (resume_points != null) {
3901 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3902 ig.Emit (OpCodes.Brfalse_S, start_finally);
3903 ig.Emit (OpCodes.Endfinally);
3906 ig.MarkLabel (start_finally);
3907 EmitFinallyBody (ec);
3909 ig.EndExceptionBlock ();
3912 public void SomeCodeFollows ()
3914 code_follows = true;
3917 protected void ResolveReachability (EmitContext ec)
3919 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3920 // So, ensure there's some IL code after this statement.
3921 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3922 ec.NeedReturnLabel ();
3926 ArrayList resume_points;
3927 int first_resume_pc;
3928 public void AddResumePoint (ResumableStatement stmt, int pc)
3930 if (resume_points == null) {
3931 resume_points = new ArrayList ();
3932 first_resume_pc = pc;
3935 if (pc != first_resume_pc + resume_points.Count)
3936 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3938 resume_points.Add (stmt);
3941 Label dispose_try_block;
3942 bool prepared_for_dispose, emitted_dispose;
3943 public override Label PrepareForDispose (EmitContext ec, Label end)
3945 if (!prepared_for_dispose) {
3946 prepared_for_dispose = true;
3947 dispose_try_block = ec.ig.DefineLabel ();
3949 return dispose_try_block;
3952 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3954 if (emitted_dispose)
3957 emitted_dispose = true;
3959 ILGenerator ig = ec.ig;
3961 Label end_of_try = ig.DefineLabel ();
3963 // Ensure that the only way we can get into this code is through a dispatcher
3964 if (have_dispatcher)
3965 ig.Emit (OpCodes.Br, end);
3967 ig.BeginExceptionBlock ();
3969 ig.MarkLabel (dispose_try_block);
3971 Label [] labels = null;
3972 for (int i = 0; i < resume_points.Count; ++i) {
3973 ResumableStatement s = (ResumableStatement) resume_points [i];
3974 Label ret = s.PrepareForDispose (ec, end_of_try);
3975 if (ret.Equals (end_of_try) && labels == null)
3977 if (labels == null) {
3978 labels = new Label [resume_points.Count];
3979 for (int j = 0; j < i; ++j)
3980 labels [j] = end_of_try;
3985 if (labels != null) {
3987 for (j = 1; j < labels.Length; ++j)
3988 if (!labels [0].Equals (labels [j]))
3990 bool emit_dispatcher = j < labels.Length;
3992 if (emit_dispatcher) {
3993 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3994 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3995 IntConstant.EmitInt (ig, first_resume_pc);
3996 ig.Emit (OpCodes.Sub);
3997 ig.Emit (OpCodes.Switch, labels);
3998 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4001 foreach (ResumableStatement s in resume_points)
4002 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4005 ig.MarkLabel (end_of_try);
4007 ig.BeginFinallyBlock ();
4009 EmitFinallyBody (ec);
4011 ig.EndExceptionBlock ();
4015 public class Lock : ExceptionStatement {
4017 public Statement Statement;
4018 TemporaryVariable temp;
4020 public Lock (Expression expr, Statement stmt, Location l)
4027 public override bool Resolve (EmitContext ec)
4029 expr = expr.Resolve (ec);
4033 if (expr.Type.IsValueType){
4034 Report.Error (185, loc,
4035 "`{0}' is not a reference type as required by the lock statement",
4036 TypeManager.CSharpName (expr.Type));
4040 ec.StartFlowBranching (this);
4041 bool ok = Statement.Resolve (ec);
4042 ec.EndFlowBranching ();
4044 ResolveReachability (ec);
4046 // Avoid creating libraries that reference the internal
4049 if (t == TypeManager.null_type)
4050 t = TypeManager.object_type;
4052 temp = new TemporaryVariable (t, loc);
4055 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4056 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4057 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4058 monitor_type, "Enter", loc, TypeManager.object_type);
4059 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4060 monitor_type, "Exit", loc, TypeManager.object_type);
4066 protected override void EmitPreTryBody (EmitContext ec)
4068 ILGenerator ig = ec.ig;
4070 temp.EmitAssign (ec, expr);
4072 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4075 protected override void EmitTryBody (EmitContext ec)
4077 Statement.Emit (ec);
4080 protected override void EmitFinallyBody (EmitContext ec)
4083 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4086 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4088 expr.MutateHoistedGenericType (storey);
4089 temp.MutateHoistedGenericType (storey);
4090 Statement.MutateHoistedGenericType (storey);
4093 protected override void CloneTo (CloneContext clonectx, Statement t)
4095 Lock target = (Lock) t;
4097 target.expr = expr.Clone (clonectx);
4098 target.Statement = Statement.Clone (clonectx);
4102 public class Unchecked : Statement {
4105 public Unchecked (Block b)
4111 public override bool Resolve (EmitContext ec)
4113 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4114 return Block.Resolve (ec);
4117 protected override void DoEmit (EmitContext ec)
4119 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4123 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4125 Block.MutateHoistedGenericType (storey);
4128 protected override void CloneTo (CloneContext clonectx, Statement t)
4130 Unchecked target = (Unchecked) t;
4132 target.Block = clonectx.LookupBlock (Block);
4136 public class Checked : Statement {
4139 public Checked (Block b)
4142 b.Unchecked = false;
4145 public override bool Resolve (EmitContext ec)
4147 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4148 return Block.Resolve (ec);
4151 protected override void DoEmit (EmitContext ec)
4153 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4157 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4159 Block.MutateHoistedGenericType (storey);
4162 protected override void CloneTo (CloneContext clonectx, Statement t)
4164 Checked target = (Checked) t;
4166 target.Block = clonectx.LookupBlock (Block);
4170 public class Unsafe : Statement {
4173 public Unsafe (Block b)
4176 Block.Unsafe = true;
4179 public override bool Resolve (EmitContext ec)
4181 using (ec.With (EmitContext.Flags.InUnsafe, true))
4182 return Block.Resolve (ec);
4185 protected override void DoEmit (EmitContext ec)
4187 using (ec.With (EmitContext.Flags.InUnsafe, true))
4191 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4193 Block.MutateHoistedGenericType (storey);
4196 protected override void CloneTo (CloneContext clonectx, Statement t)
4198 Unsafe target = (Unsafe) t;
4200 target.Block = clonectx.LookupBlock (Block);
4207 public class Fixed : Statement {
4209 ArrayList declarators;
4210 Statement statement;
4215 abstract class Emitter
4217 protected LocalInfo vi;
4218 protected Expression converted;
4220 protected Emitter (Expression expr, LocalInfo li)
4226 public abstract void Emit (EmitContext ec);
4227 public abstract void EmitExit (EmitContext ec);
4230 class ExpressionEmitter : Emitter {
4231 public ExpressionEmitter (Expression converted, LocalInfo li) :
4232 base (converted, li)
4236 public override void Emit (EmitContext ec) {
4238 // Store pointer in pinned location
4240 converted.Emit (ec);
4244 public override void EmitExit (EmitContext ec)
4246 ec.ig.Emit (OpCodes.Ldc_I4_0);
4247 ec.ig.Emit (OpCodes.Conv_U);
4252 class StringEmitter : Emitter {
4253 class StringPtr : Expression
4257 public StringPtr (LocalBuilder b, Location l)
4260 eclass = ExprClass.Value;
4261 type = TypeManager.char_ptr_type;
4265 public override Expression CreateExpressionTree (EmitContext ec)
4267 throw new NotSupportedException ("ET");
4270 public override Expression DoResolve (EmitContext ec)
4272 // This should never be invoked, we are born in fully
4273 // initialized state.
4278 public override void Emit (EmitContext ec)
4280 if (TypeManager.int_get_offset_to_string_data == null) {
4281 // TODO: Move to resolve !!
4282 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4283 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4286 ILGenerator ig = ec.ig;
4288 ig.Emit (OpCodes.Ldloc, b);
4289 ig.Emit (OpCodes.Conv_I);
4290 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4291 ig.Emit (OpCodes.Add);
4295 LocalBuilder pinned_string;
4298 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4304 public override void Emit (EmitContext ec)
4306 ILGenerator ig = ec.ig;
4307 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4309 converted.Emit (ec);
4310 ig.Emit (OpCodes.Stloc, pinned_string);
4312 Expression sptr = new StringPtr (pinned_string, loc);
4313 converted = Convert.ImplicitConversionRequired (
4314 ec, sptr, vi.VariableType, loc);
4316 if (converted == null)
4319 converted.Emit (ec);
4323 public override void EmitExit (EmitContext ec)
4325 ec.ig.Emit (OpCodes.Ldnull);
4326 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4330 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4333 declarators = decls;
4338 public Statement Statement {
4339 get { return statement; }
4342 public override bool Resolve (EmitContext ec)
4345 Expression.UnsafeError (loc);
4349 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4350 if (texpr == null) {
4351 if (type is VarExpr)
4352 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4357 expr_type = texpr.Type;
4359 data = new Emitter [declarators.Count];
4361 if (!expr_type.IsPointer){
4362 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4367 foreach (Pair p in declarators){
4368 LocalInfo vi = (LocalInfo) p.First;
4369 Expression e = (Expression) p.Second;
4371 vi.VariableInfo.SetAssigned (ec);
4372 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4375 // The rules for the possible declarators are pretty wise,
4376 // but the production on the grammar is more concise.
4378 // So we have to enforce these rules here.
4380 // We do not resolve before doing the case 1 test,
4381 // because the grammar is explicit in that the token &
4382 // is present, so we need to test for this particular case.
4386 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4391 // Case 1: & object.
4393 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4394 Expression child = ((Unary) e).Expr;
4396 if (child is ParameterReference || child is LocalVariableReference){
4399 "No need to use fixed statement for parameters or " +
4400 "local variable declarations (address is already " +
4405 ec.InFixedInitializer = true;
4407 ec.InFixedInitializer = false;
4411 child = ((Unary) e).Expr;
4413 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4416 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4417 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4421 data [i] = new ExpressionEmitter (e, vi);
4427 ec.InFixedInitializer = true;
4429 ec.InFixedInitializer = false;
4436 if (e.Type.IsArray){
4437 Type array_type = TypeManager.GetElementType (e.Type);
4440 // Provided that array_type is unmanaged,
4442 if (!TypeManager.VerifyUnManaged (array_type, loc))
4446 // and T* is implicitly convertible to the
4447 // pointer type given in the fixed statement.
4449 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4451 Expression converted = Convert.ImplicitConversionRequired (
4452 ec, array_ptr, vi.VariableType, loc);
4453 if (converted == null)
4457 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4459 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4460 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4461 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4465 converted = converted.Resolve (ec);
4467 data [i] = new ExpressionEmitter (converted, vi);
4476 if (e.Type == TypeManager.string_type){
4477 data [i] = new StringEmitter (e, vi, loc);
4482 // Case 4: fixed buffer
4483 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4484 if (fixed_buffer_ptr != null) {
4485 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4490 // For other cases, flag a `this is already fixed expression'
4492 if (e is LocalVariableReference || e is ParameterReference ||
4493 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4495 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4499 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4503 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4504 bool ok = statement.Resolve (ec);
4505 bool flow_unreachable = ec.EndFlowBranching ();
4506 has_ret = flow_unreachable;
4511 protected override void DoEmit (EmitContext ec)
4513 for (int i = 0; i < data.Length; i++) {
4517 statement.Emit (ec);
4523 // Clear the pinned variable
4525 for (int i = 0; i < data.Length; i++) {
4526 data [i].EmitExit (ec);
4530 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4532 // Fixed statement cannot be used inside anonymous methods or lambdas
4533 throw new NotSupportedException ();
4536 protected override void CloneTo (CloneContext clonectx, Statement t)
4538 Fixed target = (Fixed) t;
4540 target.type = type.Clone (clonectx);
4541 target.declarators = new ArrayList (declarators.Count);
4542 foreach (Pair p in declarators) {
4543 LocalInfo vi = (LocalInfo) p.First;
4544 Expression e = (Expression) p.Second;
4546 target.declarators.Add (
4547 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4550 target.statement = statement.Clone (clonectx);
4554 public class Catch : Statement {
4555 public readonly string Name;
4557 public Block VarBlock;
4559 Expression type_expr;
4562 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4567 VarBlock = var_block;
4571 public Type CatchType {
4577 public bool IsGeneral {
4579 return type_expr == null;
4583 protected override void DoEmit (EmitContext ec)
4585 ILGenerator ig = ec.ig;
4587 if (CatchType != null)
4588 ig.BeginCatchBlock (CatchType);
4590 ig.BeginCatchBlock (TypeManager.object_type);
4592 if (VarBlock != null)
4596 // TODO: Move to resolve
4597 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4601 if (lvr.IsHoisted) {
4602 LocalTemporary lt = new LocalTemporary (lvr.Type);
4606 // Variable is at the top of the stack
4607 source = EmptyExpression.Null;
4610 lvr.EmitAssign (ec, source, false, false);
4612 ig.Emit (OpCodes.Pop);
4617 public override bool Resolve (EmitContext ec)
4619 using (ec.With (EmitContext.Flags.InCatch, true)) {
4620 if (type_expr != null) {
4621 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4627 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4628 Error (155, "The type caught or thrown must be derived from System.Exception");
4634 if (!Block.Resolve (ec))
4637 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4638 // emit the "unused variable" warnings.
4639 if (VarBlock != null)
4640 return VarBlock.Resolve (ec);
4646 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4649 type = storey.MutateType (type);
4650 if (VarBlock != null)
4651 VarBlock.MutateHoistedGenericType (storey);
4652 Block.MutateHoistedGenericType (storey);
4655 protected override void CloneTo (CloneContext clonectx, Statement t)
4657 Catch target = (Catch) t;
4659 if (type_expr != null)
4660 target.type_expr = type_expr.Clone (clonectx);
4661 if (VarBlock != null)
4662 target.VarBlock = clonectx.LookupBlock (VarBlock);
4663 target.Block = clonectx.LookupBlock (Block);
4667 public class TryFinally : ExceptionStatement {
4671 public TryFinally (Statement stmt, Block fini, Location l)
4678 public override bool Resolve (EmitContext ec)
4682 ec.StartFlowBranching (this);
4684 if (!stmt.Resolve (ec))
4688 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4689 using (ec.With (EmitContext.Flags.InFinally, true)) {
4690 if (!fini.Resolve (ec))
4694 ec.EndFlowBranching ();
4696 ResolveReachability (ec);
4701 protected override void EmitPreTryBody (EmitContext ec)
4705 protected override void EmitTryBody (EmitContext ec)
4710 protected override void EmitFinallyBody (EmitContext ec)
4715 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4717 stmt.MutateHoistedGenericType (storey);
4718 fini.MutateHoistedGenericType (storey);
4721 protected override void CloneTo (CloneContext clonectx, Statement t)
4723 TryFinally target = (TryFinally) t;
4725 target.stmt = (Statement) stmt.Clone (clonectx);
4727 target.fini = clonectx.LookupBlock (fini);
4731 public class TryCatch : Statement {
4733 public ArrayList Specific;
4734 public Catch General;
4735 bool inside_try_finally, code_follows;
4737 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4740 this.Specific = catch_clauses;
4741 this.General = null;
4742 this.inside_try_finally = inside_try_finally;
4744 for (int i = 0; i < catch_clauses.Count; ++i) {
4745 Catch c = (Catch) catch_clauses [i];
4747 if (i != catch_clauses.Count - 1)
4748 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4750 catch_clauses.RemoveAt (i);
4758 public override bool Resolve (EmitContext ec)
4762 ec.StartFlowBranching (this);
4764 if (!Block.Resolve (ec))
4767 Type[] prev_catches = new Type [Specific.Count];
4769 foreach (Catch c in Specific){
4770 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4772 if (c.Name != null) {
4773 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4775 throw new Exception ();
4777 vi.VariableInfo = null;
4780 if (!c.Resolve (ec))
4783 Type resolved_type = c.CatchType;
4784 for (int ii = 0; ii < last_index; ++ii) {
4785 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4786 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4791 prev_catches [last_index++] = resolved_type;
4794 if (General != null) {
4795 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4796 foreach (Catch c in Specific){
4797 if (c.CatchType == TypeManager.exception_type) {
4798 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'");
4803 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4805 if (!General.Resolve (ec))
4809 ec.EndFlowBranching ();
4811 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4812 // So, ensure there's some IL code after this statement
4813 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4814 ec.NeedReturnLabel ();
4819 public void SomeCodeFollows ()
4821 code_follows = true;
4824 protected override void DoEmit (EmitContext ec)
4826 ILGenerator ig = ec.ig;
4828 if (!inside_try_finally)
4829 ig.BeginExceptionBlock ();
4833 foreach (Catch c in Specific)
4836 if (General != null)
4839 if (!inside_try_finally)
4840 ig.EndExceptionBlock ();
4843 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4845 Block.MutateHoistedGenericType (storey);
4847 if (General != null)
4848 General.MutateHoistedGenericType (storey);
4849 if (Specific != null) {
4850 foreach (Catch c in Specific)
4851 c.MutateHoistedGenericType (storey);
4855 protected override void CloneTo (CloneContext clonectx, Statement t)
4857 TryCatch target = (TryCatch) t;
4859 target.Block = clonectx.LookupBlock (Block);
4860 if (General != null)
4861 target.General = (Catch) General.Clone (clonectx);
4862 if (Specific != null){
4863 target.Specific = new ArrayList ();
4864 foreach (Catch c in Specific)
4865 target.Specific.Add (c.Clone (clonectx));
4870 public class UsingTemporary : ExceptionStatement {
4871 TemporaryVariable local_copy;
4872 public Statement Statement;
4876 public UsingTemporary (Expression expr, Statement stmt, Location l)
4883 public override bool Resolve (EmitContext ec)
4885 expr = expr.Resolve (ec);
4889 expr_type = expr.Type;
4891 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4892 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4893 Using.Error_IsNotConvertibleToIDisposable (expr);
4898 local_copy = new TemporaryVariable (expr_type, loc);
4899 local_copy.Resolve (ec);
4901 ec.StartFlowBranching (this);
4903 bool ok = Statement.Resolve (ec);
4905 ec.EndFlowBranching ();
4907 ResolveReachability (ec);
4909 if (TypeManager.void_dispose_void == null) {
4910 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4911 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4917 protected override void EmitPreTryBody (EmitContext ec)
4919 local_copy.EmitAssign (ec, expr);
4922 protected override void EmitTryBody (EmitContext ec)
4924 Statement.Emit (ec);
4927 protected override void EmitFinallyBody (EmitContext ec)
4929 ILGenerator ig = ec.ig;
4930 if (!expr_type.IsValueType) {
4931 Label skip = ig.DefineLabel ();
4932 local_copy.Emit (ec);
4933 ig.Emit (OpCodes.Brfalse, skip);
4934 local_copy.Emit (ec);
4935 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4936 ig.MarkLabel (skip);
4940 Expression ml = Expression.MemberLookup (
4941 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4942 "Dispose", Location.Null);
4944 if (!(ml is MethodGroupExpr)) {
4945 local_copy.Emit (ec);
4946 ig.Emit (OpCodes.Box, expr_type);
4947 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4951 MethodInfo mi = null;
4953 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4954 if (TypeManager.GetParameterData (mk).Count == 0) {
4961 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4965 local_copy.AddressOf (ec, AddressOp.Load);
4966 ig.Emit (OpCodes.Call, mi);
4969 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4971 expr_type = storey.MutateType (expr_type);
4972 local_copy.MutateHoistedGenericType (storey);
4973 Statement.MutateHoistedGenericType (storey);
4976 protected override void CloneTo (CloneContext clonectx, Statement t)
4978 UsingTemporary target = (UsingTemporary) t;
4980 target.expr = expr.Clone (clonectx);
4981 target.Statement = Statement.Clone (clonectx);
4985 public class Using : ExceptionStatement {
4987 public Statement EmbeddedStatement {
4988 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4994 Expression converted_var;
4995 ExpressionStatement assign;
4997 public Using (Expression var, Expression init, Statement stmt, Location l)
5005 bool ResolveVariable (EmitContext ec)
5007 ExpressionStatement a = new SimpleAssign (var, init, loc);
5008 a = a.ResolveStatement (ec);
5014 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
5015 converted_var = var;
5019 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
5021 Error_IsNotConvertibleToIDisposable (var);
5030 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5032 Report.SymbolRelatedToPreviousError (expr.Type);
5033 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5034 expr.GetSignatureForError ());
5037 protected override void EmitPreTryBody (EmitContext ec)
5039 assign.EmitStatement (ec);
5042 protected override void EmitTryBody (EmitContext ec)
5047 protected override void EmitFinallyBody (EmitContext ec)
5049 ILGenerator ig = ec.ig;
5051 if (!var.Type.IsValueType) {
5052 Label skip = ig.DefineLabel ();
5054 ig.Emit (OpCodes.Brfalse, skip);
5055 converted_var.Emit (ec);
5056 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5057 ig.MarkLabel (skip);
5059 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5061 if (!(ml is MethodGroupExpr)) {
5063 ig.Emit (OpCodes.Box, var.Type);
5064 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5066 MethodInfo mi = null;
5068 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5069 if (TypeManager.GetParameterData (mk).Count == 0) {
5076 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5080 IMemoryLocation mloc = (IMemoryLocation) var;
5082 mloc.AddressOf (ec, AddressOp.Load);
5083 ig.Emit (OpCodes.Call, mi);
5088 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5090 assign.MutateHoistedGenericType (storey);
5091 var.MutateHoistedGenericType (storey);
5092 stmt.MutateHoistedGenericType (storey);
5095 public override bool Resolve (EmitContext ec)
5097 if (!ResolveVariable (ec))
5100 ec.StartFlowBranching (this);
5102 bool ok = stmt.Resolve (ec);
5104 ec.EndFlowBranching ();
5106 ResolveReachability (ec);
5108 if (TypeManager.void_dispose_void == null) {
5109 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5110 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5116 protected override void CloneTo (CloneContext clonectx, Statement t)
5118 Using target = (Using) t;
5120 target.var = var.Clone (clonectx);
5121 target.init = init.Clone (clonectx);
5122 target.stmt = stmt.Clone (clonectx);
5127 /// Implementation of the foreach C# statement
5129 public class Foreach : Statement {
5131 Expression variable;
5133 Statement statement;
5135 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5136 Statement stmt, Location l)
5139 this.variable = var;
5145 public Statement Statement {
5146 get { return statement; }
5149 public override bool Resolve (EmitContext ec)
5151 expr = expr.Resolve (ec);
5156 Report.Error (186, loc, "Use of null is not valid in this context");
5160 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5161 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5162 expr.ExprClassName);
5166 if (expr.Type.IsArray) {
5167 statement = new ArrayForeach (type, variable, expr, statement, loc);
5169 statement = new CollectionForeach (type, variable, expr, statement, loc);
5172 return statement.Resolve (ec);
5175 protected override void DoEmit (EmitContext ec)
5177 ILGenerator ig = ec.ig;
5179 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5180 ec.LoopBegin = ig.DefineLabel ();
5181 ec.LoopEnd = ig.DefineLabel ();
5183 statement.Emit (ec);
5185 ec.LoopBegin = old_begin;
5186 ec.LoopEnd = old_end;
5189 protected class ArrayCounter : TemporaryVariable
5191 StatementExpression increment;
5193 public ArrayCounter (Location loc)
5194 : base (TypeManager.int32_type, loc)
5198 public void ResolveIncrement (EmitContext ec)
5200 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5201 increment.Resolve (ec);
5204 public void EmitIncrement (EmitContext ec)
5206 increment.Emit (ec);
5210 protected class ArrayForeach : Statement
5212 Expression variable, expr, conv;
5213 Statement statement;
5215 Expression var_type;
5216 TemporaryVariable[] lengths;
5217 ArrayCounter[] counter;
5220 TemporaryVariable copy;
5222 Expression[] length_exprs;
5224 public ArrayForeach (Expression var_type, Expression var,
5225 Expression expr, Statement stmt, Location l)
5227 this.var_type = var_type;
5228 this.variable = var;
5234 protected override void CloneTo (CloneContext clonectx, Statement target)
5236 throw new NotImplementedException ();
5239 public override bool Resolve (EmitContext ec)
5241 array_type = expr.Type;
5242 rank = array_type.GetArrayRank ();
5244 copy = new TemporaryVariable (array_type, loc);
5247 counter = new ArrayCounter [rank];
5248 lengths = new TemporaryVariable [rank];
5249 length_exprs = new Expression [rank];
5251 ArrayList list = new ArrayList (rank);
5252 for (int i = 0; i < rank; i++) {
5253 counter [i] = new ArrayCounter (loc);
5254 counter [i].ResolveIncrement (ec);
5256 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5257 lengths [i].Resolve (ec);
5260 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5262 ArrayList args = new ArrayList (1);
5263 args.Add (new Argument (new IntConstant (i, loc)));
5264 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5267 list.Add (counter [i]);
5270 access = new ElementAccess (copy, list).Resolve (ec);
5274 VarExpr ve = var_type as VarExpr;
5276 // Infer implicitly typed local variable from foreach array type
5277 var_type = new TypeExpression (access.Type, ve.Location);
5280 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5281 if (var_type == null)
5284 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5290 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5291 ec.CurrentBranching.CreateSibling ();
5293 variable = variable.ResolveLValue (ec, conv, loc);
5294 if (variable == null)
5297 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5298 if (!statement.Resolve (ec))
5300 ec.EndFlowBranching ();
5302 // There's no direct control flow from the end of the embedded statement to the end of the loop
5303 ec.CurrentBranching.CurrentUsageVector.Goto ();
5305 ec.EndFlowBranching ();
5310 protected override void DoEmit (EmitContext ec)
5312 ILGenerator ig = ec.ig;
5314 copy.EmitAssign (ec, expr);
5316 Label[] test = new Label [rank];
5317 Label[] loop = new Label [rank];
5319 for (int i = 0; i < rank; i++) {
5320 test [i] = ig.DefineLabel ();
5321 loop [i] = ig.DefineLabel ();
5323 lengths [i].EmitAssign (ec, length_exprs [i]);
5326 IntConstant zero = new IntConstant (0, loc);
5327 for (int i = 0; i < rank; i++) {
5328 counter [i].EmitAssign (ec, zero);
5330 ig.Emit (OpCodes.Br, test [i]);
5331 ig.MarkLabel (loop [i]);
5334 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5336 statement.Emit (ec);
5338 ig.MarkLabel (ec.LoopBegin);
5340 for (int i = rank - 1; i >= 0; i--){
5341 counter [i].EmitIncrement (ec);
5343 ig.MarkLabel (test [i]);
5344 counter [i].Emit (ec);
5345 lengths [i].Emit (ec);
5346 ig.Emit (OpCodes.Blt, loop [i]);
5349 ig.MarkLabel (ec.LoopEnd);
5352 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5354 copy.MutateHoistedGenericType (storey);
5355 conv.MutateHoistedGenericType (storey);
5356 variable.MutateHoistedGenericType (storey);
5357 statement.MutateHoistedGenericType (storey);
5359 for (int i = 0; i < rank; i++) {
5360 counter [i].MutateHoistedGenericType (storey);
5361 lengths [i].MutateHoistedGenericType (storey);
5366 protected class CollectionForeach : Statement
5368 Expression variable, expr;
5369 Statement statement;
5371 TemporaryVariable enumerator;
5376 MethodGroupExpr get_enumerator;
5377 PropertyExpr get_current;
5378 MethodInfo move_next;
5379 Expression var_type;
5380 Type enumerator_type;
5381 bool enumerator_found;
5383 public CollectionForeach (Expression var_type, Expression var,
5384 Expression expr, Statement stmt, Location l)
5386 this.var_type = var_type;
5387 this.variable = var;
5393 protected override void CloneTo (CloneContext clonectx, Statement target)
5395 throw new NotImplementedException ();
5398 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5400 Type return_type = mi.ReturnType;
5402 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5404 // Apply the same optimization as MS: skip the GetEnumerator
5405 // returning an IEnumerator, and use the one returning a
5406 // CharEnumerator instead. This allows us to avoid the
5407 // try-finally block and the boxing.
5412 // Ok, we can access it, now make sure that we can do something
5413 // with this `GetEnumerator'
5416 if (return_type == TypeManager.ienumerator_type ||
5417 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5418 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5420 // If it is not an interface, lets try to find the methods ourselves.
5421 // For example, if we have:
5422 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5423 // We can avoid the iface call. This is a runtime perf boost.
5424 // even bigger if we have a ValueType, because we avoid the cost
5427 // We have to make sure that both methods exist for us to take
5428 // this path. If one of the methods does not exist, we will just
5429 // use the interface. Sadly, this complex if statement is the only
5430 // way I could do this without a goto
5433 if (TypeManager.bool_movenext_void == null) {
5434 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5435 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5438 if (TypeManager.ienumerator_getcurrent == null) {
5439 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5440 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5445 // Prefer a generic enumerator over a non-generic one.
5447 if (return_type.IsInterface && return_type.IsGenericType) {
5448 enumerator_type = return_type;
5449 if (!FetchGetCurrent (ec, return_type))
5450 get_current = new PropertyExpr (
5451 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5452 if (!FetchMoveNext (return_type))
5453 move_next = TypeManager.bool_movenext_void;
5458 if (return_type.IsInterface ||
5459 !FetchMoveNext (return_type) ||
5460 !FetchGetCurrent (ec, return_type)) {
5461 enumerator_type = return_type;
5462 move_next = TypeManager.bool_movenext_void;
5463 get_current = new PropertyExpr (
5464 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5469 // Ok, so they dont return an IEnumerable, we will have to
5470 // find if they support the GetEnumerator pattern.
5473 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5474 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",
5475 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5480 enumerator_type = return_type;
5486 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5488 bool FetchMoveNext (Type t)
5490 MemberList move_next_list;
5492 move_next_list = TypeContainer.FindMembers (
5493 t, MemberTypes.Method,
5494 BindingFlags.Public | BindingFlags.Instance,
5495 Type.FilterName, "MoveNext");
5496 if (move_next_list.Count == 0)
5499 foreach (MemberInfo m in move_next_list){
5500 MethodInfo mi = (MethodInfo) m;
5502 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5503 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5513 // Retrieves a `public T get_Current ()' method from the Type `t'
5515 bool FetchGetCurrent (EmitContext ec, Type t)
5517 PropertyExpr pe = Expression.MemberLookup (
5518 ec.ContainerType, t, "Current", MemberTypes.Property,
5519 Expression.AllBindingFlags, loc) as PropertyExpr;
5528 // Retrieves a `public void Dispose ()' method from the Type `t'
5530 static MethodInfo FetchMethodDispose (Type t)
5532 MemberList dispose_list;
5534 dispose_list = TypeContainer.FindMembers (
5535 t, MemberTypes.Method,
5536 BindingFlags.Public | BindingFlags.Instance,
5537 Type.FilterName, "Dispose");
5538 if (dispose_list.Count == 0)
5541 foreach (MemberInfo m in dispose_list){
5542 MethodInfo mi = (MethodInfo) m;
5544 if (TypeManager.GetParameterData (mi).Count == 0){
5545 if (mi.ReturnType == TypeManager.void_type)
5552 void Error_Enumerator ()
5554 if (enumerator_found) {
5558 Report.Error (1579, loc,
5559 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5560 TypeManager.CSharpName (expr.Type));
5563 bool IsOverride (MethodInfo m)
5565 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5567 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5569 if (m is MethodBuilder)
5572 MethodInfo base_method = m.GetBaseDefinition ();
5573 return base_method != m;
5576 bool TryType (EmitContext ec, Type t)
5578 MethodGroupExpr mg = Expression.MemberLookup (
5579 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5580 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5584 MethodInfo result = null;
5585 MethodInfo tmp_move_next = null;
5586 PropertyExpr tmp_get_cur = null;
5587 Type tmp_enumerator_type = enumerator_type;
5588 foreach (MethodInfo mi in mg.Methods) {
5589 if (TypeManager.GetParameterData (mi).Count != 0)
5592 // Check whether GetEnumerator is public
5593 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5596 if (IsOverride (mi))
5599 enumerator_found = true;
5601 if (!GetEnumeratorFilter (ec, mi))
5604 if (result != null) {
5605 if (TypeManager.IsGenericType (result.ReturnType)) {
5606 if (!TypeManager.IsGenericType (mi.ReturnType))
5609 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5610 Report.SymbolRelatedToPreviousError (t);
5611 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5612 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5613 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5617 // Always prefer generics enumerators
5618 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5619 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5620 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5623 Report.SymbolRelatedToPreviousError (result);
5624 Report.SymbolRelatedToPreviousError (mi);
5625 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5626 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5631 tmp_move_next = move_next;
5632 tmp_get_cur = get_current;
5633 tmp_enumerator_type = enumerator_type;
5634 if (mi.DeclaringType == t)
5638 if (result != null) {
5639 move_next = tmp_move_next;
5640 get_current = tmp_get_cur;
5641 enumerator_type = tmp_enumerator_type;
5642 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5643 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5645 if (t != expr.Type) {
5646 expr = Convert.ExplicitConversion (
5649 throw new InternalErrorException ();
5652 get_enumerator.InstanceExpression = expr;
5653 get_enumerator.IsBase = t != expr.Type;
5661 bool ProbeCollectionType (EmitContext ec, Type t)
5663 int errors = Report.Errors;
5664 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5665 if (TryType (ec, tt))
5670 if (Report.Errors > errors)
5674 // Now try to find the method in the interfaces
5676 Type [] ifaces = TypeManager.GetInterfaces (t);
5677 foreach (Type i in ifaces){
5678 if (TryType (ec, i))
5685 public override bool Resolve (EmitContext ec)
5687 enumerator_type = TypeManager.ienumerator_type;
5689 if (!ProbeCollectionType (ec, expr.Type)) {
5690 Error_Enumerator ();
5694 bool is_disposable = !enumerator_type.IsSealed ||
5695 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5697 VarExpr ve = var_type as VarExpr;
5699 // Infer implicitly typed local variable from foreach enumerable type
5700 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5703 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5704 if (var_type == null)
5707 enumerator = new TemporaryVariable (enumerator_type, loc);
5708 enumerator.Resolve (ec);
5710 init = new Invocation (get_enumerator, null);
5711 init = init.Resolve (ec);
5715 Expression move_next_expr;
5717 MemberInfo[] mi = new MemberInfo[] { move_next };
5718 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5719 mg.InstanceExpression = enumerator;
5721 move_next_expr = new Invocation (mg, null);
5724 get_current.InstanceExpression = enumerator;
5726 Statement block = new CollectionForeachStatement (
5727 var_type.Type, variable, get_current, statement, loc);
5729 loop = new While (move_next_expr, block, loc);
5731 wrapper = is_disposable ?
5732 (Statement) new DisposableWrapper (this) :
5733 (Statement) new NonDisposableWrapper (this);
5734 return wrapper.Resolve (ec);
5737 protected override void DoEmit (EmitContext ec)
5742 class NonDisposableWrapper : Statement {
5743 CollectionForeach parent;
5745 internal NonDisposableWrapper (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)
5757 return parent.ResolveLoop (ec);
5760 protected override void DoEmit (EmitContext ec)
5762 parent.EmitLoopInit (ec);
5763 parent.EmitLoopBody (ec);
5766 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5768 throw new NotSupportedException ();
5772 class DisposableWrapper : ExceptionStatement {
5773 CollectionForeach parent;
5775 internal DisposableWrapper (CollectionForeach parent)
5777 this.parent = parent;
5780 protected override void CloneTo (CloneContext clonectx, Statement target)
5782 throw new NotSupportedException ();
5785 public override bool Resolve (EmitContext ec)
5789 ec.StartFlowBranching (this);
5791 if (!parent.ResolveLoop (ec))
5794 ec.EndFlowBranching ();
5796 ResolveReachability (ec);
5798 if (TypeManager.void_dispose_void == null) {
5799 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5800 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5805 protected override void EmitPreTryBody (EmitContext ec)
5807 parent.EmitLoopInit (ec);
5810 protected override void EmitTryBody (EmitContext ec)
5812 parent.EmitLoopBody (ec);
5815 protected override void EmitFinallyBody (EmitContext ec)
5817 parent.EmitFinallyBody (ec);
5820 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5822 throw new NotSupportedException ();
5826 bool ResolveLoop (EmitContext ec)
5828 return loop.Resolve (ec);
5831 void EmitLoopInit (EmitContext ec)
5833 enumerator.EmitAssign (ec, init);
5836 void EmitLoopBody (EmitContext ec)
5841 void EmitFinallyBody (EmitContext ec)
5843 ILGenerator ig = ec.ig;
5845 if (enumerator_type.IsValueType) {
5846 MethodInfo mi = FetchMethodDispose (enumerator_type);
5848 enumerator.AddressOf (ec, AddressOp.Load);
5849 ig.Emit (OpCodes.Call, mi);
5851 enumerator.Emit (ec);
5852 ig.Emit (OpCodes.Box, enumerator_type);
5853 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5856 Label call_dispose = ig.DefineLabel ();
5858 enumerator.Emit (ec);
5859 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5860 ig.Emit (OpCodes.Dup);
5861 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5863 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5864 // (Partition III, Section 3.35)
5865 ig.Emit (OpCodes.Endfinally);
5867 ig.MarkLabel (call_dispose);
5868 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5872 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5874 enumerator_type = storey.MutateType (enumerator_type);
5875 init.MutateHoistedGenericType (storey);
5876 loop.MutateHoistedGenericType (storey);
5880 protected class CollectionForeachStatement : Statement
5883 Expression variable, current, conv;
5884 Statement statement;
5887 public CollectionForeachStatement (Type type, Expression variable,
5888 Expression current, Statement statement,
5892 this.variable = variable;
5893 this.current = current;
5894 this.statement = statement;
5898 protected override void CloneTo (CloneContext clonectx, Statement target)
5900 throw new NotImplementedException ();
5903 public override bool Resolve (EmitContext ec)
5905 current = current.Resolve (ec);
5906 if (current == null)
5909 conv = Convert.ExplicitConversion (ec, current, type, loc);
5913 assign = new SimpleAssign (variable, conv, loc);
5914 if (assign.Resolve (ec) == null)
5917 if (!statement.Resolve (ec))
5923 protected override void DoEmit (EmitContext ec)
5925 assign.EmitStatement (ec);
5926 statement.Emit (ec);
5929 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5931 assign.MutateHoistedGenericType (storey);
5932 statement.MutateHoistedGenericType (storey);
5936 protected override void CloneTo (CloneContext clonectx, Statement t)
5938 Foreach target = (Foreach) t;
5940 target.type = type.Clone (clonectx);
5941 target.variable = variable.Clone (clonectx);
5942 target.expr = expr.Clone (clonectx);
5943 target.statement = statement.Clone (clonectx);
5946 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5948 statement.MutateHoistedGenericType (storey);