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 ec.CurrentBranching.CurrentUsageVector.Goto ();
1079 expr = expr.Resolve (ec);
1083 Constant c = expr as Constant;
1085 Error (150, "A constant value is expected");
1089 Type type = ec.Switch.SwitchType;
1090 Constant res = c.TryReduce (ec, type, c.Location);
1092 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1096 if (!Convert.ImplicitStandardConversionExists (c, type))
1097 Report.Warning (469, 2, loc,
1098 "The `goto case' value is not implicitly convertible to type `{0}'",
1099 TypeManager.CSharpName (type));
1101 object val = res.GetValue ();
1103 val = SwitchLabel.NullStringCase;
1105 sl = (SwitchLabel) ec.Switch.Elements [val];
1108 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1109 (c.GetValue () == null ? "null" : val.ToString ()));
1116 protected override void DoEmit (EmitContext ec)
1118 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1121 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1123 expr.MutateHoistedGenericType (storey);
1126 protected override void CloneTo (CloneContext clonectx, Statement t)
1128 GotoCase target = (GotoCase) t;
1130 target.expr = expr.Clone (clonectx);
1134 public class Throw : Statement {
1137 public Throw (Expression expr, Location l)
1143 public override bool Resolve (EmitContext ec)
1146 ec.CurrentBranching.CurrentUsageVector.Goto ();
1147 return ec.CurrentBranching.CheckRethrow (loc);
1150 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1151 ec.CurrentBranching.CurrentUsageVector.Goto ();
1158 if ((t != TypeManager.exception_type) &&
1159 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1160 t != TypeManager.null_type) {
1161 Error (155, "The type caught or thrown must be derived from System.Exception");
1167 protected override void DoEmit (EmitContext ec)
1170 ec.ig.Emit (OpCodes.Rethrow);
1174 ec.ig.Emit (OpCodes.Throw);
1178 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1181 expr.MutateHoistedGenericType (storey);
1184 protected override void CloneTo (CloneContext clonectx, Statement t)
1186 Throw target = (Throw) t;
1189 target.expr = expr.Clone (clonectx);
1193 public class Break : Statement {
1195 public Break (Location l)
1200 bool unwind_protect;
1202 public override bool Resolve (EmitContext ec)
1204 int errors = Report.Errors;
1205 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1206 ec.CurrentBranching.CurrentUsageVector.Goto ();
1207 return errors == Report.Errors;
1210 protected override void DoEmit (EmitContext ec)
1212 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1215 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1219 protected override void CloneTo (CloneContext clonectx, Statement t)
1225 public class Continue : Statement {
1227 public Continue (Location l)
1232 bool unwind_protect;
1234 public override bool Resolve (EmitContext ec)
1236 int errors = Report.Errors;
1237 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1238 ec.CurrentBranching.CurrentUsageVector.Goto ();
1239 return errors == Report.Errors;
1242 protected override void DoEmit (EmitContext ec)
1244 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1247 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1251 protected override void CloneTo (CloneContext clonectx, Statement t)
1257 public interface ILocalVariable
1259 void Emit (EmitContext ec);
1260 void EmitAssign (EmitContext ec);
1261 void EmitAddressOf (EmitContext ec);
1264 public interface IKnownVariable {
1265 Block Block { get; }
1266 Location Location { get; }
1270 // The information about a user-perceived local variable
1272 public class LocalInfo : IKnownVariable, ILocalVariable {
1273 public readonly Expression Type;
1275 public Type VariableType;
1276 public readonly string Name;
1277 public readonly Location Location;
1278 public readonly Block Block;
1280 public VariableInfo VariableInfo;
1281 public HoistedVariable HoistedVariableReference;
1290 CompilerGenerated = 64,
1294 public enum ReadOnlyContext: byte {
1301 ReadOnlyContext ro_context;
1302 LocalBuilder builder;
1304 public LocalInfo (Expression type, string name, Block block, Location l)
1312 public LocalInfo (DeclSpace ds, Block block, Location l)
1314 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1319 public void ResolveVariable (EmitContext ec)
1321 if (HoistedVariableReference != null)
1324 if (builder == null) {
1327 // This is needed to compile on both .NET 1.x and .NET 2.x
1328 // the later introduced `DeclareLocal (Type t, bool pinned)'
1330 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1332 builder = ec.ig.DeclareLocal (VariableType);
1336 public void Emit (EmitContext ec)
1338 ec.ig.Emit (OpCodes.Ldloc, builder);
1341 public void EmitAssign (EmitContext ec)
1343 ec.ig.Emit (OpCodes.Stloc, builder);
1346 public void EmitAddressOf (EmitContext ec)
1348 ec.ig.Emit (OpCodes.Ldloca, builder);
1351 public void EmitSymbolInfo (EmitContext ec)
1353 if (builder != null)
1354 ec.DefineLocalVariable (Name, builder);
1357 public bool IsThisAssigned (EmitContext ec)
1359 if (VariableInfo == null)
1360 throw new Exception ();
1362 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1365 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1368 public bool IsAssigned (EmitContext ec)
1370 if (VariableInfo == null)
1371 throw new Exception ();
1373 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1376 public bool Resolve (EmitContext ec)
1378 if (VariableType != null)
1381 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1385 VariableType = texpr.Type;
1387 if (TypeManager.IsGenericParameter (VariableType))
1390 if (VariableType.IsAbstract && VariableType.IsSealed) {
1391 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1395 if (VariableType.IsPointer && !ec.InUnsafe)
1396 Expression.UnsafeError (Location);
1401 public bool IsConstant {
1402 get { return (flags & Flags.IsConstant) != 0; }
1403 set { flags |= Flags.IsConstant; }
1406 public bool AddressTaken {
1407 get { return (flags & Flags.AddressTaken) != 0; }
1408 set { flags |= Flags.AddressTaken; }
1411 public bool CompilerGenerated {
1412 get { return (flags & Flags.CompilerGenerated) != 0; }
1413 set { flags |= Flags.CompilerGenerated; }
1416 public override string ToString ()
1418 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1419 Name, Type, VariableInfo, Location);
1423 get { return (flags & Flags.Used) != 0; }
1424 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1427 public bool ReadOnly {
1428 get { return (flags & Flags.ReadOnly) != 0; }
1431 public void SetReadOnlyContext (ReadOnlyContext context)
1433 flags |= Flags.ReadOnly;
1434 ro_context = context;
1437 public string GetReadOnlyContext ()
1440 throw new InternalErrorException ("Variable is not readonly");
1442 switch (ro_context) {
1443 case ReadOnlyContext.Fixed:
1444 return "fixed variable";
1445 case ReadOnlyContext.Foreach:
1446 return "foreach iteration variable";
1447 case ReadOnlyContext.Using:
1448 return "using variable";
1450 throw new NotImplementedException ();
1454 // Whether the variable is pinned, if Pinned the variable has been
1455 // allocated in a pinned slot with DeclareLocal.
1457 public bool Pinned {
1458 get { return (flags & Flags.Pinned) != 0; }
1459 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1462 public bool IsThis {
1463 get { return (flags & Flags.IsThis) != 0; }
1464 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1467 Block IKnownVariable.Block {
1468 get { return Block; }
1471 Location IKnownVariable.Location {
1472 get { return Location; }
1475 public LocalInfo Clone (CloneContext clonectx)
1478 // Variables in anonymous block are not resolved yet
1480 if (VariableType == null)
1481 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1484 // Variables in method block are resolved
1486 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1487 li.VariableType = VariableType;
1493 /// Block represents a C# block.
1497 /// This class is used in a number of places: either to represent
1498 /// explicit blocks that the programmer places or implicit blocks.
1500 /// Implicit blocks are used as labels or to introduce variable
1503 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1504 /// they contain extra information that is not necessary on normal blocks.
1506 public class Block : Statement {
1507 public Block Parent;
1508 public Location StartLocation;
1509 public Location EndLocation = Location.Null;
1511 public ExplicitBlock Explicit;
1512 public ToplevelBlock Toplevel; // TODO: Use Explicit
1515 public enum Flags : byte {
1518 VariablesInitialized = 4,
1522 HasCapturedVariable = 64,
1523 HasCapturedThis = 128
1525 protected Flags flags;
1527 public bool Unchecked {
1528 get { return (flags & Flags.Unchecked) != 0; }
1529 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1532 public bool Unsafe {
1533 get { return (flags & Flags.Unsafe) != 0; }
1534 set { flags |= Flags.Unsafe; }
1538 // The statements in this block
1540 protected ArrayList statements;
1543 // An array of Blocks. We keep track of children just
1544 // to generate the local variable declarations.
1546 // Statements and child statements are handled through the
1552 // Labels. (label, block) pairs.
1554 protected HybridDictionary labels;
1557 // Keeps track of (name, type) pairs
1559 IDictionary variables;
1562 // Keeps track of constants
1563 HybridDictionary constants;
1566 // Temporary variables.
1568 ArrayList temporary_variables;
1571 // If this is a switch section, the enclosing switch block.
1575 protected ArrayList scope_initializers;
1577 ArrayList anonymous_children;
1579 protected static int id;
1583 int assignable_slots;
1584 bool unreachable_shown;
1587 public Block (Block parent)
1588 : this (parent, (Flags) 0, Location.Null, Location.Null)
1591 public Block (Block parent, Flags flags)
1592 : this (parent, flags, Location.Null, Location.Null)
1595 public Block (Block parent, Location start, Location end)
1596 : this (parent, (Flags) 0, start, end)
1600 // Useful when TopLevel block is downgraded to normal block
1602 public Block (ToplevelBlock parent, ToplevelBlock source)
1603 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1605 statements = source.statements;
1606 children = source.children;
1607 labels = source.labels;
1608 variables = source.variables;
1609 constants = source.constants;
1610 switch_block = source.switch_block;
1613 public Block (Block parent, Flags flags, Location start, Location end)
1615 if (parent != null) {
1616 parent.AddChild (this);
1618 // the appropriate constructors will fixup these fields
1619 Toplevel = parent.Toplevel;
1620 Explicit = parent.Explicit;
1623 this.Parent = parent;
1625 this.StartLocation = start;
1626 this.EndLocation = end;
1629 statements = new ArrayList (4);
1632 public Block CreateSwitchBlock (Location start)
1634 // FIXME: should this be implicit?
1635 Block new_block = new ExplicitBlock (this, start, start);
1636 new_block.switch_block = this;
1641 get { return this_id; }
1644 public IDictionary Variables {
1646 if (variables == null)
1647 variables = new ListDictionary ();
1652 void AddChild (Block b)
1654 if (children == null)
1655 children = new ArrayList (1);
1660 public void SetEndLocation (Location loc)
1665 protected static void Error_158 (string name, Location loc)
1667 Report.Error (158, loc, "The label `{0}' shadows another label " +
1668 "by the same name in a contained scope", name);
1672 /// Adds a label to the current block.
1676 /// false if the name already exists in this block. true
1680 public bool AddLabel (LabeledStatement target)
1682 if (switch_block != null)
1683 return switch_block.AddLabel (target);
1685 string name = target.Name;
1688 while (cur != null) {
1689 LabeledStatement s = cur.DoLookupLabel (name);
1691 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1692 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1696 if (this == Explicit)
1702 while (cur != null) {
1703 if (cur.DoLookupLabel (name) != null) {
1704 Error_158 (name, target.loc);
1708 if (children != null) {
1709 foreach (Block b in children) {
1710 LabeledStatement s = b.DoLookupLabel (name);
1714 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1715 Error_158 (name, target.loc);
1723 Toplevel.CheckError158 (name, target.loc);
1726 labels = new HybridDictionary();
1728 labels.Add (name, target);
1732 public LabeledStatement LookupLabel (string name)
1734 LabeledStatement s = DoLookupLabel (name);
1738 if (children == null)
1741 foreach (Block child in children) {
1742 if (Explicit != child.Explicit)
1745 s = child.LookupLabel (name);
1753 LabeledStatement DoLookupLabel (string name)
1755 if (switch_block != null)
1756 return switch_block.LookupLabel (name);
1759 if (labels.Contains (name))
1760 return ((LabeledStatement) labels [name]);
1765 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1768 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1769 while (kvi == null) {
1770 b = b.Explicit.Parent;
1773 kvi = b.Explicit.GetKnownVariable (name);
1779 // Is kvi.Block nested inside 'b'
1780 if (b.Explicit != kvi.Block.Explicit) {
1782 // If a variable by the same name it defined in a nested block of this
1783 // block, we violate the invariant meaning in a block.
1786 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1787 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1792 // It's ok if the definition is in a nested subblock of b, but not
1793 // nested inside this block -- a definition in a sibling block
1794 // should not affect us.
1800 // Block 'b' and kvi.Block are the same textual block.
1801 // However, different variables are extant.
1803 // Check if the variable is in scope in both blocks. We use
1804 // an indirect check that depends on AddVariable doing its
1805 // part in maintaining the invariant-meaning-in-block property.
1807 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1810 if (this is ToplevelBlock) {
1811 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1812 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1817 // Even though we detected the error when the name is used, we
1818 // treat it as if the variable declaration was in error.
1820 Report.SymbolRelatedToPreviousError (loc, name);
1821 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1825 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1827 LocalInfo vi = GetLocalInfo (name);
1829 Report.SymbolRelatedToPreviousError (vi.Location, name);
1830 if (Explicit == vi.Block.Explicit) {
1831 Error_AlreadyDeclared (l, name, null);
1833 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1834 "parent or current" : "parent");
1839 if (block != null) {
1840 Expression e = block.GetParameterReference (name, Location.Null);
1842 ParameterReference pr = e as ParameterReference;
1843 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1844 Error_AlreadyDeclared (loc, name);
1846 Error_AlreadyDeclared (loc, name, "parent or current");
1854 public LocalInfo AddVariable (Expression type, string name, Location l)
1856 if (!CheckParentConflictName (Toplevel, name, l))
1859 if (Toplevel.GenericMethod != null) {
1860 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1861 if (tp.Name == name) {
1862 Report.SymbolRelatedToPreviousError (tp);
1863 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1869 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1871 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1872 Error_AlreadyDeclared (l, name, "child");
1876 LocalInfo vi = new LocalInfo (type, name, this, l);
1879 if ((flags & Flags.VariablesInitialized) != 0)
1880 throw new InternalErrorException ("block has already been resolved");
1885 protected virtual void AddVariable (LocalInfo li)
1887 Variables.Add (li.Name, li);
1888 Explicit.AddKnownVariable (li.Name, li);
1891 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1893 if (reason == null) {
1894 Error_AlreadyDeclared (loc, var);
1898 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1899 "in this scope because it would give a different meaning " +
1900 "to `{0}', which is already used in a `{1}' scope " +
1901 "to denote something else", var, reason);
1904 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1906 Report.Error (128, loc,
1907 "A local variable named `{0}' is already defined in this scope", name);
1910 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1912 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1916 public bool AddConstant (Expression type, string name, Expression value, Location l)
1918 if (AddVariable (type, name, l) == null)
1921 if (constants == null)
1922 constants = new HybridDictionary();
1924 constants.Add (name, value);
1926 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1931 static int next_temp_id = 0;
1933 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1935 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1937 if (temporary_variables == null)
1938 temporary_variables = new ArrayList ();
1940 int id = ++next_temp_id;
1941 string name = "$s_" + id.ToString ();
1943 LocalInfo li = new LocalInfo (te, name, this, loc);
1944 li.CompilerGenerated = true;
1945 temporary_variables.Add (li);
1949 public LocalInfo GetLocalInfo (string name)
1952 for (Block b = this; b != null; b = b.Parent) {
1953 if (b.variables != null) {
1954 ret = (LocalInfo) b.variables [name];
1963 public Expression GetVariableType (string name)
1965 LocalInfo vi = GetLocalInfo (name);
1966 return vi == null ? null : vi.Type;
1969 public Expression GetConstantExpression (string name)
1971 for (Block b = this; b != null; b = b.Parent) {
1972 if (b.constants != null) {
1973 Expression ret = b.constants [name] as Expression;
1982 // It should be used by expressions which require to
1983 // register a statement during resolve process.
1985 public void AddScopeStatement (Statement s)
1987 if (scope_initializers == null)
1988 scope_initializers = new ArrayList ();
1990 scope_initializers.Add (s);
1993 public void AddStatement (Statement s)
1996 flags |= Flags.BlockUsed;
2000 get { return (flags & Flags.BlockUsed) != 0; }
2005 flags |= Flags.BlockUsed;
2008 public bool HasRet {
2009 get { return (flags & Flags.HasRet) != 0; }
2012 public int AssignableSlots {
2015 // if ((flags & Flags.VariablesInitialized) == 0)
2016 // throw new Exception ("Variables have not been initialized yet");
2017 return assignable_slots;
2021 public ArrayList AnonymousChildren {
2022 get { return anonymous_children; }
2025 public void AddAnonymousChild (ToplevelBlock b)
2027 if (anonymous_children == null)
2028 anonymous_children = new ArrayList ();
2030 anonymous_children.Add (b);
2033 void DoResolveConstants (EmitContext ec)
2035 if (constants == null)
2038 if (variables == null)
2039 throw new InternalErrorException ("cannot happen");
2041 foreach (DictionaryEntry de in variables) {
2042 string name = (string) de.Key;
2043 LocalInfo vi = (LocalInfo) de.Value;
2044 Type variable_type = vi.VariableType;
2046 if (variable_type == null) {
2047 if (vi.Type is VarExpr)
2048 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2053 Expression cv = (Expression) constants [name];
2057 // Don't let 'const int Foo = Foo;' succeed.
2058 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2059 // which in turn causes the 'must be constant' error to be triggered.
2060 constants.Remove (name);
2062 if (!Const.IsConstantTypeValid (variable_type)) {
2063 Const.Error_InvalidConstantType (variable_type, loc);
2067 ec.CurrentBlock = this;
2069 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2070 e = cv.Resolve (ec);
2075 Constant ce = e as Constant;
2077 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2081 e = ce.ConvertImplicitly (variable_type);
2083 if (TypeManager.IsReferenceType (variable_type))
2084 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2086 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2090 constants.Add (name, e);
2091 vi.IsConstant = true;
2095 protected void ResolveMeta (EmitContext ec, int offset)
2097 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2099 // If some parent block was unsafe, we remain unsafe even if this block
2100 // isn't explicitly marked as such.
2101 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2102 flags |= Flags.VariablesInitialized;
2104 if (variables != null) {
2105 foreach (LocalInfo li in variables.Values) {
2106 if (!li.Resolve (ec))
2108 li.VariableInfo = new VariableInfo (li, offset);
2109 offset += li.VariableInfo.Length;
2112 assignable_slots = offset;
2114 DoResolveConstants (ec);
2116 if (children == null)
2118 foreach (Block b in children)
2119 b.ResolveMeta (ec, offset);
2124 // Emits the local variable declarations for a block
2126 public virtual void EmitMeta (EmitContext ec)
2128 if (variables != null){
2129 foreach (LocalInfo vi in variables.Values)
2130 vi.ResolveVariable (ec);
2133 if (temporary_variables != null) {
2134 for (int i = 0; i < temporary_variables.Count; i++)
2135 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2138 if (children != null) {
2139 for (int i = 0; i < children.Count; i++)
2140 ((Block)children[i]).EmitMeta(ec);
2144 void UsageWarning ()
2146 if (variables == null || Report.WarningLevel < 3)
2149 foreach (DictionaryEntry de in variables) {
2150 LocalInfo vi = (LocalInfo) de.Value;
2153 string name = (string) de.Key;
2155 // vi.VariableInfo can be null for 'catch' variables
2156 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2157 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2159 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2164 static void CheckPossibleMistakenEmptyStatement (Statement s)
2168 // Some statements are wrapped by a Block. Since
2169 // others' internal could be changed, here I treat
2170 // them as possibly wrapped by Block equally.
2171 Block b = s as Block;
2172 if (b != null && b.statements.Count == 1)
2173 s = (Statement) b.statements [0];
2176 body = ((Lock) s).Statement;
2178 body = ((For) s).Statement;
2179 else if (s is Foreach)
2180 body = ((Foreach) s).Statement;
2181 else if (s is While)
2182 body = ((While) s).Statement;
2183 else if (s is Fixed)
2184 body = ((Fixed) s).Statement;
2185 else if (s is Using)
2186 body = ((Using) s).EmbeddedStatement;
2187 else if (s is UsingTemporary)
2188 body = ((UsingTemporary) s).Statement;
2192 if (body == null || body is EmptyStatement)
2193 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2196 public override bool Resolve (EmitContext ec)
2198 Block prev_block = ec.CurrentBlock;
2201 int errors = Report.Errors;
2203 ec.CurrentBlock = this;
2204 ec.StartFlowBranching (this);
2206 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2209 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2210 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2211 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2212 // responsible for handling the situation.
2214 int statement_count = statements.Count;
2215 for (int ix = 0; ix < statement_count; ix++){
2216 Statement s = (Statement) statements [ix];
2217 // Check possible empty statement (CS0642)
2218 if (Report.WarningLevel >= 3 &&
2219 ix + 1 < statement_count &&
2220 statements [ix + 1] is ExplicitBlock)
2221 CheckPossibleMistakenEmptyStatement (s);
2224 // Warn if we detect unreachable code.
2227 if (s is EmptyStatement)
2230 if (!unreachable_shown && !(s is LabeledStatement)) {
2231 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2232 unreachable_shown = true;
2235 Block c_block = s as Block;
2236 if (c_block != null)
2237 c_block.unreachable = c_block.unreachable_shown = true;
2241 // Note that we're not using ResolveUnreachable() for unreachable
2242 // statements here. ResolveUnreachable() creates a temporary
2243 // flow branching and kills it afterwards. This leads to problems
2244 // if you have two unreachable statements where the first one
2245 // assigns a variable and the second one tries to access it.
2248 if (!s.Resolve (ec)) {
2250 if (ec.IsInProbingMode)
2253 statements [ix] = EmptyStatement.Value;
2257 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2258 statements [ix] = EmptyStatement.Value;
2260 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2261 if (unreachable && s is LabeledStatement)
2262 throw new InternalErrorException ("should not happen");
2265 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2266 ec.CurrentBranching, statement_count);
2268 while (ec.CurrentBranching is FlowBranchingLabeled)
2269 ec.EndFlowBranching ();
2271 bool flow_unreachable = ec.EndFlowBranching ();
2273 ec.CurrentBlock = prev_block;
2275 if (flow_unreachable)
2276 flags |= Flags.HasRet;
2278 // If we're a non-static `struct' constructor which doesn't have an
2279 // initializer, then we must initialize all of the struct's fields.
2280 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2283 if ((labels != null) && (Report.WarningLevel >= 2)) {
2284 foreach (LabeledStatement label in labels.Values)
2285 if (!label.HasBeenReferenced)
2286 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2289 if (ok && errors == Report.Errors)
2295 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2297 unreachable_shown = true;
2301 Report.Warning (162, 2, loc, "Unreachable code detected");
2303 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2304 bool ok = Resolve (ec);
2305 ec.KillFlowBranching ();
2310 protected override void DoEmit (EmitContext ec)
2312 for (int ix = 0; ix < statements.Count; ix++){
2313 Statement s = (Statement) statements [ix];
2318 public override void Emit (EmitContext ec)
2320 Block prev_block = ec.CurrentBlock;
2321 ec.CurrentBlock = this;
2323 if (scope_initializers != null)
2324 EmitScopeInitializers (ec);
2326 ec.Mark (StartLocation);
2329 if (SymbolWriter.HasSymbolWriter)
2330 EmitSymbolInfo (ec);
2332 ec.CurrentBlock = prev_block;
2335 protected void EmitScopeInitializers (EmitContext ec)
2337 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2339 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2340 foreach (Statement s in scope_initializers)
2344 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2347 protected virtual void EmitSymbolInfo (EmitContext ec)
2349 if (variables != null) {
2350 foreach (LocalInfo vi in variables.Values) {
2351 vi.EmitSymbolInfo (ec);
2356 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2358 MutateVariables (storey);
2360 if (scope_initializers != null) {
2361 foreach (Statement s in scope_initializers)
2362 s.MutateHoistedGenericType (storey);
2365 foreach (Statement s in statements)
2366 s.MutateHoistedGenericType (storey);
2369 void MutateVariables (AnonymousMethodStorey storey)
2371 if (variables != null) {
2372 foreach (LocalInfo vi in variables.Values) {
2373 vi.VariableType = storey.MutateType (vi.VariableType);
2377 if (temporary_variables != null) {
2378 foreach (LocalInfo vi in temporary_variables)
2379 vi.VariableType = storey.MutateType (vi.VariableType);
2383 public override string ToString ()
2385 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2388 protected override void CloneTo (CloneContext clonectx, Statement t)
2390 Block target = (Block) t;
2392 clonectx.AddBlockMap (this, target);
2394 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2395 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2397 target.Parent = clonectx.RemapBlockCopy (Parent);
2399 if (variables != null){
2400 target.variables = new Hashtable ();
2402 foreach (DictionaryEntry de in variables){
2403 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2404 target.variables [de.Key] = newlocal;
2405 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2409 target.statements = new ArrayList (statements.Count);
2410 foreach (Statement s in statements)
2411 target.statements.Add (s.Clone (clonectx));
2413 if (target.children != null){
2414 target.children = new ArrayList (children.Count);
2415 foreach (Block b in children){
2416 target.children.Add (clonectx.LookupBlock (b));
2421 // TODO: labels, switch_block, constants (?), anonymous_children
2426 public class ExplicitBlock : Block {
2427 HybridDictionary known_variables;
2428 protected AnonymousMethodStorey am_storey;
2430 public ExplicitBlock (Block parent, Location start, Location end)
2431 : this (parent, (Flags) 0, start, end)
2435 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2436 : base (parent, flags, start, end)
2438 this.Explicit = this;
2442 // Marks a variable with name @name as being used in this or a child block.
2443 // If a variable name has been used in a child block, it's illegal to
2444 // declare a variable with the same name in the current block.
2446 internal void AddKnownVariable (string name, IKnownVariable info)
2448 if (known_variables == null)
2449 known_variables = new HybridDictionary();
2451 known_variables [name] = info;
2454 Parent.Explicit.AddKnownVariable (name, info);
2457 public AnonymousMethodStorey AnonymousMethodStorey {
2458 get { return am_storey; }
2462 // Creates anonymous method storey in current block
2464 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2467 // When referencing a variable in iterator storey from children anonymous method
2469 if (Toplevel.am_storey is IteratorStorey) {
2470 return Toplevel.am_storey;
2474 // An iterator has only 1 storey block
2476 if (ec.CurrentIterator != null)
2477 return ec.CurrentIterator.Storey;
2479 if (am_storey == null) {
2480 MemberBase mc = ec.ResolveContext as MemberBase;
2481 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2484 // Creates anonymous method storey for this block
2486 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2492 public override void Emit (EmitContext ec)
2494 if (am_storey != null)
2495 am_storey.EmitStoreyInstantiation (ec);
2497 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2498 if (emit_debug_info)
2503 if (emit_debug_info)
2507 public override void EmitMeta (EmitContext ec)
2510 // Creates anonymous method storey
2512 if (am_storey != null) {
2513 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2514 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2515 ExplicitBlock parent = Toplevel.Parent.Explicit;
2516 while (parent.am_storey == null)
2517 parent = parent.Parent.Explicit;
2519 am_storey.AddParentStoreyReference (parent.am_storey);
2522 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2525 am_storey.DefineType ();
2526 am_storey.ResolveType ();
2527 am_storey.Define ();
2528 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2530 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2531 if (ref_blocks != null) {
2532 foreach (ExplicitBlock ref_block in ref_blocks) {
2533 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2534 if (b.am_storey != null) {
2535 b.am_storey.AddParentStoreyReference (am_storey);
2537 // Stop propagation inside same top block
2538 if (b.Toplevel == Toplevel)
2543 b.HasCapturedVariable = true;
2552 internal IKnownVariable GetKnownVariable (string name)
2554 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2557 public bool HasCapturedThis
2559 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2560 get { return (flags & Flags.HasCapturedThis) != 0; }
2563 public bool HasCapturedVariable
2565 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2566 get { return (flags & Flags.HasCapturedVariable) != 0; }
2569 protected override void CloneTo (CloneContext clonectx, Statement t)
2571 ExplicitBlock target = (ExplicitBlock) t;
2572 target.known_variables = null;
2573 base.CloneTo (clonectx, t);
2577 public class ToplevelParameterInfo : IKnownVariable {
2578 public readonly ToplevelBlock Block;
2579 public readonly int Index;
2580 public VariableInfo VariableInfo;
2582 Block IKnownVariable.Block {
2583 get { return Block; }
2585 public Parameter Parameter {
2586 get { return Block.Parameters [Index]; }
2589 public Type ParameterType {
2590 get { return Block.Parameters.Types [Index]; }
2593 public Location Location {
2594 get { return Parameter.Location; }
2597 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2605 // A toplevel block contains extra information, the split is done
2606 // only to separate information that would otherwise bloat the more
2607 // lightweight Block.
2609 // In particular, this was introduced when the support for Anonymous
2610 // Methods was implemented.
2612 public class ToplevelBlock : ExplicitBlock
2615 // Block is converted to an expression
2617 sealed class BlockScopeExpression : Expression
2620 readonly ToplevelBlock block;
2622 public BlockScopeExpression (Expression child, ToplevelBlock block)
2628 public override Expression CreateExpressionTree (EmitContext ec)
2630 throw new NotSupportedException ();
2633 public override Expression DoResolve (EmitContext ec)
2638 block.ResolveMeta (ec, ParametersCompiled.EmptyReadOnlyParameters);
2639 child = child.Resolve (ec);
2643 eclass = child.eclass;
2648 public override void Emit (EmitContext ec)
2650 block.EmitMeta (ec);
2651 block.EmitScopeInitializers (ec);
2655 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2657 type = storey.MutateType (type);
2658 child.MutateHoistedGenericType (storey);
2659 block.MutateHoistedGenericType (storey);
2663 GenericMethod generic;
2664 FlowBranchingToplevel top_level_branching;
2665 protected ParametersCompiled parameters;
2666 ToplevelParameterInfo[] parameter_info;
2667 LocalInfo this_variable;
2669 public HoistedVariable HoistedThisVariable;
2672 // The parameters for the block.
2674 public ParametersCompiled Parameters {
2675 get { return parameters; }
2678 public GenericMethod GenericMethod {
2679 get { return generic; }
2682 public ToplevelBlock Container {
2683 get { return Parent == null ? null : Parent.Toplevel; }
2686 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2687 this (parent, (Flags) 0, parameters, start)
2691 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2692 this (parent, parameters, start)
2694 this.generic = generic;
2697 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2698 this (null, (Flags) 0, parameters, start)
2702 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2703 this (null, flags, parameters, start)
2707 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2708 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2709 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2710 base (null, flags, start, Location.Null)
2712 this.Toplevel = this;
2714 this.parameters = parameters;
2715 this.Parent = parent;
2717 parent.AddAnonymousChild (this);
2719 if (!this.parameters.IsEmpty)
2720 ProcessParameters ();
2723 public ToplevelBlock (Location loc)
2724 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2728 protected override void CloneTo (CloneContext clonectx, Statement t)
2730 ToplevelBlock target = (ToplevelBlock) t;
2731 base.CloneTo (clonectx, t);
2733 if (parameters.Count != 0)
2734 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2735 for (int i = 0; i < parameters.Count; ++i)
2736 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2739 public bool CheckError158 (string name, Location loc)
2741 if (AnonymousChildren != null) {
2742 foreach (ToplevelBlock child in AnonymousChildren) {
2743 if (!child.CheckError158 (name, loc))
2748 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2749 if (!c.DoCheckError158 (name, loc))
2756 void ProcessParameters ()
2758 int n = parameters.Count;
2759 parameter_info = new ToplevelParameterInfo [n];
2760 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2761 for (int i = 0; i < n; ++i) {
2762 parameter_info [i] = new ToplevelParameterInfo (this, i);
2764 Parameter p = parameters [i];
2768 string name = p.Name;
2769 if (CheckParentConflictName (top_parent, name, loc))
2770 AddKnownVariable (name, parameter_info [i]);
2773 // mark this block as "used" so that we create local declarations in a sub-block
2774 // FIXME: This appears to uncover a lot of bugs
2778 bool DoCheckError158 (string name, Location loc)
2780 LabeledStatement s = LookupLabel (name);
2782 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2783 Error_158 (name, loc);
2790 public override Expression CreateExpressionTree (EmitContext ec)
2792 if (statements.Count == 1) {
2793 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2794 if (scope_initializers != null)
2795 expr = new BlockScopeExpression (expr, this);
2800 return base.CreateExpressionTree (ec);
2804 // Reformats this block to be top-level iterator block
2806 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2810 // Creates block with original statements
2811 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2813 source.statements = new ArrayList (1);
2814 source.AddStatement (new Return (iterator, iterator.Location));
2815 source.IsIterator = false;
2817 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2818 source.am_storey = iterator_storey;
2819 return iterator_storey;
2822 public FlowBranchingToplevel TopLevelBranching {
2823 get { return top_level_branching; }
2827 // Returns a parameter reference expression for the given name,
2828 // or null if there is no such parameter
2830 public Expression GetParameterReference (string name, Location loc)
2832 for (ToplevelBlock t = this; t != null; t = t.Container) {
2833 Expression expr = t.GetParameterReferenceExpression (name, loc);
2841 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2843 int idx = parameters.GetParameterIndexByName (name);
2845 null : new ParameterReference (parameter_info [idx], loc);
2849 // Returns the "this" instance variable of this block.
2850 // See AddThisVariable() for more information.
2852 public LocalInfo ThisVariable {
2853 get { return this_variable; }
2857 // This is used by non-static `struct' constructors which do not have an
2858 // initializer - in this case, the constructor must initialize all of the
2859 // struct's fields. To do this, we add a "this" variable and use the flow
2860 // analysis code to ensure that it's been fully initialized before control
2861 // leaves the constructor.
2863 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2865 if (this_variable == null) {
2866 this_variable = new LocalInfo (ds, this, l);
2867 this_variable.Used = true;
2868 this_variable.IsThis = true;
2870 Variables.Add ("this", this_variable);
2873 return this_variable;
2876 public bool IsIterator {
2877 get { return (flags & Flags.IsIterator) != 0; }
2878 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2881 public bool IsThisAssigned (EmitContext ec)
2883 return this_variable == null || this_variable.IsThisAssigned (ec);
2886 public bool ResolveMeta (EmitContext ec, ParametersCompiled ip)
2888 int errors = Report.Errors;
2889 int orig_count = parameters.Count;
2891 if (top_level_branching != null)
2897 // Assert: orig_count != parameter.Count => orig_count == 0
2898 if (orig_count != 0 && orig_count != parameters.Count)
2899 throw new InternalErrorException ("parameter information mismatch");
2901 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2903 for (int i = 0; i < orig_count; ++i) {
2904 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2906 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2909 VariableInfo vi = new VariableInfo (ip, i, offset);
2910 parameter_info [i].VariableInfo = vi;
2911 offset += vi.Length;
2914 ResolveMeta (ec, offset);
2916 top_level_branching = ec.StartFlowBranching (this);
2918 return Report.Errors == errors;
2922 // Check whether all `out' parameters have been assigned.
2924 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2926 if (vector.IsUnreachable)
2929 int n = parameter_info == null ? 0 : parameter_info.Length;
2931 for (int i = 0; i < n; i++) {
2932 VariableInfo var = parameter_info [i].VariableInfo;
2937 if (vector.IsAssigned (var, false))
2940 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2945 public override void EmitMeta (EmitContext ec)
2947 parameters.ResolveVariable ();
2949 // Avoid declaring an IL variable for this_variable since it is not accessed
2950 // from the generated IL
2951 if (this_variable != null)
2952 Variables.Remove ("this");
2956 protected override void EmitSymbolInfo (EmitContext ec)
2958 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2959 if ((ae != null) && (ae.Storey != null))
2960 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2962 base.EmitSymbolInfo (ec);
2965 public override void Emit (EmitContext ec)
2968 ec.Mark (EndLocation);
2972 public class SwitchLabel {
2979 Label il_label_code;
2980 bool il_label_code_set;
2982 public static readonly object NullStringCase = new object ();
2985 // if expr == null, then it is the default case.
2987 public SwitchLabel (Expression expr, Location l)
2993 public Expression Label {
2999 public Location Location {
3003 public object Converted {
3009 public Label GetILLabel (EmitContext ec)
3012 il_label = ec.ig.DefineLabel ();
3013 il_label_set = true;
3018 public Label GetILLabelCode (EmitContext ec)
3020 if (!il_label_code_set){
3021 il_label_code = ec.ig.DefineLabel ();
3022 il_label_code_set = true;
3024 return il_label_code;
3028 // Resolves the expression, reduces it to a literal if possible
3029 // and then converts it to the requested type.
3031 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
3033 Expression e = label.Resolve (ec);
3038 Constant c = e as Constant;
3040 Report.Error (150, loc, "A constant value is expected");
3044 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3045 converted = NullStringCase;
3049 if (allow_nullable && c.GetValue () == null) {
3050 converted = NullStringCase;
3054 c = c.ImplicitConversionRequired (ec, required_type, loc);
3058 converted = c.GetValue ();
3062 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3065 if (converted == null)
3067 else if (converted == NullStringCase)
3070 label = converted.ToString ();
3072 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3073 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3076 public SwitchLabel Clone (CloneContext clonectx)
3078 return new SwitchLabel (label.Clone (clonectx), loc);
3082 public class SwitchSection {
3083 // An array of SwitchLabels.
3084 public readonly ArrayList Labels;
3085 public readonly Block Block;
3087 public SwitchSection (ArrayList labels, Block block)
3093 public SwitchSection Clone (CloneContext clonectx)
3095 ArrayList cloned_labels = new ArrayList ();
3097 foreach (SwitchLabel sl in cloned_labels)
3098 cloned_labels.Add (sl.Clone (clonectx));
3100 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3104 public class Switch : Statement {
3105 public ArrayList Sections;
3106 public Expression Expr;
3109 /// Maps constants whose type type SwitchType to their SwitchLabels.
3111 public IDictionary Elements;
3114 /// The governing switch type
3116 public Type SwitchType;
3121 Label default_target;
3123 Expression new_expr;
3126 SwitchSection constant_section;
3127 SwitchSection default_section;
3129 ExpressionStatement string_dictionary;
3130 FieldExpr switch_cache_field;
3131 static int unique_counter;
3135 // Nullable Types support for GMCS.
3137 Nullable.Unwrap unwrap;
3139 protected bool HaveUnwrap {
3140 get { return unwrap != null; }
3143 protected bool HaveUnwrap {
3144 get { return false; }
3149 // The types allowed to be implicitly cast from
3150 // on the governing type
3152 static Type [] allowed_types;
3154 public Switch (Expression e, ArrayList sects, Location l)
3161 public bool GotDefault {
3163 return default_section != null;
3167 public Label DefaultTarget {
3169 return default_target;
3174 // Determines the governing type for a switch. The returned
3175 // expression might be the expression from the switch, or an
3176 // expression that includes any potential conversions to the
3177 // integral types or to string.
3179 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3183 if (t == TypeManager.byte_type ||
3184 t == TypeManager.sbyte_type ||
3185 t == TypeManager.ushort_type ||
3186 t == TypeManager.short_type ||
3187 t == TypeManager.uint32_type ||
3188 t == TypeManager.int32_type ||
3189 t == TypeManager.uint64_type ||
3190 t == TypeManager.int64_type ||
3191 t == TypeManager.char_type ||
3192 t == TypeManager.string_type ||
3193 t == TypeManager.bool_type ||
3194 TypeManager.IsEnumType (t))
3197 if (allowed_types == null){
3198 allowed_types = new Type [] {
3199 TypeManager.sbyte_type,
3200 TypeManager.byte_type,
3201 TypeManager.short_type,
3202 TypeManager.ushort_type,
3203 TypeManager.int32_type,
3204 TypeManager.uint32_type,
3205 TypeManager.int64_type,
3206 TypeManager.uint64_type,
3207 TypeManager.char_type,
3208 TypeManager.string_type
3213 // Try to find a *user* defined implicit conversion.
3215 // If there is no implicit conversion, or if there are multiple
3216 // conversions, we have to report an error
3218 Expression converted = null;
3219 foreach (Type tt in allowed_types){
3222 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3227 // Ignore over-worked ImplicitUserConversions that do
3228 // an implicit conversion in addition to the user conversion.
3230 if (!(e is UserCast))
3233 if (converted != null){
3234 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3244 // Performs the basic sanity checks on the switch statement
3245 // (looks for duplicate keys and non-constant expressions).
3247 // It also returns a hashtable with the keys that we will later
3248 // use to compute the switch tables
3250 bool CheckSwitch (EmitContext ec)
3253 Elements = Sections.Count > 10 ?
3254 (IDictionary)new Hashtable () :
3255 (IDictionary)new ListDictionary ();
3257 foreach (SwitchSection ss in Sections){
3258 foreach (SwitchLabel sl in ss.Labels){
3259 if (sl.Label == null){
3260 if (default_section != null){
3261 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3264 default_section = ss;
3268 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3273 object key = sl.Converted;
3274 if (key == SwitchLabel.NullStringCase)
3275 has_null_case = true;
3278 Elements.Add (key, sl);
3279 } catch (ArgumentException) {
3280 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3288 void EmitObjectInteger (ILGenerator ig, object k)
3291 IntConstant.EmitInt (ig, (int) k);
3292 else if (k is Constant) {
3293 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3296 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3299 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3301 IntConstant.EmitInt (ig, (int) (long) k);
3302 ig.Emit (OpCodes.Conv_I8);
3305 LongConstant.EmitLong (ig, (long) k);
3307 else if (k is ulong)
3309 ulong ul = (ulong) k;
3312 IntConstant.EmitInt (ig, unchecked ((int) ul));
3313 ig.Emit (OpCodes.Conv_U8);
3317 LongConstant.EmitLong (ig, unchecked ((long) ul));
3321 IntConstant.EmitInt (ig, (int) ((char) k));
3322 else if (k is sbyte)
3323 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3325 IntConstant.EmitInt (ig, (int) ((byte) k));
3326 else if (k is short)
3327 IntConstant.EmitInt (ig, (int) ((short) k));
3328 else if (k is ushort)
3329 IntConstant.EmitInt (ig, (int) ((ushort) k));
3331 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3333 throw new Exception ("Unhandled case");
3336 // structure used to hold blocks of keys while calculating table switch
3337 class KeyBlock : IComparable
3339 public KeyBlock (long _first)
3341 first = last = _first;
3345 public ArrayList element_keys = null;
3346 // how many items are in the bucket
3347 public int Size = 1;
3350 get { return (int) (last - first + 1); }
3352 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3354 return kb_last.last - kb_first.first + 1;
3356 public int CompareTo (object obj)
3358 KeyBlock kb = (KeyBlock) obj;
3359 int nLength = Length;
3360 int nLengthOther = kb.Length;
3361 if (nLengthOther == nLength)
3362 return (int) (kb.first - first);
3363 return nLength - nLengthOther;
3368 /// This method emits code for a lookup-based switch statement (non-string)
3369 /// Basically it groups the cases into blocks that are at least half full,
3370 /// and then spits out individual lookup opcodes for each block.
3371 /// It emits the longest blocks first, and short blocks are just
3372 /// handled with direct compares.
3374 /// <param name="ec"></param>
3375 /// <param name="val"></param>
3376 /// <returns></returns>
3377 void TableSwitchEmit (EmitContext ec, Expression val)
3379 int element_count = Elements.Count;
3380 object [] element_keys = new object [element_count];
3381 Elements.Keys.CopyTo (element_keys, 0);
3382 Array.Sort (element_keys);
3384 // initialize the block list with one element per key
3385 ArrayList key_blocks = new ArrayList (element_count);
3386 foreach (object key in element_keys)
3387 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3389 KeyBlock current_kb;
3390 // iteratively merge the blocks while they are at least half full
3391 // there's probably a really cool way to do this with a tree...
3392 while (key_blocks.Count > 1)
3394 ArrayList key_blocks_new = new ArrayList ();
3395 current_kb = (KeyBlock) key_blocks [0];
3396 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3398 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3399 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3402 current_kb.last = kb.last;
3403 current_kb.Size += kb.Size;
3407 // start a new block
3408 key_blocks_new.Add (current_kb);
3412 key_blocks_new.Add (current_kb);
3413 if (key_blocks.Count == key_blocks_new.Count)
3415 key_blocks = key_blocks_new;
3418 // initialize the key lists
3419 foreach (KeyBlock kb in key_blocks)
3420 kb.element_keys = new ArrayList ();
3422 // fill the key lists
3424 if (key_blocks.Count > 0) {
3425 current_kb = (KeyBlock) key_blocks [0];
3426 foreach (object key in element_keys)
3428 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3429 System.Convert.ToInt64 (key) > current_kb.last;
3431 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3432 current_kb.element_keys.Add (key);
3436 // sort the blocks so we can tackle the largest ones first
3439 // okay now we can start...
3440 ILGenerator ig = ec.ig;
3441 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3442 Label lbl_default = default_target;
3444 Type type_keys = null;
3445 if (element_keys.Length > 0)
3446 type_keys = element_keys [0].GetType (); // used for conversions
3450 if (TypeManager.IsEnumType (SwitchType))
3451 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3453 compare_type = SwitchType;
3455 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3457 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3458 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3461 foreach (object key in kb.element_keys) {
3462 SwitchLabel sl = (SwitchLabel) Elements [key];
3463 if (key is int && (int) key == 0) {
3464 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3467 EmitObjectInteger (ig, key);
3468 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3474 // TODO: if all the keys in the block are the same and there are
3475 // no gaps/defaults then just use a range-check.
3476 if (compare_type == TypeManager.int64_type ||
3477 compare_type == TypeManager.uint64_type)
3479 // TODO: optimize constant/I4 cases
3481 // check block range (could be > 2^31)
3483 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3484 ig.Emit (OpCodes.Blt, lbl_default);
3486 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3487 ig.Emit (OpCodes.Bgt, lbl_default);
3493 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3494 ig.Emit (OpCodes.Sub);
3496 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3502 int first = (int) kb.first;
3505 IntConstant.EmitInt (ig, first);
3506 ig.Emit (OpCodes.Sub);
3510 IntConstant.EmitInt (ig, -first);
3511 ig.Emit (OpCodes.Add);
3515 // first, build the list of labels for the switch
3517 int cJumps = kb.Length;
3518 Label [] switch_labels = new Label [cJumps];
3519 for (int iJump = 0; iJump < cJumps; iJump++)
3521 object key = kb.element_keys [iKey];
3522 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3524 SwitchLabel sl = (SwitchLabel) Elements [key];
3525 switch_labels [iJump] = sl.GetILLabel (ec);
3529 switch_labels [iJump] = lbl_default;
3531 // emit the switch opcode
3532 ig.Emit (OpCodes.Switch, switch_labels);
3535 // mark the default for this block
3537 ig.MarkLabel (lbl_default);
3540 // TODO: find the default case and emit it here,
3541 // to prevent having to do the following jump.
3542 // make sure to mark other labels in the default section
3544 // the last default just goes to the end
3545 if (element_keys.Length > 0)
3546 ig.Emit (OpCodes.Br, lbl_default);
3548 // now emit the code for the sections
3549 bool found_default = false;
3551 foreach (SwitchSection ss in Sections) {
3552 foreach (SwitchLabel sl in ss.Labels) {
3553 if (sl.Converted == SwitchLabel.NullStringCase) {
3554 ig.MarkLabel (null_target);
3555 } else if (sl.Label == null) {
3556 ig.MarkLabel (lbl_default);
3557 found_default = true;
3559 ig.MarkLabel (null_target);
3561 ig.MarkLabel (sl.GetILLabel (ec));
3562 ig.MarkLabel (sl.GetILLabelCode (ec));
3567 if (!found_default) {
3568 ig.MarkLabel (lbl_default);
3569 if (!has_null_case) {
3570 ig.MarkLabel (null_target);
3574 ig.MarkLabel (lbl_end);
3577 SwitchSection FindSection (SwitchLabel label)
3579 foreach (SwitchSection ss in Sections){
3580 foreach (SwitchLabel sl in ss.Labels){
3589 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3591 foreach (SwitchSection ss in Sections)
3592 ss.Block.MutateHoistedGenericType (storey);
3595 public static void Reset ()
3600 public override bool Resolve (EmitContext ec)
3602 Expr = Expr.Resolve (ec);
3606 new_expr = SwitchGoverningType (ec, Expr);
3609 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3610 unwrap = Nullable.Unwrap.Create (Expr, ec);
3614 new_expr = SwitchGoverningType (ec, unwrap);
3618 if (new_expr == null){
3619 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3624 SwitchType = new_expr.Type;
3626 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3627 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3631 if (!CheckSwitch (ec))
3635 Elements.Remove (SwitchLabel.NullStringCase);
3637 Switch old_switch = ec.Switch;
3639 ec.Switch.SwitchType = SwitchType;
3641 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3642 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3644 is_constant = new_expr is Constant;
3646 object key = ((Constant) new_expr).GetValue ();
3647 SwitchLabel label = (SwitchLabel) Elements [key];
3649 constant_section = FindSection (label);
3650 if (constant_section == null)
3651 constant_section = default_section;
3656 foreach (SwitchSection ss in Sections){
3658 ec.CurrentBranching.CreateSibling (
3659 null, FlowBranching.SiblingType.SwitchSection);
3663 if (is_constant && (ss != constant_section)) {
3664 // If we're a constant switch, we're only emitting
3665 // one single section - mark all the others as
3667 ec.CurrentBranching.CurrentUsageVector.Goto ();
3668 if (!ss.Block.ResolveUnreachable (ec, true)) {
3672 if (!ss.Block.Resolve (ec))
3677 if (default_section == null)
3678 ec.CurrentBranching.CreateSibling (
3679 null, FlowBranching.SiblingType.SwitchSection);
3681 ec.EndFlowBranching ();
3682 ec.Switch = old_switch;
3684 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3689 if (SwitchType == TypeManager.string_type && !is_constant) {
3690 // TODO: Optimize single case, and single+default case
3691 ResolveStringSwitchMap (ec);
3697 void ResolveStringSwitchMap (EmitContext ec)
3699 FullNamedExpression string_dictionary_type;
3701 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3702 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3704 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3706 new TypeExpression (TypeManager.string_type, loc),
3707 new TypeExpression (TypeManager.int32_type, loc)), loc);
3709 MemberAccess system_collections_generic = new MemberAccess (
3710 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3712 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3714 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3715 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3716 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3717 if (!field.Define ())
3719 ec.TypeContainer.PartialContainer.AddField (field);
3721 ArrayList init = new ArrayList ();
3724 string value = null;
3725 foreach (SwitchSection section in Sections) {
3726 foreach (SwitchLabel sl in section.Labels) {
3727 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3732 value = (string) sl.Converted;
3733 ArrayList init_args = new ArrayList (2);
3734 init_args.Add (new StringLiteral (value, sl.Location));
3735 init_args.Add (new IntConstant (counter, loc));
3736 init.Add (new CollectionElementInitializer (init_args, loc));
3742 Elements.Add (counter, section.Labels [0]);
3746 ArrayList args = new ArrayList (1);
3747 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3748 Expression initializer = new NewInitialize (string_dictionary_type, args,
3749 new CollectionOrObjectInitializers (init, loc), loc);
3751 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3752 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3755 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3757 ILGenerator ig = ec.ig;
3758 Label l_initialized = ig.DefineLabel ();
3761 // Skip initialization when value is null
3763 value.EmitBranchable (ec, null_target, false);
3766 // Check if string dictionary is initialized and initialize
3768 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3769 string_dictionary.EmitStatement (ec);
3770 ig.MarkLabel (l_initialized);
3772 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3775 ArrayList get_value_args = new ArrayList (2);
3776 get_value_args.Add (new Argument (value));
3777 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3778 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3779 if (get_item == null)
3783 // A value was not found, go to default case
3785 get_item.EmitBranchable (ec, default_target, false);
3787 ArrayList get_value_args = new ArrayList (1);
3788 get_value_args.Add (value);
3790 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3791 if (get_item == null)
3794 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3795 get_item_object.EmitAssign (ec, get_item, true, false);
3796 ec.ig.Emit (OpCodes.Brfalse, default_target);
3798 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3799 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3801 get_item_int.EmitStatement (ec);
3802 get_item_object.Release (ec);
3804 TableSwitchEmit (ec, string_switch_variable);
3805 string_switch_variable.Release (ec);
3808 protected override void DoEmit (EmitContext ec)
3810 ILGenerator ig = ec.ig;
3812 default_target = ig.DefineLabel ();
3813 null_target = ig.DefineLabel ();
3815 // Store variable for comparission purposes
3816 // TODO: Don't duplicate non-captured VariableReference
3817 LocalTemporary value;
3819 value = new LocalTemporary (SwitchType);
3821 unwrap.EmitCheck (ec);
3822 ig.Emit (OpCodes.Brfalse, null_target);
3826 } else if (!is_constant) {
3827 value = new LocalTemporary (SwitchType);
3834 // Setup the codegen context
3836 Label old_end = ec.LoopEnd;
3837 Switch old_switch = ec.Switch;
3839 ec.LoopEnd = ig.DefineLabel ();
3844 if (constant_section != null)
3845 constant_section.Block.Emit (ec);
3846 } else if (string_dictionary != null) {
3847 DoEmitStringSwitch (value, ec);
3849 TableSwitchEmit (ec, value);
3855 // Restore context state.
3856 ig.MarkLabel (ec.LoopEnd);
3859 // Restore the previous context
3861 ec.LoopEnd = old_end;
3862 ec.Switch = old_switch;
3865 protected override void CloneTo (CloneContext clonectx, Statement t)
3867 Switch target = (Switch) t;
3869 target.Expr = Expr.Clone (clonectx);
3870 target.Sections = new ArrayList ();
3871 foreach (SwitchSection ss in Sections){
3872 target.Sections.Add (ss.Clone (clonectx));
3877 // A place where execution can restart in an iterator
3878 public abstract class ResumableStatement : Statement
3881 protected Label resume_point;
3883 public Label PrepareForEmit (EmitContext ec)
3887 resume_point = ec.ig.DefineLabel ();
3889 return resume_point;
3892 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3896 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3901 // Base class for statements that are implemented in terms of try...finally
3902 public abstract class ExceptionStatement : ResumableStatement
3906 protected abstract void EmitPreTryBody (EmitContext ec);
3907 protected abstract void EmitTryBody (EmitContext ec);
3908 protected abstract void EmitFinallyBody (EmitContext ec);
3910 protected sealed override void DoEmit (EmitContext ec)
3912 ILGenerator ig = ec.ig;
3914 EmitPreTryBody (ec);
3916 if (resume_points != null) {
3917 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3918 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3921 ig.BeginExceptionBlock ();
3923 if (resume_points != null) {
3924 ig.MarkLabel (resume_point);
3926 // For normal control flow, we want to fall-through the Switch
3927 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3928 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3929 IntConstant.EmitInt (ig, first_resume_pc);
3930 ig.Emit (OpCodes.Sub);
3932 Label [] labels = new Label [resume_points.Count];
3933 for (int i = 0; i < resume_points.Count; ++i)
3934 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3935 ig.Emit (OpCodes.Switch, labels);
3940 ig.BeginFinallyBlock ();
3942 Label start_finally = ec.ig.DefineLabel ();
3943 if (resume_points != null) {
3944 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3945 ig.Emit (OpCodes.Brfalse_S, start_finally);
3946 ig.Emit (OpCodes.Endfinally);
3949 ig.MarkLabel (start_finally);
3950 EmitFinallyBody (ec);
3952 ig.EndExceptionBlock ();
3955 public void SomeCodeFollows ()
3957 code_follows = true;
3960 protected void ResolveReachability (EmitContext ec)
3962 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3963 // So, ensure there's some IL code after this statement.
3964 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3965 ec.NeedReturnLabel ();
3969 ArrayList resume_points;
3970 int first_resume_pc;
3971 public void AddResumePoint (ResumableStatement stmt, int pc)
3973 if (resume_points == null) {
3974 resume_points = new ArrayList ();
3975 first_resume_pc = pc;
3978 if (pc != first_resume_pc + resume_points.Count)
3979 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3981 resume_points.Add (stmt);
3984 Label dispose_try_block;
3985 bool prepared_for_dispose, emitted_dispose;
3986 public override Label PrepareForDispose (EmitContext ec, Label end)
3988 if (!prepared_for_dispose) {
3989 prepared_for_dispose = true;
3990 dispose_try_block = ec.ig.DefineLabel ();
3992 return dispose_try_block;
3995 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3997 if (emitted_dispose)
4000 emitted_dispose = true;
4002 ILGenerator ig = ec.ig;
4004 Label end_of_try = ig.DefineLabel ();
4006 // Ensure that the only way we can get into this code is through a dispatcher
4007 if (have_dispatcher)
4008 ig.Emit (OpCodes.Br, end);
4010 ig.BeginExceptionBlock ();
4012 ig.MarkLabel (dispose_try_block);
4014 Label [] labels = null;
4015 for (int i = 0; i < resume_points.Count; ++i) {
4016 ResumableStatement s = (ResumableStatement) resume_points [i];
4017 Label ret = s.PrepareForDispose (ec, end_of_try);
4018 if (ret.Equals (end_of_try) && labels == null)
4020 if (labels == null) {
4021 labels = new Label [resume_points.Count];
4022 for (int j = 0; j < i; ++j)
4023 labels [j] = end_of_try;
4028 if (labels != null) {
4030 for (j = 1; j < labels.Length; ++j)
4031 if (!labels [0].Equals (labels [j]))
4033 bool emit_dispatcher = j < labels.Length;
4035 if (emit_dispatcher) {
4036 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4037 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4038 IntConstant.EmitInt (ig, first_resume_pc);
4039 ig.Emit (OpCodes.Sub);
4040 ig.Emit (OpCodes.Switch, labels);
4041 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4044 foreach (ResumableStatement s in resume_points)
4045 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4048 ig.MarkLabel (end_of_try);
4050 ig.BeginFinallyBlock ();
4052 EmitFinallyBody (ec);
4054 ig.EndExceptionBlock ();
4058 public class Lock : ExceptionStatement {
4060 public Statement Statement;
4061 TemporaryVariable temp;
4063 public Lock (Expression expr, Statement stmt, Location l)
4070 public override bool Resolve (EmitContext ec)
4072 expr = expr.Resolve (ec);
4076 if (!TypeManager.IsReferenceType (expr.Type)){
4077 Report.Error (185, loc,
4078 "`{0}' is not a reference type as required by the lock statement",
4079 TypeManager.CSharpName (expr.Type));
4083 ec.StartFlowBranching (this);
4084 bool ok = Statement.Resolve (ec);
4085 ec.EndFlowBranching ();
4087 ResolveReachability (ec);
4089 // Avoid creating libraries that reference the internal
4092 if (t == TypeManager.null_type)
4093 t = TypeManager.object_type;
4095 temp = new TemporaryVariable (t, loc);
4098 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4099 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4100 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4101 monitor_type, "Enter", loc, TypeManager.object_type);
4102 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4103 monitor_type, "Exit", loc, TypeManager.object_type);
4109 protected override void EmitPreTryBody (EmitContext ec)
4111 ILGenerator ig = ec.ig;
4113 temp.EmitAssign (ec, expr);
4115 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4118 protected override void EmitTryBody (EmitContext ec)
4120 Statement.Emit (ec);
4123 protected override void EmitFinallyBody (EmitContext ec)
4126 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4129 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4131 expr.MutateHoistedGenericType (storey);
4132 temp.MutateHoistedGenericType (storey);
4133 Statement.MutateHoistedGenericType (storey);
4136 protected override void CloneTo (CloneContext clonectx, Statement t)
4138 Lock target = (Lock) t;
4140 target.expr = expr.Clone (clonectx);
4141 target.Statement = Statement.Clone (clonectx);
4145 public class Unchecked : Statement {
4148 public Unchecked (Block b)
4154 public override bool Resolve (EmitContext ec)
4156 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4157 return Block.Resolve (ec);
4160 protected override void DoEmit (EmitContext ec)
4162 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4166 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4168 Block.MutateHoistedGenericType (storey);
4171 protected override void CloneTo (CloneContext clonectx, Statement t)
4173 Unchecked target = (Unchecked) t;
4175 target.Block = clonectx.LookupBlock (Block);
4179 public class Checked : Statement {
4182 public Checked (Block b)
4185 b.Unchecked = false;
4188 public override bool Resolve (EmitContext ec)
4190 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4191 return Block.Resolve (ec);
4194 protected override void DoEmit (EmitContext ec)
4196 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4200 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4202 Block.MutateHoistedGenericType (storey);
4205 protected override void CloneTo (CloneContext clonectx, Statement t)
4207 Checked target = (Checked) t;
4209 target.Block = clonectx.LookupBlock (Block);
4213 public class Unsafe : Statement {
4216 public Unsafe (Block b)
4219 Block.Unsafe = true;
4222 public override bool Resolve (EmitContext ec)
4224 using (ec.With (EmitContext.Flags.InUnsafe, true))
4225 return Block.Resolve (ec);
4228 protected override void DoEmit (EmitContext ec)
4230 using (ec.With (EmitContext.Flags.InUnsafe, true))
4234 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4236 Block.MutateHoistedGenericType (storey);
4239 protected override void CloneTo (CloneContext clonectx, Statement t)
4241 Unsafe target = (Unsafe) t;
4243 target.Block = clonectx.LookupBlock (Block);
4250 public class Fixed : Statement {
4252 ArrayList declarators;
4253 Statement statement;
4258 abstract class Emitter
4260 protected LocalInfo vi;
4261 protected Expression converted;
4263 protected Emitter (Expression expr, LocalInfo li)
4269 public abstract void Emit (EmitContext ec);
4270 public abstract void EmitExit (EmitContext ec);
4273 class ExpressionEmitter : Emitter {
4274 public ExpressionEmitter (Expression converted, LocalInfo li) :
4275 base (converted, li)
4279 public override void Emit (EmitContext ec) {
4281 // Store pointer in pinned location
4283 converted.Emit (ec);
4287 public override void EmitExit (EmitContext ec)
4289 ec.ig.Emit (OpCodes.Ldc_I4_0);
4290 ec.ig.Emit (OpCodes.Conv_U);
4295 class StringEmitter : Emitter {
4296 class StringPtr : Expression
4300 public StringPtr (LocalBuilder b, Location l)
4303 eclass = ExprClass.Value;
4304 type = TypeManager.char_ptr_type;
4308 public override Expression CreateExpressionTree (EmitContext ec)
4310 throw new NotSupportedException ("ET");
4313 public override Expression DoResolve (EmitContext ec)
4315 // This should never be invoked, we are born in fully
4316 // initialized state.
4321 public override void Emit (EmitContext ec)
4323 if (TypeManager.int_get_offset_to_string_data == null) {
4324 // TODO: Move to resolve !!
4325 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4326 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4329 ILGenerator ig = ec.ig;
4331 ig.Emit (OpCodes.Ldloc, b);
4332 ig.Emit (OpCodes.Conv_I);
4333 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4334 ig.Emit (OpCodes.Add);
4338 LocalBuilder pinned_string;
4341 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4347 public override void Emit (EmitContext ec)
4349 ILGenerator ig = ec.ig;
4350 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4352 converted.Emit (ec);
4353 ig.Emit (OpCodes.Stloc, pinned_string);
4355 Expression sptr = new StringPtr (pinned_string, loc);
4356 converted = Convert.ImplicitConversionRequired (
4357 ec, sptr, vi.VariableType, loc);
4359 if (converted == null)
4362 converted.Emit (ec);
4366 public override void EmitExit (EmitContext ec)
4368 ec.ig.Emit (OpCodes.Ldnull);
4369 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4373 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4376 declarators = decls;
4381 public Statement Statement {
4382 get { return statement; }
4385 public override bool Resolve (EmitContext ec)
4388 Expression.UnsafeError (loc);
4392 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4393 if (texpr == null) {
4394 if (type is VarExpr)
4395 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4400 expr_type = texpr.Type;
4402 data = new Emitter [declarators.Count];
4404 if (!expr_type.IsPointer){
4405 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4410 foreach (Pair p in declarators){
4411 LocalInfo vi = (LocalInfo) p.First;
4412 Expression e = (Expression) p.Second;
4414 vi.VariableInfo.SetAssigned (ec);
4415 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4418 // The rules for the possible declarators are pretty wise,
4419 // but the production on the grammar is more concise.
4421 // So we have to enforce these rules here.
4423 // We do not resolve before doing the case 1 test,
4424 // because the grammar is explicit in that the token &
4425 // is present, so we need to test for this particular case.
4429 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4433 ec.InFixedInitializer = true;
4435 ec.InFixedInitializer = false;
4442 if (e.Type.IsArray){
4443 Type array_type = TypeManager.GetElementType (e.Type);
4446 // Provided that array_type is unmanaged,
4448 if (!TypeManager.VerifyUnManaged (array_type, loc))
4452 // and T* is implicitly convertible to the
4453 // pointer type given in the fixed statement.
4455 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4457 Expression converted = Convert.ImplicitConversionRequired (
4458 ec, array_ptr, vi.VariableType, loc);
4459 if (converted == null)
4463 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4465 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4466 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4467 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4468 new NullPointer (loc),
4471 converted = converted.Resolve (ec);
4473 data [i] = new ExpressionEmitter (converted, vi);
4482 if (e.Type == TypeManager.string_type){
4483 data [i] = new StringEmitter (e, vi, loc);
4488 // Case 4: fixed buffer
4489 if (e is FixedBufferPtr) {
4490 data [i++] = new ExpressionEmitter (e, vi);
4495 // Case 1: & object.
4497 Unary u = e as Unary;
4498 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4499 IVariableReference vr = u.Expr as IVariableReference;
4500 if (vr == null || !vr.IsFixed) {
4501 data [i] = new ExpressionEmitter (e, vi);
4505 if (data [i++] == null)
4506 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4508 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4511 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4512 bool ok = statement.Resolve (ec);
4513 bool flow_unreachable = ec.EndFlowBranching ();
4514 has_ret = flow_unreachable;
4519 protected override void DoEmit (EmitContext ec)
4521 for (int i = 0; i < data.Length; i++) {
4525 statement.Emit (ec);
4531 // Clear the pinned variable
4533 for (int i = 0; i < data.Length; i++) {
4534 data [i].EmitExit (ec);
4538 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4540 // Fixed statement cannot be used inside anonymous methods or lambdas
4541 throw new NotSupportedException ();
4544 protected override void CloneTo (CloneContext clonectx, Statement t)
4546 Fixed target = (Fixed) t;
4548 target.type = type.Clone (clonectx);
4549 target.declarators = new ArrayList (declarators.Count);
4550 foreach (Pair p in declarators) {
4551 LocalInfo vi = (LocalInfo) p.First;
4552 Expression e = (Expression) p.Second;
4554 target.declarators.Add (
4555 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4558 target.statement = statement.Clone (clonectx);
4562 public class Catch : Statement {
4563 public readonly string Name;
4565 public Block VarBlock;
4567 Expression type_expr;
4570 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4575 VarBlock = var_block;
4579 public Type CatchType {
4585 public bool IsGeneral {
4587 return type_expr == null;
4591 protected override void DoEmit (EmitContext ec)
4593 ILGenerator ig = ec.ig;
4595 if (CatchType != null)
4596 ig.BeginCatchBlock (CatchType);
4598 ig.BeginCatchBlock (TypeManager.object_type);
4600 if (VarBlock != null)
4604 // TODO: Move to resolve
4605 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4609 if (lvr.IsHoisted) {
4610 LocalTemporary lt = new LocalTemporary (lvr.Type);
4614 // Variable is at the top of the stack
4615 source = EmptyExpression.Null;
4618 lvr.EmitAssign (ec, source, false, false);
4620 ig.Emit (OpCodes.Pop);
4625 public override bool Resolve (EmitContext ec)
4627 using (ec.With (EmitContext.Flags.InCatch, true)) {
4628 if (type_expr != null) {
4629 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4635 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4636 Error (155, "The type caught or thrown must be derived from System.Exception");
4642 if (!Block.Resolve (ec))
4645 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4646 // emit the "unused variable" warnings.
4647 if (VarBlock != null)
4648 return VarBlock.Resolve (ec);
4654 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4657 type = storey.MutateType (type);
4658 if (VarBlock != null)
4659 VarBlock.MutateHoistedGenericType (storey);
4660 Block.MutateHoistedGenericType (storey);
4663 protected override void CloneTo (CloneContext clonectx, Statement t)
4665 Catch target = (Catch) t;
4667 if (type_expr != null)
4668 target.type_expr = type_expr.Clone (clonectx);
4669 if (VarBlock != null)
4670 target.VarBlock = clonectx.LookupBlock (VarBlock);
4671 target.Block = clonectx.LookupBlock (Block);
4675 public class TryFinally : ExceptionStatement {
4679 public TryFinally (Statement stmt, Block fini, Location l)
4686 public override bool Resolve (EmitContext ec)
4690 ec.StartFlowBranching (this);
4692 if (!stmt.Resolve (ec))
4696 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4697 using (ec.With (EmitContext.Flags.InFinally, true)) {
4698 if (!fini.Resolve (ec))
4702 ec.EndFlowBranching ();
4704 ResolveReachability (ec);
4709 protected override void EmitPreTryBody (EmitContext ec)
4713 protected override void EmitTryBody (EmitContext ec)
4718 protected override void EmitFinallyBody (EmitContext ec)
4723 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4725 stmt.MutateHoistedGenericType (storey);
4726 fini.MutateHoistedGenericType (storey);
4729 protected override void CloneTo (CloneContext clonectx, Statement t)
4731 TryFinally target = (TryFinally) t;
4733 target.stmt = (Statement) stmt.Clone (clonectx);
4735 target.fini = clonectx.LookupBlock (fini);
4739 public class TryCatch : Statement {
4741 public ArrayList Specific;
4742 public Catch General;
4743 bool inside_try_finally, code_follows;
4745 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4748 this.Specific = catch_clauses;
4749 this.General = null;
4750 this.inside_try_finally = inside_try_finally;
4752 for (int i = 0; i < catch_clauses.Count; ++i) {
4753 Catch c = (Catch) catch_clauses [i];
4755 if (i != catch_clauses.Count - 1)
4756 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4758 catch_clauses.RemoveAt (i);
4766 public override bool Resolve (EmitContext ec)
4770 ec.StartFlowBranching (this);
4772 if (!Block.Resolve (ec))
4775 Type[] prev_catches = new Type [Specific.Count];
4777 foreach (Catch c in Specific){
4778 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4780 if (c.Name != null) {
4781 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4783 throw new Exception ();
4785 vi.VariableInfo = null;
4788 if (!c.Resolve (ec))
4791 Type resolved_type = c.CatchType;
4792 for (int ii = 0; ii < last_index; ++ii) {
4793 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4794 Report.Error (160, c.loc,
4795 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4796 TypeManager.CSharpName (prev_catches [ii]));
4801 prev_catches [last_index++] = resolved_type;
4804 if (General != null) {
4805 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4806 foreach (Catch c in Specific){
4807 if (c.CatchType == TypeManager.exception_type) {
4808 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'");
4813 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4815 if (!General.Resolve (ec))
4819 ec.EndFlowBranching ();
4821 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4822 // So, ensure there's some IL code after this statement
4823 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4824 ec.NeedReturnLabel ();
4829 public void SomeCodeFollows ()
4831 code_follows = true;
4834 protected override void DoEmit (EmitContext ec)
4836 ILGenerator ig = ec.ig;
4838 if (!inside_try_finally)
4839 ig.BeginExceptionBlock ();
4843 foreach (Catch c in Specific)
4846 if (General != null)
4849 if (!inside_try_finally)
4850 ig.EndExceptionBlock ();
4853 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4855 Block.MutateHoistedGenericType (storey);
4857 if (General != null)
4858 General.MutateHoistedGenericType (storey);
4859 if (Specific != null) {
4860 foreach (Catch c in Specific)
4861 c.MutateHoistedGenericType (storey);
4865 protected override void CloneTo (CloneContext clonectx, Statement t)
4867 TryCatch target = (TryCatch) t;
4869 target.Block = clonectx.LookupBlock (Block);
4870 if (General != null)
4871 target.General = (Catch) General.Clone (clonectx);
4872 if (Specific != null){
4873 target.Specific = new ArrayList ();
4874 foreach (Catch c in Specific)
4875 target.Specific.Add (c.Clone (clonectx));
4880 public class UsingTemporary : ExceptionStatement {
4881 TemporaryVariable local_copy;
4882 public Statement Statement;
4886 public UsingTemporary (Expression expr, Statement stmt, Location l)
4893 public override bool Resolve (EmitContext ec)
4895 expr = expr.Resolve (ec);
4899 expr_type = expr.Type;
4901 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4902 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4903 Using.Error_IsNotConvertibleToIDisposable (expr);
4908 local_copy = new TemporaryVariable (expr_type, loc);
4909 local_copy.Resolve (ec);
4911 ec.StartFlowBranching (this);
4913 bool ok = Statement.Resolve (ec);
4915 ec.EndFlowBranching ();
4917 ResolveReachability (ec);
4919 if (TypeManager.void_dispose_void == null) {
4920 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4921 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4927 protected override void EmitPreTryBody (EmitContext ec)
4929 local_copy.EmitAssign (ec, expr);
4932 protected override void EmitTryBody (EmitContext ec)
4934 Statement.Emit (ec);
4937 protected override void EmitFinallyBody (EmitContext ec)
4939 ILGenerator ig = ec.ig;
4940 if (!TypeManager.IsStruct (expr_type)) {
4941 Label skip = ig.DefineLabel ();
4942 local_copy.Emit (ec);
4943 ig.Emit (OpCodes.Brfalse, skip);
4944 local_copy.Emit (ec);
4945 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4946 ig.MarkLabel (skip);
4950 Expression ml = Expression.MemberLookup (
4951 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4952 "Dispose", Location.Null);
4954 if (!(ml is MethodGroupExpr)) {
4955 local_copy.Emit (ec);
4956 ig.Emit (OpCodes.Box, expr_type);
4957 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4961 MethodInfo mi = null;
4963 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4964 if (TypeManager.GetParameterData (mk).Count == 0) {
4971 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4975 local_copy.AddressOf (ec, AddressOp.Load);
4976 ig.Emit (OpCodes.Call, mi);
4979 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4981 expr_type = storey.MutateType (expr_type);
4982 local_copy.MutateHoistedGenericType (storey);
4983 Statement.MutateHoistedGenericType (storey);
4986 protected override void CloneTo (CloneContext clonectx, Statement t)
4988 UsingTemporary target = (UsingTemporary) t;
4990 target.expr = expr.Clone (clonectx);
4991 target.Statement = Statement.Clone (clonectx);
4995 public class Using : ExceptionStatement {
4997 public Statement EmbeddedStatement {
4998 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5004 Expression converted_var;
5005 ExpressionStatement assign;
5007 public Using (Expression var, Expression init, Statement stmt, Location l)
5015 bool ResolveVariable (EmitContext ec)
5017 ExpressionStatement a = new SimpleAssign (var, init, loc);
5018 a = a.ResolveStatement (ec);
5024 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
5025 converted_var = var;
5029 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
5031 Error_IsNotConvertibleToIDisposable (var);
5040 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5042 Report.SymbolRelatedToPreviousError (expr.Type);
5043 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5044 expr.GetSignatureForError ());
5047 protected override void EmitPreTryBody (EmitContext ec)
5049 assign.EmitStatement (ec);
5052 protected override void EmitTryBody (EmitContext ec)
5057 protected override void EmitFinallyBody (EmitContext ec)
5059 ILGenerator ig = ec.ig;
5061 if (!TypeManager.IsStruct (var.Type)) {
5062 Label skip = ig.DefineLabel ();
5064 ig.Emit (OpCodes.Brfalse, skip);
5065 converted_var.Emit (ec);
5066 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5067 ig.MarkLabel (skip);
5069 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5071 if (!(ml is MethodGroupExpr)) {
5073 ig.Emit (OpCodes.Box, var.Type);
5074 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5076 MethodInfo mi = null;
5078 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5079 if (TypeManager.GetParameterData (mk).Count == 0) {
5086 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5090 IMemoryLocation mloc = (IMemoryLocation) var;
5092 mloc.AddressOf (ec, AddressOp.Load);
5093 ig.Emit (OpCodes.Call, mi);
5098 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5100 assign.MutateHoistedGenericType (storey);
5101 var.MutateHoistedGenericType (storey);
5102 stmt.MutateHoistedGenericType (storey);
5105 public override bool Resolve (EmitContext ec)
5107 if (!ResolveVariable (ec))
5110 ec.StartFlowBranching (this);
5112 bool ok = stmt.Resolve (ec);
5114 ec.EndFlowBranching ();
5116 ResolveReachability (ec);
5118 if (TypeManager.void_dispose_void == null) {
5119 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5120 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5126 protected override void CloneTo (CloneContext clonectx, Statement t)
5128 Using target = (Using) t;
5130 target.var = var.Clone (clonectx);
5131 target.init = init.Clone (clonectx);
5132 target.stmt = stmt.Clone (clonectx);
5137 /// Implementation of the foreach C# statement
5139 public class Foreach : Statement {
5141 sealed class ArrayForeach : Statement
5143 class ArrayCounter : TemporaryVariable
5145 StatementExpression increment;
5147 public ArrayCounter (Location loc)
5148 : base (TypeManager.int32_type, loc)
5152 public void ResolveIncrement (EmitContext ec)
5154 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5155 increment.Resolve (ec);
5158 public void EmitIncrement (EmitContext ec)
5160 increment.Emit (ec);
5164 readonly Foreach for_each;
5165 readonly Statement statement;
5168 TemporaryVariable[] lengths;
5169 Expression [] length_exprs;
5170 ArrayCounter[] counter;
5172 TemporaryVariable copy;
5175 public ArrayForeach (Foreach @foreach, int rank)
5177 for_each = @foreach;
5178 statement = for_each.statement;
5181 counter = new ArrayCounter [rank];
5182 length_exprs = new Expression [rank];
5185 // Only use temporary length variables when dealing with
5186 // multi-dimensional arrays
5189 lengths = new TemporaryVariable [rank];
5192 protected override void CloneTo (CloneContext clonectx, Statement target)
5194 throw new NotImplementedException ();
5197 public override bool Resolve (EmitContext ec)
5199 copy = new TemporaryVariable (for_each.expr.Type, loc);
5202 int rank = length_exprs.Length;
5203 ArrayList list = new ArrayList (rank);
5204 for (int i = 0; i < rank; i++) {
5205 counter [i] = new ArrayCounter (loc);
5206 counter [i].ResolveIncrement (ec);
5209 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5211 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5212 lengths [i].Resolve (ec);
5214 ArrayList args = new ArrayList (1);
5215 args.Add (new Argument (new IntConstant (i, loc)));
5216 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5219 list.Add (counter [i]);
5222 access = new ElementAccess (copy, list).Resolve (ec);
5226 Expression var_type = for_each.type;
5227 VarExpr ve = var_type as VarExpr;
5229 // Infer implicitly typed local variable from foreach array type
5230 var_type = new TypeExpression (access.Type, ve.Location);
5233 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5234 if (var_type == null)
5237 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5243 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5244 ec.CurrentBranching.CreateSibling ();
5246 for_each.variable = for_each.variable.ResolveLValue (ec, conv, loc);
5247 if (for_each.variable == null)
5250 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5251 if (!statement.Resolve (ec))
5253 ec.EndFlowBranching ();
5255 // There's no direct control flow from the end of the embedded statement to the end of the loop
5256 ec.CurrentBranching.CurrentUsageVector.Goto ();
5258 ec.EndFlowBranching ();
5263 protected override void DoEmit (EmitContext ec)
5265 ILGenerator ig = ec.ig;
5267 copy.EmitAssign (ec, for_each.expr);
5269 int rank = length_exprs.Length;
5270 Label[] test = new Label [rank];
5271 Label[] loop = new Label [rank];
5273 for (int i = 0; i < rank; i++) {
5274 test [i] = ig.DefineLabel ();
5275 loop [i] = ig.DefineLabel ();
5277 if (lengths != null)
5278 lengths [i].EmitAssign (ec, length_exprs [i]);
5281 IntConstant zero = new IntConstant (0, loc);
5282 for (int i = 0; i < rank; i++) {
5283 counter [i].EmitAssign (ec, zero);
5285 ig.Emit (OpCodes.Br, test [i]);
5286 ig.MarkLabel (loop [i]);
5289 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5291 statement.Emit (ec);
5293 ig.MarkLabel (ec.LoopBegin);
5295 for (int i = rank - 1; i >= 0; i--){
5296 counter [i].EmitIncrement (ec);
5298 ig.MarkLabel (test [i]);
5299 counter [i].Emit (ec);
5301 if (lengths != null)
5302 lengths [i].Emit (ec);
5304 length_exprs [i].Emit (ec);
5306 ig.Emit (OpCodes.Blt, loop [i]);
5309 ig.MarkLabel (ec.LoopEnd);
5312 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5314 for_each.expr.MutateHoistedGenericType (storey);
5316 copy.MutateHoistedGenericType (storey);
5317 conv.MutateHoistedGenericType (storey);
5318 statement.MutateHoistedGenericType (storey);
5320 for (int i = 0; i < counter.Length; i++) {
5321 counter [i].MutateHoistedGenericType (storey);
5322 if (lengths != null)
5323 lengths [i].MutateHoistedGenericType (storey);
5328 sealed class CollectionForeach : Statement
5330 class CollectionForeachStatement : Statement
5333 Expression variable, current, conv;
5334 Statement statement;
5337 public CollectionForeachStatement (Type type, Expression variable,
5338 Expression current, Statement statement,
5342 this.variable = variable;
5343 this.current = current;
5344 this.statement = statement;
5348 protected override void CloneTo (CloneContext clonectx, Statement target)
5350 throw new NotImplementedException ();
5353 public override bool Resolve (EmitContext ec)
5355 current = current.Resolve (ec);
5356 if (current == null)
5359 conv = Convert.ExplicitConversion (ec, current, type, loc);
5363 assign = new SimpleAssign (variable, conv, loc);
5364 if (assign.Resolve (ec) == null)
5367 if (!statement.Resolve (ec))
5373 protected override void DoEmit (EmitContext ec)
5375 assign.EmitStatement (ec);
5376 statement.Emit (ec);
5379 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5381 assign.MutateHoistedGenericType (storey);
5382 statement.MutateHoistedGenericType (storey);
5386 Expression variable, expr;
5387 Statement statement;
5389 TemporaryVariable enumerator;
5394 MethodGroupExpr get_enumerator;
5395 PropertyExpr get_current;
5396 MethodInfo move_next;
5397 Expression var_type;
5398 Type enumerator_type;
5399 bool enumerator_found;
5401 public CollectionForeach (Expression var_type, Expression var,
5402 Expression expr, Statement stmt, Location l)
5404 this.var_type = var_type;
5405 this.variable = var;
5411 protected override void CloneTo (CloneContext clonectx, Statement target)
5413 throw new NotImplementedException ();
5416 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5418 Type return_type = mi.ReturnType;
5421 // Ok, we can access it, now make sure that we can do something
5422 // with this `GetEnumerator'
5425 if (return_type == TypeManager.ienumerator_type ||
5426 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5428 // If it is not an interface, lets try to find the methods ourselves.
5429 // For example, if we have:
5430 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5431 // We can avoid the iface call. This is a runtime perf boost.
5432 // even bigger if we have a ValueType, because we avoid the cost
5435 // We have to make sure that both methods exist for us to take
5436 // this path. If one of the methods does not exist, we will just
5437 // use the interface. Sadly, this complex if statement is the only
5438 // way I could do this without a goto
5441 if (TypeManager.bool_movenext_void == null) {
5442 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5443 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5446 if (TypeManager.ienumerator_getcurrent == null) {
5447 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5448 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5453 // Prefer a generic enumerator over a non-generic one.
5455 if (return_type.IsInterface && return_type.IsGenericType) {
5456 enumerator_type = return_type;
5457 if (!FetchGetCurrent (ec, return_type))
5458 get_current = new PropertyExpr (
5459 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5460 if (!FetchMoveNext (return_type))
5461 move_next = TypeManager.bool_movenext_void;
5466 if (return_type.IsInterface ||
5467 !FetchMoveNext (return_type) ||
5468 !FetchGetCurrent (ec, return_type)) {
5469 enumerator_type = return_type;
5470 move_next = TypeManager.bool_movenext_void;
5471 get_current = new PropertyExpr (
5472 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5477 // Ok, so they dont return an IEnumerable, we will have to
5478 // find if they support the GetEnumerator pattern.
5481 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5482 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",
5483 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5488 enumerator_type = return_type;
5494 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5496 bool FetchMoveNext (Type t)
5498 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5500 BindingFlags.Public | BindingFlags.Instance,
5503 foreach (MemberInfo m in move_next_list){
5504 MethodInfo mi = (MethodInfo) m;
5506 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5507 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5517 // Retrieves a `public T get_Current ()' method from the Type `t'
5519 bool FetchGetCurrent (EmitContext ec, Type t)
5521 PropertyExpr pe = Expression.MemberLookup (
5522 ec.ContainerType, t, "Current", MemberTypes.Property,
5523 Expression.AllBindingFlags, loc) as PropertyExpr;
5531 void Error_Enumerator ()
5533 if (enumerator_found) {
5537 Report.Error (1579, loc,
5538 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5539 TypeManager.CSharpName (expr.Type));
5542 bool IsOverride (MethodInfo m)
5544 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5546 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5548 if (m is MethodBuilder)
5551 MethodInfo base_method = m.GetBaseDefinition ();
5552 return base_method != m;
5555 bool TryType (EmitContext ec, Type t)
5557 MethodGroupExpr mg = Expression.MemberLookup (
5558 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5559 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5563 MethodInfo result = null;
5564 MethodInfo tmp_move_next = null;
5565 PropertyExpr tmp_get_cur = null;
5566 Type tmp_enumerator_type = enumerator_type;
5567 foreach (MethodInfo mi in mg.Methods) {
5568 if (TypeManager.GetParameterData (mi).Count != 0)
5571 // Check whether GetEnumerator is public
5572 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5575 if (IsOverride (mi))
5578 enumerator_found = true;
5580 if (!GetEnumeratorFilter (ec, mi))
5583 if (result != null) {
5584 if (TypeManager.IsGenericType (result.ReturnType)) {
5585 if (!TypeManager.IsGenericType (mi.ReturnType))
5588 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5589 Report.SymbolRelatedToPreviousError (t);
5590 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5591 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5592 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5596 // Always prefer generics enumerators
5597 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5598 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5599 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5602 Report.SymbolRelatedToPreviousError (result);
5603 Report.SymbolRelatedToPreviousError (mi);
5604 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5605 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5610 tmp_move_next = move_next;
5611 tmp_get_cur = get_current;
5612 tmp_enumerator_type = enumerator_type;
5613 if (mi.DeclaringType == t)
5617 if (result != null) {
5618 move_next = tmp_move_next;
5619 get_current = tmp_get_cur;
5620 enumerator_type = tmp_enumerator_type;
5621 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5622 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5624 if (t != expr.Type) {
5625 expr = Convert.ExplicitConversion (
5628 throw new InternalErrorException ();
5631 get_enumerator.InstanceExpression = expr;
5632 get_enumerator.IsBase = t != expr.Type;
5640 bool ProbeCollectionType (EmitContext ec, Type t)
5642 int errors = Report.Errors;
5643 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5644 if (TryType (ec, tt))
5649 if (Report.Errors > errors)
5653 // Now try to find the method in the interfaces
5655 Type [] ifaces = TypeManager.GetInterfaces (t);
5656 foreach (Type i in ifaces){
5657 if (TryType (ec, i))
5664 public override bool Resolve (EmitContext ec)
5666 enumerator_type = TypeManager.ienumerator_type;
5668 if (!ProbeCollectionType (ec, expr.Type)) {
5669 Error_Enumerator ();
5673 VarExpr ve = var_type as VarExpr;
5675 // Infer implicitly typed local variable from foreach enumerable type
5676 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5679 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5680 if (var_type == null)
5683 enumerator = new TemporaryVariable (enumerator_type, loc);
5684 enumerator.Resolve (ec);
5686 init = new Invocation (get_enumerator, null);
5687 init = init.Resolve (ec);
5691 Expression move_next_expr;
5693 MemberInfo[] mi = new MemberInfo[] { move_next };
5694 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5695 mg.InstanceExpression = enumerator;
5697 move_next_expr = new Invocation (mg, null);
5700 get_current.InstanceExpression = enumerator;
5702 Statement block = new CollectionForeachStatement (
5703 var_type.Type, variable, get_current, statement, loc);
5705 loop = new While (move_next_expr, block, loc);
5708 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5709 if (implements_idisposable || !enumerator_type.IsSealed) {
5710 wrapper = new DisposableWrapper (this, implements_idisposable);
5712 wrapper = new NonDisposableWrapper (this);
5715 return wrapper.Resolve (ec);
5718 protected override void DoEmit (EmitContext ec)
5723 class NonDisposableWrapper : Statement {
5724 CollectionForeach parent;
5726 internal NonDisposableWrapper (CollectionForeach parent)
5728 this.parent = parent;
5731 protected override void CloneTo (CloneContext clonectx, Statement target)
5733 throw new NotSupportedException ();
5736 public override bool Resolve (EmitContext ec)
5738 return parent.ResolveLoop (ec);
5741 protected override void DoEmit (EmitContext ec)
5743 parent.EmitLoopInit (ec);
5744 parent.EmitLoopBody (ec);
5747 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5749 throw new NotSupportedException ();
5753 sealed class DisposableWrapper : ExceptionStatement
5755 CollectionForeach parent;
5756 bool implements_idisposable;
5758 internal DisposableWrapper (CollectionForeach parent, bool implements)
5760 this.parent = parent;
5761 this.implements_idisposable = implements;
5764 protected override void CloneTo (CloneContext clonectx, Statement target)
5766 throw new NotSupportedException ();
5769 public override bool Resolve (EmitContext ec)
5773 ec.StartFlowBranching (this);
5775 if (!parent.ResolveLoop (ec))
5778 ec.EndFlowBranching ();
5780 ResolveReachability (ec);
5782 if (TypeManager.void_dispose_void == null) {
5783 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5784 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5789 protected override void EmitPreTryBody (EmitContext ec)
5791 parent.EmitLoopInit (ec);
5794 protected override void EmitTryBody (EmitContext ec)
5796 parent.EmitLoopBody (ec);
5799 protected override void EmitFinallyBody (EmitContext ec)
5801 ILGenerator ig = ec.ig;
5803 Expression instance = parent.enumerator;
5804 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5806 parent.enumerator.Emit (ec);
5808 Label call_dispose = ig.DefineLabel ();
5810 if (!implements_idisposable) {
5811 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5812 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5818 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5820 // using 'endfinally' to empty the evaluation stack
5821 ig.Emit (OpCodes.Endfinally);
5822 ig.MarkLabel (call_dispose);
5825 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, new ArrayList (0), loc);
5828 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5830 throw new NotSupportedException ();
5834 bool ResolveLoop (EmitContext ec)
5836 return loop.Resolve (ec);
5839 void EmitLoopInit (EmitContext ec)
5841 enumerator.EmitAssign (ec, init);
5844 void EmitLoopBody (EmitContext ec)
5849 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5851 enumerator_type = storey.MutateType (enumerator_type);
5852 init.MutateHoistedGenericType (storey);
5853 loop.MutateHoistedGenericType (storey);
5858 Expression variable;
5860 Statement statement;
5862 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5863 Statement stmt, Location l)
5866 this.variable = var;
5872 public Statement Statement {
5873 get { return statement; }
5876 public override bool Resolve (EmitContext ec)
5878 expr = expr.Resolve (ec);
5883 Report.Error (186, loc, "Use of null is not valid in this context");
5887 if (expr.Type == TypeManager.string_type) {
5888 statement = new ArrayForeach (this, 1);
5889 } else if (expr.Type.IsArray) {
5890 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5892 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5893 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5894 expr.ExprClassName);
5898 statement = new CollectionForeach (type, variable, expr, statement, loc);
5901 return statement.Resolve (ec);
5904 protected override void DoEmit (EmitContext ec)
5906 ILGenerator ig = ec.ig;
5908 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5909 ec.LoopBegin = ig.DefineLabel ();
5910 ec.LoopEnd = ig.DefineLabel ();
5912 statement.Emit (ec);
5914 ec.LoopBegin = old_begin;
5915 ec.LoopEnd = old_end;
5918 protected override void CloneTo (CloneContext clonectx, Statement t)
5920 Foreach target = (Foreach) t;
5922 target.type = type.Clone (clonectx);
5923 target.variable = variable.Clone (clonectx);
5924 target.expr = expr.Clone (clonectx);
5925 target.statement = statement.Clone (clonectx);
5928 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5930 statement.MutateHoistedGenericType (storey);