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 FullNamedExpression 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 (FullNamedExpression 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 ((FullNamedExpression) 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 ((FullNamedExpression) 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) {
2515 // Creates parent storey reference when hoisted this is accessible
2517 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2518 ExplicitBlock parent = Toplevel.Parent.Explicit;
2521 // Hoisted this exists in top-level parent storey only
2523 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2524 parent = parent.Parent.Explicit;
2526 am_storey.AddParentStoreyReference (parent.am_storey);
2529 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2532 am_storey.DefineType ();
2533 am_storey.ResolveType ();
2534 am_storey.Define ();
2535 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2537 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2538 if (ref_blocks != null) {
2539 foreach (ExplicitBlock ref_block in ref_blocks) {
2540 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2541 if (b.am_storey != null) {
2542 b.am_storey.AddParentStoreyReference (am_storey);
2544 // Stop propagation inside same top block
2545 if (b.Toplevel == Toplevel)
2550 b.HasCapturedVariable = true;
2559 internal IKnownVariable GetKnownVariable (string name)
2561 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2564 public bool HasCapturedThis
2566 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2567 get { return (flags & Flags.HasCapturedThis) != 0; }
2570 public bool HasCapturedVariable
2572 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2573 get { return (flags & Flags.HasCapturedVariable) != 0; }
2576 protected override void CloneTo (CloneContext clonectx, Statement t)
2578 ExplicitBlock target = (ExplicitBlock) t;
2579 target.known_variables = null;
2580 base.CloneTo (clonectx, t);
2584 public class ToplevelParameterInfo : IKnownVariable {
2585 public readonly ToplevelBlock Block;
2586 public readonly int Index;
2587 public VariableInfo VariableInfo;
2589 Block IKnownVariable.Block {
2590 get { return Block; }
2592 public Parameter Parameter {
2593 get { return Block.Parameters [Index]; }
2596 public Type ParameterType {
2597 get { return Block.Parameters.Types [Index]; }
2600 public Location Location {
2601 get { return Parameter.Location; }
2604 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2612 // A toplevel block contains extra information, the split is done
2613 // only to separate information that would otherwise bloat the more
2614 // lightweight Block.
2616 // In particular, this was introduced when the support for Anonymous
2617 // Methods was implemented.
2619 public class ToplevelBlock : ExplicitBlock
2622 // Block is converted to an expression
2624 sealed class BlockScopeExpression : Expression
2627 readonly ToplevelBlock block;
2629 public BlockScopeExpression (Expression child, ToplevelBlock block)
2635 public override Expression CreateExpressionTree (EmitContext ec)
2637 throw new NotSupportedException ();
2640 public override Expression DoResolve (EmitContext ec)
2645 block.ResolveMeta (ec, ParametersCompiled.EmptyReadOnlyParameters);
2646 child = child.Resolve (ec);
2650 eclass = child.eclass;
2655 public override void Emit (EmitContext ec)
2657 block.EmitMeta (ec);
2658 block.EmitScopeInitializers (ec);
2662 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2664 type = storey.MutateType (type);
2665 child.MutateHoistedGenericType (storey);
2666 block.MutateHoistedGenericType (storey);
2670 GenericMethod generic;
2671 FlowBranchingToplevel top_level_branching;
2672 protected ParametersCompiled parameters;
2673 ToplevelParameterInfo[] parameter_info;
2674 LocalInfo this_variable;
2676 public HoistedVariable HoistedThisVariable;
2679 // The parameters for the block.
2681 public ParametersCompiled Parameters {
2682 get { return parameters; }
2685 public GenericMethod GenericMethod {
2686 get { return generic; }
2689 public ToplevelBlock Container {
2690 get { return Parent == null ? null : Parent.Toplevel; }
2693 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2694 this (parent, (Flags) 0, parameters, start)
2698 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2699 this (parent, parameters, start)
2701 this.generic = generic;
2704 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2705 this (null, (Flags) 0, parameters, start)
2709 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2710 this (null, flags, parameters, start)
2714 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2715 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2716 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2717 base (null, flags, start, Location.Null)
2719 this.Toplevel = this;
2721 this.parameters = parameters;
2722 this.Parent = parent;
2724 parent.AddAnonymousChild (this);
2726 if (!this.parameters.IsEmpty)
2727 ProcessParameters ();
2730 public ToplevelBlock (Location loc)
2731 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2735 protected override void CloneTo (CloneContext clonectx, Statement t)
2737 ToplevelBlock target = (ToplevelBlock) t;
2738 base.CloneTo (clonectx, t);
2740 if (parameters.Count != 0)
2741 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2742 for (int i = 0; i < parameters.Count; ++i)
2743 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2746 public bool CheckError158 (string name, Location loc)
2748 if (AnonymousChildren != null) {
2749 foreach (ToplevelBlock child in AnonymousChildren) {
2750 if (!child.CheckError158 (name, loc))
2755 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2756 if (!c.DoCheckError158 (name, loc))
2763 void ProcessParameters ()
2765 int n = parameters.Count;
2766 parameter_info = new ToplevelParameterInfo [n];
2767 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2768 for (int i = 0; i < n; ++i) {
2769 parameter_info [i] = new ToplevelParameterInfo (this, i);
2771 Parameter p = parameters [i];
2775 string name = p.Name;
2776 if (CheckParentConflictName (top_parent, name, loc))
2777 AddKnownVariable (name, parameter_info [i]);
2780 // mark this block as "used" so that we create local declarations in a sub-block
2781 // FIXME: This appears to uncover a lot of bugs
2785 bool DoCheckError158 (string name, Location loc)
2787 LabeledStatement s = LookupLabel (name);
2789 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2790 Error_158 (name, loc);
2797 public override Expression CreateExpressionTree (EmitContext ec)
2799 if (statements.Count == 1) {
2800 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2801 if (scope_initializers != null)
2802 expr = new BlockScopeExpression (expr, this);
2807 return base.CreateExpressionTree (ec);
2811 // Reformats this block to be top-level iterator block
2813 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2817 // Creates block with original statements
2818 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2820 source.statements = new ArrayList (1);
2821 source.AddStatement (new Return (iterator, iterator.Location));
2822 source.IsIterator = false;
2824 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2825 source.am_storey = iterator_storey;
2826 return iterator_storey;
2829 public FlowBranchingToplevel TopLevelBranching {
2830 get { return top_level_branching; }
2834 // Returns a parameter reference expression for the given name,
2835 // or null if there is no such parameter
2837 public Expression GetParameterReference (string name, Location loc)
2839 for (ToplevelBlock t = this; t != null; t = t.Container) {
2840 Expression expr = t.GetParameterReferenceExpression (name, loc);
2848 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2850 int idx = parameters.GetParameterIndexByName (name);
2852 null : new ParameterReference (parameter_info [idx], loc);
2856 // Returns the "this" instance variable of this block.
2857 // See AddThisVariable() for more information.
2859 public LocalInfo ThisVariable {
2860 get { return this_variable; }
2864 // This is used by non-static `struct' constructors which do not have an
2865 // initializer - in this case, the constructor must initialize all of the
2866 // struct's fields. To do this, we add a "this" variable and use the flow
2867 // analysis code to ensure that it's been fully initialized before control
2868 // leaves the constructor.
2870 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2872 if (this_variable == null) {
2873 this_variable = new LocalInfo (ds, this, l);
2874 this_variable.Used = true;
2875 this_variable.IsThis = true;
2877 Variables.Add ("this", this_variable);
2880 return this_variable;
2883 public bool IsIterator {
2884 get { return (flags & Flags.IsIterator) != 0; }
2885 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2888 public bool IsThisAssigned (EmitContext ec)
2890 return this_variable == null || this_variable.IsThisAssigned (ec);
2893 public bool ResolveMeta (EmitContext ec, ParametersCompiled ip)
2895 int errors = Report.Errors;
2896 int orig_count = parameters.Count;
2898 if (top_level_branching != null)
2904 // Assert: orig_count != parameter.Count => orig_count == 0
2905 if (orig_count != 0 && orig_count != parameters.Count)
2906 throw new InternalErrorException ("parameter information mismatch");
2908 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2910 for (int i = 0; i < orig_count; ++i) {
2911 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2913 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2916 VariableInfo vi = new VariableInfo (ip, i, offset);
2917 parameter_info [i].VariableInfo = vi;
2918 offset += vi.Length;
2921 ResolveMeta (ec, offset);
2923 top_level_branching = ec.StartFlowBranching (this);
2925 return Report.Errors == errors;
2929 // Check whether all `out' parameters have been assigned.
2931 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2933 if (vector.IsUnreachable)
2936 int n = parameter_info == null ? 0 : parameter_info.Length;
2938 for (int i = 0; i < n; i++) {
2939 VariableInfo var = parameter_info [i].VariableInfo;
2944 if (vector.IsAssigned (var, false))
2947 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2952 public override void EmitMeta (EmitContext ec)
2954 parameters.ResolveVariable ();
2956 // Avoid declaring an IL variable for this_variable since it is not accessed
2957 // from the generated IL
2958 if (this_variable != null)
2959 Variables.Remove ("this");
2963 protected override void EmitSymbolInfo (EmitContext ec)
2965 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2966 if ((ae != null) && (ae.Storey != null))
2967 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2969 base.EmitSymbolInfo (ec);
2972 public override void Emit (EmitContext ec)
2975 ec.Mark (EndLocation);
2979 public class SwitchLabel {
2986 Label il_label_code;
2987 bool il_label_code_set;
2989 public static readonly object NullStringCase = new object ();
2992 // if expr == null, then it is the default case.
2994 public SwitchLabel (Expression expr, Location l)
3000 public Expression Label {
3006 public Location Location {
3010 public object Converted {
3016 public Label GetILLabel (EmitContext ec)
3019 il_label = ec.ig.DefineLabel ();
3020 il_label_set = true;
3025 public Label GetILLabelCode (EmitContext ec)
3027 if (!il_label_code_set){
3028 il_label_code = ec.ig.DefineLabel ();
3029 il_label_code_set = true;
3031 return il_label_code;
3035 // Resolves the expression, reduces it to a literal if possible
3036 // and then converts it to the requested type.
3038 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
3040 Expression e = label.Resolve (ec);
3045 Constant c = e as Constant;
3047 Report.Error (150, loc, "A constant value is expected");
3051 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3052 converted = NullStringCase;
3056 if (allow_nullable && c.GetValue () == null) {
3057 converted = NullStringCase;
3061 c = c.ImplicitConversionRequired (ec, required_type, loc);
3065 converted = c.GetValue ();
3069 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3072 if (converted == null)
3074 else if (converted == NullStringCase)
3077 label = converted.ToString ();
3079 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3080 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3083 public SwitchLabel Clone (CloneContext clonectx)
3085 return new SwitchLabel (label.Clone (clonectx), loc);
3089 public class SwitchSection {
3090 // An array of SwitchLabels.
3091 public readonly ArrayList Labels;
3092 public readonly Block Block;
3094 public SwitchSection (ArrayList labels, Block block)
3100 public SwitchSection Clone (CloneContext clonectx)
3102 ArrayList cloned_labels = new ArrayList ();
3104 foreach (SwitchLabel sl in cloned_labels)
3105 cloned_labels.Add (sl.Clone (clonectx));
3107 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3111 public class Switch : Statement {
3112 public ArrayList Sections;
3113 public Expression Expr;
3116 /// Maps constants whose type type SwitchType to their SwitchLabels.
3118 public IDictionary Elements;
3121 /// The governing switch type
3123 public Type SwitchType;
3128 Label default_target;
3130 Expression new_expr;
3133 SwitchSection constant_section;
3134 SwitchSection default_section;
3136 ExpressionStatement string_dictionary;
3137 FieldExpr switch_cache_field;
3138 static int unique_counter;
3141 // Nullable Types support
3143 Nullable.Unwrap unwrap;
3145 protected bool HaveUnwrap {
3146 get { return unwrap != null; }
3150 // The types allowed to be implicitly cast from
3151 // on the governing type
3153 static Type [] allowed_types;
3155 public Switch (Expression e, ArrayList sects, Location l)
3162 public bool GotDefault {
3164 return default_section != null;
3168 public Label DefaultTarget {
3170 return default_target;
3175 // Determines the governing type for a switch. The returned
3176 // expression might be the expression from the switch, or an
3177 // expression that includes any potential conversions to the
3178 // integral types or to string.
3180 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3184 if (t == TypeManager.byte_type ||
3185 t == TypeManager.sbyte_type ||
3186 t == TypeManager.ushort_type ||
3187 t == TypeManager.short_type ||
3188 t == TypeManager.uint32_type ||
3189 t == TypeManager.int32_type ||
3190 t == TypeManager.uint64_type ||
3191 t == TypeManager.int64_type ||
3192 t == TypeManager.char_type ||
3193 t == TypeManager.string_type ||
3194 t == TypeManager.bool_type ||
3195 TypeManager.IsEnumType (t))
3198 if (allowed_types == null){
3199 allowed_types = new Type [] {
3200 TypeManager.sbyte_type,
3201 TypeManager.byte_type,
3202 TypeManager.short_type,
3203 TypeManager.ushort_type,
3204 TypeManager.int32_type,
3205 TypeManager.uint32_type,
3206 TypeManager.int64_type,
3207 TypeManager.uint64_type,
3208 TypeManager.char_type,
3209 TypeManager.string_type
3214 // Try to find a *user* defined implicit conversion.
3216 // If there is no implicit conversion, or if there are multiple
3217 // conversions, we have to report an error
3219 Expression converted = null;
3220 foreach (Type tt in allowed_types){
3223 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3228 // Ignore over-worked ImplicitUserConversions that do
3229 // an implicit conversion in addition to the user conversion.
3231 if (!(e is UserCast))
3234 if (converted != null){
3235 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3245 // Performs the basic sanity checks on the switch statement
3246 // (looks for duplicate keys and non-constant expressions).
3248 // It also returns a hashtable with the keys that we will later
3249 // use to compute the switch tables
3251 bool CheckSwitch (EmitContext ec)
3254 Elements = Sections.Count > 10 ?
3255 (IDictionary)new Hashtable () :
3256 (IDictionary)new ListDictionary ();
3258 foreach (SwitchSection ss in Sections){
3259 foreach (SwitchLabel sl in ss.Labels){
3260 if (sl.Label == null){
3261 if (default_section != null){
3262 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3265 default_section = ss;
3269 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3274 object key = sl.Converted;
3275 if (key == SwitchLabel.NullStringCase)
3276 has_null_case = true;
3279 Elements.Add (key, sl);
3280 } catch (ArgumentException) {
3281 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3289 void EmitObjectInteger (ILGenerator ig, object k)
3292 IntConstant.EmitInt (ig, (int) k);
3293 else if (k is Constant) {
3294 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3297 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3300 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3302 IntConstant.EmitInt (ig, (int) (long) k);
3303 ig.Emit (OpCodes.Conv_I8);
3306 LongConstant.EmitLong (ig, (long) k);
3308 else if (k is ulong)
3310 ulong ul = (ulong) k;
3313 IntConstant.EmitInt (ig, unchecked ((int) ul));
3314 ig.Emit (OpCodes.Conv_U8);
3318 LongConstant.EmitLong (ig, unchecked ((long) ul));
3322 IntConstant.EmitInt (ig, (int) ((char) k));
3323 else if (k is sbyte)
3324 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3326 IntConstant.EmitInt (ig, (int) ((byte) k));
3327 else if (k is short)
3328 IntConstant.EmitInt (ig, (int) ((short) k));
3329 else if (k is ushort)
3330 IntConstant.EmitInt (ig, (int) ((ushort) k));
3332 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3334 throw new Exception ("Unhandled case");
3337 // structure used to hold blocks of keys while calculating table switch
3338 class KeyBlock : IComparable
3340 public KeyBlock (long _first)
3342 first = last = _first;
3346 public ArrayList element_keys = null;
3347 // how many items are in the bucket
3348 public int Size = 1;
3351 get { return (int) (last - first + 1); }
3353 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3355 return kb_last.last - kb_first.first + 1;
3357 public int CompareTo (object obj)
3359 KeyBlock kb = (KeyBlock) obj;
3360 int nLength = Length;
3361 int nLengthOther = kb.Length;
3362 if (nLengthOther == nLength)
3363 return (int) (kb.first - first);
3364 return nLength - nLengthOther;
3369 /// This method emits code for a lookup-based switch statement (non-string)
3370 /// Basically it groups the cases into blocks that are at least half full,
3371 /// and then spits out individual lookup opcodes for each block.
3372 /// It emits the longest blocks first, and short blocks are just
3373 /// handled with direct compares.
3375 /// <param name="ec"></param>
3376 /// <param name="val"></param>
3377 /// <returns></returns>
3378 void TableSwitchEmit (EmitContext ec, Expression val)
3380 int element_count = Elements.Count;
3381 object [] element_keys = new object [element_count];
3382 Elements.Keys.CopyTo (element_keys, 0);
3383 Array.Sort (element_keys);
3385 // initialize the block list with one element per key
3386 ArrayList key_blocks = new ArrayList (element_count);
3387 foreach (object key in element_keys)
3388 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3390 KeyBlock current_kb;
3391 // iteratively merge the blocks while they are at least half full
3392 // there's probably a really cool way to do this with a tree...
3393 while (key_blocks.Count > 1)
3395 ArrayList key_blocks_new = new ArrayList ();
3396 current_kb = (KeyBlock) key_blocks [0];
3397 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3399 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3400 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3403 current_kb.last = kb.last;
3404 current_kb.Size += kb.Size;
3408 // start a new block
3409 key_blocks_new.Add (current_kb);
3413 key_blocks_new.Add (current_kb);
3414 if (key_blocks.Count == key_blocks_new.Count)
3416 key_blocks = key_blocks_new;
3419 // initialize the key lists
3420 foreach (KeyBlock kb in key_blocks)
3421 kb.element_keys = new ArrayList ();
3423 // fill the key lists
3425 if (key_blocks.Count > 0) {
3426 current_kb = (KeyBlock) key_blocks [0];
3427 foreach (object key in element_keys)
3429 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3430 System.Convert.ToInt64 (key) > current_kb.last;
3432 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3433 current_kb.element_keys.Add (key);
3437 // sort the blocks so we can tackle the largest ones first
3440 // okay now we can start...
3441 ILGenerator ig = ec.ig;
3442 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3443 Label lbl_default = default_target;
3445 Type type_keys = null;
3446 if (element_keys.Length > 0)
3447 type_keys = element_keys [0].GetType (); // used for conversions
3451 if (TypeManager.IsEnumType (SwitchType))
3452 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3454 compare_type = SwitchType;
3456 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3458 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3459 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3462 foreach (object key in kb.element_keys) {
3463 SwitchLabel sl = (SwitchLabel) Elements [key];
3464 if (key is int && (int) key == 0) {
3465 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3468 EmitObjectInteger (ig, key);
3469 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3475 // TODO: if all the keys in the block are the same and there are
3476 // no gaps/defaults then just use a range-check.
3477 if (compare_type == TypeManager.int64_type ||
3478 compare_type == TypeManager.uint64_type)
3480 // TODO: optimize constant/I4 cases
3482 // check block range (could be > 2^31)
3484 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3485 ig.Emit (OpCodes.Blt, lbl_default);
3487 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3488 ig.Emit (OpCodes.Bgt, lbl_default);
3494 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3495 ig.Emit (OpCodes.Sub);
3497 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3503 int first = (int) kb.first;
3506 IntConstant.EmitInt (ig, first);
3507 ig.Emit (OpCodes.Sub);
3511 IntConstant.EmitInt (ig, -first);
3512 ig.Emit (OpCodes.Add);
3516 // first, build the list of labels for the switch
3518 int cJumps = kb.Length;
3519 Label [] switch_labels = new Label [cJumps];
3520 for (int iJump = 0; iJump < cJumps; iJump++)
3522 object key = kb.element_keys [iKey];
3523 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3525 SwitchLabel sl = (SwitchLabel) Elements [key];
3526 switch_labels [iJump] = sl.GetILLabel (ec);
3530 switch_labels [iJump] = lbl_default;
3532 // emit the switch opcode
3533 ig.Emit (OpCodes.Switch, switch_labels);
3536 // mark the default for this block
3538 ig.MarkLabel (lbl_default);
3541 // TODO: find the default case and emit it here,
3542 // to prevent having to do the following jump.
3543 // make sure to mark other labels in the default section
3545 // the last default just goes to the end
3546 if (element_keys.Length > 0)
3547 ig.Emit (OpCodes.Br, lbl_default);
3549 // now emit the code for the sections
3550 bool found_default = false;
3552 foreach (SwitchSection ss in Sections) {
3553 foreach (SwitchLabel sl in ss.Labels) {
3554 if (sl.Converted == SwitchLabel.NullStringCase) {
3555 ig.MarkLabel (null_target);
3556 } else if (sl.Label == null) {
3557 ig.MarkLabel (lbl_default);
3558 found_default = true;
3560 ig.MarkLabel (null_target);
3562 ig.MarkLabel (sl.GetILLabel (ec));
3563 ig.MarkLabel (sl.GetILLabelCode (ec));
3568 if (!found_default) {
3569 ig.MarkLabel (lbl_default);
3570 if (!has_null_case) {
3571 ig.MarkLabel (null_target);
3575 ig.MarkLabel (lbl_end);
3578 SwitchSection FindSection (SwitchLabel label)
3580 foreach (SwitchSection ss in Sections){
3581 foreach (SwitchLabel sl in ss.Labels){
3590 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3592 foreach (SwitchSection ss in Sections)
3593 ss.Block.MutateHoistedGenericType (storey);
3596 public static void Reset ()
3599 allowed_types = null;
3602 public override bool Resolve (EmitContext ec)
3604 Expr = Expr.Resolve (ec);
3608 new_expr = SwitchGoverningType (ec, Expr);
3610 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3611 unwrap = Nullable.Unwrap.Create (Expr, false);
3615 new_expr = SwitchGoverningType (ec, unwrap);
3618 if (new_expr == null){
3619 Report.Error (151, loc,
3620 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3621 TypeManager.CSharpName (Expr.Type));
3626 SwitchType = new_expr.Type;
3628 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3629 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3633 if (!CheckSwitch (ec))
3637 Elements.Remove (SwitchLabel.NullStringCase);
3639 Switch old_switch = ec.Switch;
3641 ec.Switch.SwitchType = SwitchType;
3643 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3644 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3646 is_constant = new_expr is Constant;
3648 object key = ((Constant) new_expr).GetValue ();
3649 SwitchLabel label = (SwitchLabel) Elements [key];
3651 constant_section = FindSection (label);
3652 if (constant_section == null)
3653 constant_section = default_section;
3658 foreach (SwitchSection ss in Sections){
3660 ec.CurrentBranching.CreateSibling (
3661 null, FlowBranching.SiblingType.SwitchSection);
3665 if (is_constant && (ss != constant_section)) {
3666 // If we're a constant switch, we're only emitting
3667 // one single section - mark all the others as
3669 ec.CurrentBranching.CurrentUsageVector.Goto ();
3670 if (!ss.Block.ResolveUnreachable (ec, true)) {
3674 if (!ss.Block.Resolve (ec))
3679 if (default_section == null)
3680 ec.CurrentBranching.CreateSibling (
3681 null, FlowBranching.SiblingType.SwitchSection);
3683 ec.EndFlowBranching ();
3684 ec.Switch = old_switch;
3686 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3691 if (SwitchType == TypeManager.string_type && !is_constant) {
3692 // TODO: Optimize single case, and single+default case
3693 ResolveStringSwitchMap (ec);
3699 void ResolveStringSwitchMap (EmitContext ec)
3701 FullNamedExpression string_dictionary_type;
3702 if (TypeManager.generic_ienumerable_type != null) {
3703 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3704 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3706 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3708 new TypeExpression (TypeManager.string_type, loc),
3709 new TypeExpression (TypeManager.int32_type, loc)), loc);
3711 MemberAccess system_collections_generic = new MemberAccess (
3712 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3714 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3717 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3718 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3719 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3720 if (!field.Define ())
3722 ec.TypeContainer.PartialContainer.AddField (field);
3724 ArrayList init = new ArrayList ();
3727 string value = null;
3728 foreach (SwitchSection section in Sections) {
3729 int last_count = init.Count;
3730 foreach (SwitchLabel sl in section.Labels) {
3731 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3734 value = (string) sl.Converted;
3735 ArrayList init_args = new ArrayList (2);
3736 init_args.Add (new StringLiteral (value, sl.Location));
3737 init_args.Add (new IntConstant (counter, loc));
3738 init.Add (new CollectionElementInitializer (init_args, loc));
3742 // Don't add empty sections
3744 if (last_count == init.Count)
3747 Elements.Add (counter, section.Labels [0]);
3751 Arguments args = new Arguments (1);
3752 args.Add (new Argument (new IntConstant (init.Count, loc)));
3753 Expression initializer = new NewInitialize (string_dictionary_type, args,
3754 new CollectionOrObjectInitializers (init, loc), loc);
3756 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3757 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3760 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3762 ILGenerator ig = ec.ig;
3763 Label l_initialized = ig.DefineLabel ();
3766 // Skip initialization when value is null
3768 value.EmitBranchable (ec, null_target, false);
3771 // Check if string dictionary is initialized and initialize
3773 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3774 string_dictionary.EmitStatement (ec);
3775 ig.MarkLabel (l_initialized);
3777 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3779 if (TypeManager.generic_ienumerable_type != null) {
3780 Arguments get_value_args = new Arguments (2);
3781 get_value_args.Add (new Argument (value));
3782 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3783 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3784 if (get_item == null)
3788 // A value was not found, go to default case
3790 get_item.EmitBranchable (ec, default_target, false);
3792 Arguments get_value_args = new Arguments (1);
3793 get_value_args.Add (new Argument (value));
3795 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3796 if (get_item == null)
3799 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3800 get_item_object.EmitAssign (ec, get_item, true, false);
3801 ec.ig.Emit (OpCodes.Brfalse, default_target);
3803 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3804 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3806 get_item_int.EmitStatement (ec);
3807 get_item_object.Release (ec);
3810 TableSwitchEmit (ec, string_switch_variable);
3811 string_switch_variable.Release (ec);
3814 protected override void DoEmit (EmitContext ec)
3816 ILGenerator ig = ec.ig;
3818 default_target = ig.DefineLabel ();
3819 null_target = ig.DefineLabel ();
3821 // Store variable for comparission purposes
3822 // TODO: Don't duplicate non-captured VariableReference
3823 LocalTemporary value;
3825 value = new LocalTemporary (SwitchType);
3826 unwrap.EmitCheck (ec);
3827 ig.Emit (OpCodes.Brfalse, null_target);
3830 } else if (!is_constant) {
3831 value = new LocalTemporary (SwitchType);
3838 // Setup the codegen context
3840 Label old_end = ec.LoopEnd;
3841 Switch old_switch = ec.Switch;
3843 ec.LoopEnd = ig.DefineLabel ();
3848 if (constant_section != null)
3849 constant_section.Block.Emit (ec);
3850 } else if (string_dictionary != null) {
3851 DoEmitStringSwitch (value, ec);
3853 TableSwitchEmit (ec, value);
3859 // Restore context state.
3860 ig.MarkLabel (ec.LoopEnd);
3863 // Restore the previous context
3865 ec.LoopEnd = old_end;
3866 ec.Switch = old_switch;
3869 protected override void CloneTo (CloneContext clonectx, Statement t)
3871 Switch target = (Switch) t;
3873 target.Expr = Expr.Clone (clonectx);
3874 target.Sections = new ArrayList ();
3875 foreach (SwitchSection ss in Sections){
3876 target.Sections.Add (ss.Clone (clonectx));
3881 // A place where execution can restart in an iterator
3882 public abstract class ResumableStatement : Statement
3885 protected Label resume_point;
3887 public Label PrepareForEmit (EmitContext ec)
3891 resume_point = ec.ig.DefineLabel ();
3893 return resume_point;
3896 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3900 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3905 // Base class for statements that are implemented in terms of try...finally
3906 public abstract class ExceptionStatement : ResumableStatement
3910 protected abstract void EmitPreTryBody (EmitContext ec);
3911 protected abstract void EmitTryBody (EmitContext ec);
3912 protected abstract void EmitFinallyBody (EmitContext ec);
3914 protected sealed override void DoEmit (EmitContext ec)
3916 ILGenerator ig = ec.ig;
3918 EmitPreTryBody (ec);
3920 if (resume_points != null) {
3921 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3922 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3925 ig.BeginExceptionBlock ();
3927 if (resume_points != null) {
3928 ig.MarkLabel (resume_point);
3930 // For normal control flow, we want to fall-through the Switch
3931 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3932 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3933 IntConstant.EmitInt (ig, first_resume_pc);
3934 ig.Emit (OpCodes.Sub);
3936 Label [] labels = new Label [resume_points.Count];
3937 for (int i = 0; i < resume_points.Count; ++i)
3938 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3939 ig.Emit (OpCodes.Switch, labels);
3944 ig.BeginFinallyBlock ();
3946 Label start_finally = ec.ig.DefineLabel ();
3947 if (resume_points != null) {
3948 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3949 ig.Emit (OpCodes.Brfalse_S, start_finally);
3950 ig.Emit (OpCodes.Endfinally);
3953 ig.MarkLabel (start_finally);
3954 EmitFinallyBody (ec);
3956 ig.EndExceptionBlock ();
3959 public void SomeCodeFollows ()
3961 code_follows = true;
3964 protected void ResolveReachability (EmitContext ec)
3966 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3967 // So, ensure there's some IL code after this statement.
3968 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3969 ec.NeedReturnLabel ();
3973 ArrayList resume_points;
3974 int first_resume_pc;
3975 public void AddResumePoint (ResumableStatement stmt, int pc)
3977 if (resume_points == null) {
3978 resume_points = new ArrayList ();
3979 first_resume_pc = pc;
3982 if (pc != first_resume_pc + resume_points.Count)
3983 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3985 resume_points.Add (stmt);
3988 Label dispose_try_block;
3989 bool prepared_for_dispose, emitted_dispose;
3990 public override Label PrepareForDispose (EmitContext ec, Label end)
3992 if (!prepared_for_dispose) {
3993 prepared_for_dispose = true;
3994 dispose_try_block = ec.ig.DefineLabel ();
3996 return dispose_try_block;
3999 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4001 if (emitted_dispose)
4004 emitted_dispose = true;
4006 ILGenerator ig = ec.ig;
4008 Label end_of_try = ig.DefineLabel ();
4010 // Ensure that the only way we can get into this code is through a dispatcher
4011 if (have_dispatcher)
4012 ig.Emit (OpCodes.Br, end);
4014 ig.BeginExceptionBlock ();
4016 ig.MarkLabel (dispose_try_block);
4018 Label [] labels = null;
4019 for (int i = 0; i < resume_points.Count; ++i) {
4020 ResumableStatement s = (ResumableStatement) resume_points [i];
4021 Label ret = s.PrepareForDispose (ec, end_of_try);
4022 if (ret.Equals (end_of_try) && labels == null)
4024 if (labels == null) {
4025 labels = new Label [resume_points.Count];
4026 for (int j = 0; j < i; ++j)
4027 labels [j] = end_of_try;
4032 if (labels != null) {
4034 for (j = 1; j < labels.Length; ++j)
4035 if (!labels [0].Equals (labels [j]))
4037 bool emit_dispatcher = j < labels.Length;
4039 if (emit_dispatcher) {
4040 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4041 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4042 IntConstant.EmitInt (ig, first_resume_pc);
4043 ig.Emit (OpCodes.Sub);
4044 ig.Emit (OpCodes.Switch, labels);
4045 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4048 foreach (ResumableStatement s in resume_points)
4049 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4052 ig.MarkLabel (end_of_try);
4054 ig.BeginFinallyBlock ();
4056 EmitFinallyBody (ec);
4058 ig.EndExceptionBlock ();
4062 public class Lock : ExceptionStatement {
4064 public Statement Statement;
4065 TemporaryVariable temp;
4067 public Lock (Expression expr, Statement stmt, Location l)
4074 public override bool Resolve (EmitContext ec)
4076 expr = expr.Resolve (ec);
4080 if (!TypeManager.IsReferenceType (expr.Type)){
4081 Report.Error (185, loc,
4082 "`{0}' is not a reference type as required by the lock statement",
4083 TypeManager.CSharpName (expr.Type));
4087 ec.StartFlowBranching (this);
4088 bool ok = Statement.Resolve (ec);
4089 ec.EndFlowBranching ();
4091 ResolveReachability (ec);
4093 // Avoid creating libraries that reference the internal
4096 if (t == TypeManager.null_type)
4097 t = TypeManager.object_type;
4099 temp = new TemporaryVariable (t, loc);
4102 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4103 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4104 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4105 monitor_type, "Enter", loc, TypeManager.object_type);
4106 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4107 monitor_type, "Exit", loc, TypeManager.object_type);
4113 protected override void EmitPreTryBody (EmitContext ec)
4115 ILGenerator ig = ec.ig;
4117 temp.EmitAssign (ec, expr);
4119 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4122 protected override void EmitTryBody (EmitContext ec)
4124 Statement.Emit (ec);
4127 protected override void EmitFinallyBody (EmitContext ec)
4130 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4133 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4135 expr.MutateHoistedGenericType (storey);
4136 temp.MutateHoistedGenericType (storey);
4137 Statement.MutateHoistedGenericType (storey);
4140 protected override void CloneTo (CloneContext clonectx, Statement t)
4142 Lock target = (Lock) t;
4144 target.expr = expr.Clone (clonectx);
4145 target.Statement = Statement.Clone (clonectx);
4149 public class Unchecked : Statement {
4152 public Unchecked (Block b)
4158 public override bool Resolve (EmitContext ec)
4160 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4161 return Block.Resolve (ec);
4164 protected override void DoEmit (EmitContext ec)
4166 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4170 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4172 Block.MutateHoistedGenericType (storey);
4175 protected override void CloneTo (CloneContext clonectx, Statement t)
4177 Unchecked target = (Unchecked) t;
4179 target.Block = clonectx.LookupBlock (Block);
4183 public class Checked : Statement {
4186 public Checked (Block b)
4189 b.Unchecked = false;
4192 public override bool Resolve (EmitContext ec)
4194 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4195 return Block.Resolve (ec);
4198 protected override void DoEmit (EmitContext ec)
4200 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4204 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4206 Block.MutateHoistedGenericType (storey);
4209 protected override void CloneTo (CloneContext clonectx, Statement t)
4211 Checked target = (Checked) t;
4213 target.Block = clonectx.LookupBlock (Block);
4217 public class Unsafe : Statement {
4220 public Unsafe (Block b)
4223 Block.Unsafe = true;
4226 public override bool Resolve (EmitContext ec)
4228 using (ec.With (EmitContext.Flags.InUnsafe, true))
4229 return Block.Resolve (ec);
4232 protected override void DoEmit (EmitContext ec)
4234 using (ec.With (EmitContext.Flags.InUnsafe, true))
4238 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4240 Block.MutateHoistedGenericType (storey);
4243 protected override void CloneTo (CloneContext clonectx, Statement t)
4245 Unsafe target = (Unsafe) t;
4247 target.Block = clonectx.LookupBlock (Block);
4254 public class Fixed : Statement {
4256 ArrayList declarators;
4257 Statement statement;
4262 abstract class Emitter
4264 protected LocalInfo vi;
4265 protected Expression converted;
4267 protected Emitter (Expression expr, LocalInfo li)
4273 public abstract void Emit (EmitContext ec);
4274 public abstract void EmitExit (EmitContext ec);
4277 class ExpressionEmitter : Emitter {
4278 public ExpressionEmitter (Expression converted, LocalInfo li) :
4279 base (converted, li)
4283 public override void Emit (EmitContext ec) {
4285 // Store pointer in pinned location
4287 converted.Emit (ec);
4291 public override void EmitExit (EmitContext ec)
4293 ec.ig.Emit (OpCodes.Ldc_I4_0);
4294 ec.ig.Emit (OpCodes.Conv_U);
4299 class StringEmitter : Emitter
4301 LocalInfo pinned_string;
4303 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4306 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4307 pinned_string.Pinned = true;
4310 public override void Emit (EmitContext ec)
4312 pinned_string.Resolve (ec);
4313 pinned_string.ResolveVariable (ec);
4315 converted.Emit (ec);
4316 pinned_string.EmitAssign (ec);
4318 PropertyInfo p = TypeManager.int_get_offset_to_string_data;
4320 // TODO: Move to resolve
4321 p = TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4322 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4328 // TODO: Should use Binary::Add
4329 pinned_string.Emit (ec);
4330 ec.ig.Emit (OpCodes.Conv_I);
4332 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, p, pinned_string.Location);
4333 //pe.InstanceExpression = pinned_string;
4334 pe.Resolve (ec).Emit (ec);
4336 ec.ig.Emit (OpCodes.Add);
4340 public override void EmitExit (EmitContext ec)
4342 ec.ig.Emit (OpCodes.Ldnull);
4343 pinned_string.EmitAssign (ec);
4347 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4350 declarators = decls;
4355 public Statement Statement {
4356 get { return statement; }
4359 public override bool Resolve (EmitContext ec)
4362 Expression.UnsafeError (loc);
4366 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4367 if (texpr == null) {
4368 if (type is VarExpr)
4369 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4374 expr_type = texpr.Type;
4376 data = new Emitter [declarators.Count];
4378 if (!expr_type.IsPointer){
4379 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4384 foreach (Pair p in declarators){
4385 LocalInfo vi = (LocalInfo) p.First;
4386 Expression e = (Expression) p.Second;
4388 vi.VariableInfo.SetAssigned (ec);
4389 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4392 // The rules for the possible declarators are pretty wise,
4393 // but the production on the grammar is more concise.
4395 // So we have to enforce these rules here.
4397 // We do not resolve before doing the case 1 test,
4398 // because the grammar is explicit in that the token &
4399 // is present, so we need to test for this particular case.
4403 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4407 ec.InFixedInitializer = true;
4409 ec.InFixedInitializer = false;
4416 if (e.Type.IsArray){
4417 Type array_type = TypeManager.GetElementType (e.Type);
4420 // Provided that array_type is unmanaged,
4422 if (!TypeManager.VerifyUnManaged (array_type, loc))
4426 // and T* is implicitly convertible to the
4427 // pointer type given in the fixed statement.
4429 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4431 Expression converted = Convert.ImplicitConversionRequired (
4432 ec, array_ptr, vi.VariableType, loc);
4433 if (converted == null)
4437 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4439 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4440 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4441 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4442 new NullPointer (loc),
4445 converted = converted.Resolve (ec);
4447 data [i] = new ExpressionEmitter (converted, vi);
4456 if (e.Type == TypeManager.string_type){
4457 data [i] = new StringEmitter (e, vi, loc);
4462 // Case 4: fixed buffer
4463 if (e is FixedBufferPtr) {
4464 data [i++] = new ExpressionEmitter (e, vi);
4469 // Case 1: & object.
4471 Unary u = e as Unary;
4472 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4473 IVariableReference vr = u.Expr as IVariableReference;
4474 if (vr == null || !vr.IsFixed) {
4475 data [i] = new ExpressionEmitter (e, vi);
4479 if (data [i++] == null)
4480 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4482 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4485 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4486 bool ok = statement.Resolve (ec);
4487 bool flow_unreachable = ec.EndFlowBranching ();
4488 has_ret = flow_unreachable;
4493 protected override void DoEmit (EmitContext ec)
4495 for (int i = 0; i < data.Length; i++) {
4499 statement.Emit (ec);
4505 // Clear the pinned variable
4507 for (int i = 0; i < data.Length; i++) {
4508 data [i].EmitExit (ec);
4512 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4514 // Fixed statement cannot be used inside anonymous methods or lambdas
4515 throw new NotSupportedException ();
4518 protected override void CloneTo (CloneContext clonectx, Statement t)
4520 Fixed target = (Fixed) t;
4522 target.type = type.Clone (clonectx);
4523 target.declarators = new ArrayList (declarators.Count);
4524 foreach (Pair p in declarators) {
4525 LocalInfo vi = (LocalInfo) p.First;
4526 Expression e = (Expression) p.Second;
4528 target.declarators.Add (
4529 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4532 target.statement = statement.Clone (clonectx);
4536 public class Catch : Statement {
4537 public readonly string Name;
4539 public Block VarBlock;
4541 Expression type_expr;
4544 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4549 VarBlock = var_block;
4553 public Type CatchType {
4559 public bool IsGeneral {
4561 return type_expr == null;
4565 protected override void DoEmit (EmitContext ec)
4567 ILGenerator ig = ec.ig;
4569 if (CatchType != null)
4570 ig.BeginCatchBlock (CatchType);
4572 ig.BeginCatchBlock (TypeManager.object_type);
4574 if (VarBlock != null)
4578 // TODO: Move to resolve
4579 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4583 // Only to make verifier happy
4584 if (TypeManager.IsGenericParameter (lvr.Type))
4585 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4589 if (lvr.IsHoisted) {
4590 LocalTemporary lt = new LocalTemporary (lvr.Type);
4594 // Variable is at the top of the stack
4595 source = EmptyExpression.Null;
4598 lvr.EmitAssign (ec, source, false, false);
4600 ig.Emit (OpCodes.Pop);
4605 public override bool Resolve (EmitContext ec)
4607 using (ec.With (EmitContext.Flags.InCatch, true)) {
4608 if (type_expr != null) {
4609 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4615 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4616 Error (155, "The type caught or thrown must be derived from System.Exception");
4622 if (!Block.Resolve (ec))
4625 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4626 // emit the "unused variable" warnings.
4627 if (VarBlock != null)
4628 return VarBlock.Resolve (ec);
4634 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4637 type = storey.MutateType (type);
4638 if (VarBlock != null)
4639 VarBlock.MutateHoistedGenericType (storey);
4640 Block.MutateHoistedGenericType (storey);
4643 protected override void CloneTo (CloneContext clonectx, Statement t)
4645 Catch target = (Catch) t;
4647 if (type_expr != null)
4648 target.type_expr = type_expr.Clone (clonectx);
4649 if (VarBlock != null)
4650 target.VarBlock = clonectx.LookupBlock (VarBlock);
4651 target.Block = clonectx.LookupBlock (Block);
4655 public class TryFinally : ExceptionStatement {
4659 public TryFinally (Statement stmt, Block fini, Location l)
4666 public override bool Resolve (EmitContext ec)
4670 ec.StartFlowBranching (this);
4672 if (!stmt.Resolve (ec))
4676 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4677 using (ec.With (EmitContext.Flags.InFinally, true)) {
4678 if (!fini.Resolve (ec))
4682 ec.EndFlowBranching ();
4684 ResolveReachability (ec);
4689 protected override void EmitPreTryBody (EmitContext ec)
4693 protected override void EmitTryBody (EmitContext ec)
4698 protected override void EmitFinallyBody (EmitContext ec)
4703 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4705 stmt.MutateHoistedGenericType (storey);
4706 fini.MutateHoistedGenericType (storey);
4709 protected override void CloneTo (CloneContext clonectx, Statement t)
4711 TryFinally target = (TryFinally) t;
4713 target.stmt = (Statement) stmt.Clone (clonectx);
4715 target.fini = clonectx.LookupBlock (fini);
4719 public class TryCatch : Statement {
4721 public ArrayList Specific;
4722 public Catch General;
4723 bool inside_try_finally, code_follows;
4725 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4728 this.Specific = catch_clauses;
4729 this.General = null;
4730 this.inside_try_finally = inside_try_finally;
4732 for (int i = 0; i < catch_clauses.Count; ++i) {
4733 Catch c = (Catch) catch_clauses [i];
4735 if (i != catch_clauses.Count - 1)
4736 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4738 catch_clauses.RemoveAt (i);
4746 public override bool Resolve (EmitContext ec)
4750 ec.StartFlowBranching (this);
4752 if (!Block.Resolve (ec))
4755 Type[] prev_catches = new Type [Specific.Count];
4757 foreach (Catch c in Specific){
4758 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4760 if (c.Name != null) {
4761 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4763 throw new Exception ();
4765 vi.VariableInfo = null;
4768 if (!c.Resolve (ec))
4771 Type resolved_type = c.CatchType;
4772 for (int ii = 0; ii < last_index; ++ii) {
4773 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4774 Report.Error (160, c.loc,
4775 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4776 TypeManager.CSharpName (prev_catches [ii]));
4781 prev_catches [last_index++] = resolved_type;
4784 if (General != null) {
4785 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4786 foreach (Catch c in Specific){
4787 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4788 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'");
4793 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4795 if (!General.Resolve (ec))
4799 ec.EndFlowBranching ();
4801 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4802 // So, ensure there's some IL code after this statement
4803 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4804 ec.NeedReturnLabel ();
4809 public void SomeCodeFollows ()
4811 code_follows = true;
4814 protected override void DoEmit (EmitContext ec)
4816 ILGenerator ig = ec.ig;
4818 if (!inside_try_finally)
4819 ig.BeginExceptionBlock ();
4823 foreach (Catch c in Specific)
4826 if (General != null)
4829 if (!inside_try_finally)
4830 ig.EndExceptionBlock ();
4833 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4835 Block.MutateHoistedGenericType (storey);
4837 if (General != null)
4838 General.MutateHoistedGenericType (storey);
4839 if (Specific != null) {
4840 foreach (Catch c in Specific)
4841 c.MutateHoistedGenericType (storey);
4845 protected override void CloneTo (CloneContext clonectx, Statement t)
4847 TryCatch target = (TryCatch) t;
4849 target.Block = clonectx.LookupBlock (Block);
4850 if (General != null)
4851 target.General = (Catch) General.Clone (clonectx);
4852 if (Specific != null){
4853 target.Specific = new ArrayList ();
4854 foreach (Catch c in Specific)
4855 target.Specific.Add (c.Clone (clonectx));
4860 // FIXME: Why is it almost exact copy of Using ??
4861 public class UsingTemporary : ExceptionStatement {
4862 TemporaryVariable local_copy;
4863 public Statement Statement;
4867 public UsingTemporary (Expression expr, Statement stmt, Location l)
4874 public override bool Resolve (EmitContext ec)
4876 expr = expr.Resolve (ec);
4880 expr_type = expr.Type;
4882 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4883 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4884 Using.Error_IsNotConvertibleToIDisposable (expr);
4889 local_copy = new TemporaryVariable (expr_type, loc);
4890 local_copy.Resolve (ec);
4892 ec.StartFlowBranching (this);
4894 bool ok = Statement.Resolve (ec);
4896 ec.EndFlowBranching ();
4898 ResolveReachability (ec);
4900 if (TypeManager.void_dispose_void == null) {
4901 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4902 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4908 protected override void EmitPreTryBody (EmitContext ec)
4910 local_copy.EmitAssign (ec, expr);
4913 protected override void EmitTryBody (EmitContext ec)
4915 Statement.Emit (ec);
4918 protected override void EmitFinallyBody (EmitContext ec)
4920 ILGenerator ig = ec.ig;
4921 if (!TypeManager.IsStruct (expr_type)) {
4922 Label skip = ig.DefineLabel ();
4923 local_copy.Emit (ec);
4924 ig.Emit (OpCodes.Brfalse, skip);
4925 local_copy.Emit (ec);
4926 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4927 ig.MarkLabel (skip);
4931 Expression ml = Expression.MemberLookup (
4932 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4933 "Dispose", Location.Null);
4935 if (!(ml is MethodGroupExpr)) {
4936 local_copy.Emit (ec);
4937 ig.Emit (OpCodes.Box, expr_type);
4938 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4942 MethodInfo mi = null;
4944 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4945 if (TypeManager.GetParameterData (mk).Count == 0) {
4952 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4956 local_copy.AddressOf (ec, AddressOp.Load);
4957 ig.Emit (OpCodes.Call, mi);
4960 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4962 expr_type = storey.MutateType (expr_type);
4963 local_copy.MutateHoistedGenericType (storey);
4964 Statement.MutateHoistedGenericType (storey);
4967 protected override void CloneTo (CloneContext clonectx, Statement t)
4969 UsingTemporary target = (UsingTemporary) t;
4971 target.expr = expr.Clone (clonectx);
4972 target.Statement = Statement.Clone (clonectx);
4976 public class Using : ExceptionStatement {
4978 public Statement EmbeddedStatement {
4979 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4985 ExpressionStatement assign;
4987 public Using (Expression var, Expression init, Statement stmt, Location l)
4995 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4997 Report.SymbolRelatedToPreviousError (expr.Type);
4998 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4999 expr.GetSignatureForError ());
5002 protected override void EmitPreTryBody (EmitContext ec)
5004 assign.EmitStatement (ec);
5007 protected override void EmitTryBody (EmitContext ec)
5012 protected override void EmitFinallyBody (EmitContext ec)
5014 ILGenerator ig = ec.ig;
5015 Label skip = ig.DefineLabel ();
5017 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5018 if (emit_null_check) {
5020 ig.Emit (OpCodes.Brfalse, skip);
5023 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5025 if (emit_null_check)
5026 ig.MarkLabel (skip);
5029 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5031 assign.MutateHoistedGenericType (storey);
5032 var.MutateHoistedGenericType (storey);
5033 stmt.MutateHoistedGenericType (storey);
5036 public override bool Resolve (EmitContext ec)
5038 if (!ResolveVariable (ec))
5041 ec.StartFlowBranching (this);
5043 bool ok = stmt.Resolve (ec);
5045 ec.EndFlowBranching ();
5047 ResolveReachability (ec);
5049 if (TypeManager.void_dispose_void == null) {
5050 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5051 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5057 bool ResolveVariable (EmitContext ec)
5059 assign = new SimpleAssign (var, init, loc);
5060 assign = assign.ResolveStatement (ec);
5064 if (assign.Type == TypeManager.idisposable_type ||
5065 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5069 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5071 Error_IsNotConvertibleToIDisposable (var);
5075 throw new NotImplementedException ("covariance?");
5078 protected override void CloneTo (CloneContext clonectx, Statement t)
5080 Using target = (Using) t;
5082 target.var = var.Clone (clonectx);
5083 target.init = init.Clone (clonectx);
5084 target.stmt = stmt.Clone (clonectx);
5089 /// Implementation of the foreach C# statement
5091 public class Foreach : Statement {
5093 sealed class ArrayForeach : Statement
5095 class ArrayCounter : TemporaryVariable
5097 StatementExpression increment;
5099 public ArrayCounter (Location loc)
5100 : base (TypeManager.int32_type, loc)
5104 public void ResolveIncrement (EmitContext ec)
5106 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5107 increment.Resolve (ec);
5110 public void EmitIncrement (EmitContext ec)
5112 increment.Emit (ec);
5116 readonly Foreach for_each;
5117 readonly Statement statement;
5120 TemporaryVariable[] lengths;
5121 Expression [] length_exprs;
5122 ArrayCounter[] counter;
5124 TemporaryVariable copy;
5127 public ArrayForeach (Foreach @foreach, int rank)
5129 for_each = @foreach;
5130 statement = for_each.statement;
5133 counter = new ArrayCounter [rank];
5134 length_exprs = new Expression [rank];
5137 // Only use temporary length variables when dealing with
5138 // multi-dimensional arrays
5141 lengths = new TemporaryVariable [rank];
5144 protected override void CloneTo (CloneContext clonectx, Statement target)
5146 throw new NotImplementedException ();
5149 public override bool Resolve (EmitContext ec)
5151 copy = new TemporaryVariable (for_each.expr.Type, loc);
5154 int rank = length_exprs.Length;
5155 Arguments list = new Arguments (rank);
5156 for (int i = 0; i < rank; i++) {
5157 counter [i] = new ArrayCounter (loc);
5158 counter [i].ResolveIncrement (ec);
5161 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5163 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5164 lengths [i].Resolve (ec);
5166 Arguments args = new Arguments (1);
5167 args.Add (new Argument (new IntConstant (i, loc)));
5168 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5171 list.Add (new Argument (counter [i]));
5174 access = new ElementAccess (copy, list).Resolve (ec);
5178 Expression var_type = for_each.type;
5179 VarExpr ve = var_type as VarExpr;
5181 // Infer implicitly typed local variable from foreach array type
5182 var_type = new TypeExpression (access.Type, ve.Location);
5185 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5186 if (var_type == null)
5189 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5195 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5196 ec.CurrentBranching.CreateSibling ();
5198 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5199 if (for_each.variable == null)
5202 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5203 if (!statement.Resolve (ec))
5205 ec.EndFlowBranching ();
5207 // There's no direct control flow from the end of the embedded statement to the end of the loop
5208 ec.CurrentBranching.CurrentUsageVector.Goto ();
5210 ec.EndFlowBranching ();
5215 protected override void DoEmit (EmitContext ec)
5217 ILGenerator ig = ec.ig;
5219 copy.EmitAssign (ec, for_each.expr);
5221 int rank = length_exprs.Length;
5222 Label[] test = new Label [rank];
5223 Label[] loop = new Label [rank];
5225 for (int i = 0; i < rank; i++) {
5226 test [i] = ig.DefineLabel ();
5227 loop [i] = ig.DefineLabel ();
5229 if (lengths != null)
5230 lengths [i].EmitAssign (ec, length_exprs [i]);
5233 IntConstant zero = new IntConstant (0, loc);
5234 for (int i = 0; i < rank; i++) {
5235 counter [i].EmitAssign (ec, zero);
5237 ig.Emit (OpCodes.Br, test [i]);
5238 ig.MarkLabel (loop [i]);
5241 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5243 statement.Emit (ec);
5245 ig.MarkLabel (ec.LoopBegin);
5247 for (int i = rank - 1; i >= 0; i--){
5248 counter [i].EmitIncrement (ec);
5250 ig.MarkLabel (test [i]);
5251 counter [i].Emit (ec);
5253 if (lengths != null)
5254 lengths [i].Emit (ec);
5256 length_exprs [i].Emit (ec);
5258 ig.Emit (OpCodes.Blt, loop [i]);
5261 ig.MarkLabel (ec.LoopEnd);
5264 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5266 for_each.expr.MutateHoistedGenericType (storey);
5268 copy.MutateHoistedGenericType (storey);
5269 conv.MutateHoistedGenericType (storey);
5270 statement.MutateHoistedGenericType (storey);
5272 for (int i = 0; i < counter.Length; i++) {
5273 counter [i].MutateHoistedGenericType (storey);
5274 if (lengths != null)
5275 lengths [i].MutateHoistedGenericType (storey);
5280 sealed class CollectionForeach : Statement
5282 class CollectionForeachStatement : Statement
5285 Expression variable, current, conv;
5286 Statement statement;
5289 public CollectionForeachStatement (Type type, Expression variable,
5290 Expression current, Statement statement,
5294 this.variable = variable;
5295 this.current = current;
5296 this.statement = statement;
5300 protected override void CloneTo (CloneContext clonectx, Statement target)
5302 throw new NotImplementedException ();
5305 public override bool Resolve (EmitContext ec)
5307 current = current.Resolve (ec);
5308 if (current == null)
5311 conv = Convert.ExplicitConversion (ec, current, type, loc);
5315 assign = new SimpleAssign (variable, conv, loc);
5316 if (assign.Resolve (ec) == null)
5319 if (!statement.Resolve (ec))
5325 protected override void DoEmit (EmitContext ec)
5327 assign.EmitStatement (ec);
5328 statement.Emit (ec);
5331 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5333 assign.MutateHoistedGenericType (storey);
5334 statement.MutateHoistedGenericType (storey);
5338 Expression variable, expr;
5339 Statement statement;
5341 TemporaryVariable enumerator;
5346 MethodGroupExpr get_enumerator;
5347 PropertyExpr get_current;
5348 MethodInfo move_next;
5349 Expression var_type;
5350 Type enumerator_type;
5351 bool enumerator_found;
5353 public CollectionForeach (Expression var_type, Expression var,
5354 Expression expr, Statement stmt, Location l)
5356 this.var_type = var_type;
5357 this.variable = var;
5363 protected override void CloneTo (CloneContext clonectx, Statement target)
5365 throw new NotImplementedException ();
5368 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5370 Type return_type = mi.ReturnType;
5373 // Ok, we can access it, now make sure that we can do something
5374 // with this `GetEnumerator'
5377 if (return_type == TypeManager.ienumerator_type ||
5378 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5380 // If it is not an interface, lets try to find the methods ourselves.
5381 // For example, if we have:
5382 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5383 // We can avoid the iface call. This is a runtime perf boost.
5384 // even bigger if we have a ValueType, because we avoid the cost
5387 // We have to make sure that both methods exist for us to take
5388 // this path. If one of the methods does not exist, we will just
5389 // use the interface. Sadly, this complex if statement is the only
5390 // way I could do this without a goto
5393 if (TypeManager.bool_movenext_void == null) {
5394 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5395 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5398 if (TypeManager.ienumerator_getcurrent == null) {
5399 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5400 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5404 // Prefer a generic enumerator over a non-generic one.
5406 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5407 enumerator_type = return_type;
5408 if (!FetchGetCurrent (ec, return_type))
5409 get_current = new PropertyExpr (
5410 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5411 if (!FetchMoveNext (return_type))
5412 move_next = TypeManager.bool_movenext_void;
5416 if (return_type.IsInterface ||
5417 !FetchMoveNext (return_type) ||
5418 !FetchGetCurrent (ec, return_type)) {
5419 enumerator_type = return_type;
5420 move_next = TypeManager.bool_movenext_void;
5421 get_current = new PropertyExpr (
5422 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5427 // Ok, so they dont return an IEnumerable, we will have to
5428 // find if they support the GetEnumerator pattern.
5431 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5432 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",
5433 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5438 enumerator_type = return_type;
5444 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5446 bool FetchMoveNext (Type t)
5448 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5450 BindingFlags.Public | BindingFlags.Instance,
5453 foreach (MemberInfo m in move_next_list){
5454 MethodInfo mi = (MethodInfo) m;
5456 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5457 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5467 // Retrieves a `public T get_Current ()' method from the Type `t'
5469 bool FetchGetCurrent (EmitContext ec, Type t)
5471 PropertyExpr pe = Expression.MemberLookup (
5472 ec.ContainerType, t, "Current", MemberTypes.Property,
5473 Expression.AllBindingFlags, loc) as PropertyExpr;
5481 void Error_Enumerator ()
5483 if (enumerator_found) {
5487 Report.Error (1579, loc,
5488 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5489 TypeManager.CSharpName (expr.Type));
5492 bool IsOverride (MethodInfo m)
5494 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5496 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5498 if (m is MethodBuilder)
5501 MethodInfo base_method = m.GetBaseDefinition ();
5502 return base_method != m;
5505 bool TryType (EmitContext ec, Type t)
5507 MethodGroupExpr mg = Expression.MemberLookup (
5508 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5509 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5513 MethodInfo result = null;
5514 MethodInfo tmp_move_next = null;
5515 PropertyExpr tmp_get_cur = null;
5516 Type tmp_enumerator_type = enumerator_type;
5517 foreach (MethodInfo mi in mg.Methods) {
5518 if (TypeManager.GetParameterData (mi).Count != 0)
5521 // Check whether GetEnumerator is public
5522 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5525 if (IsOverride (mi))
5528 enumerator_found = true;
5530 if (!GetEnumeratorFilter (ec, mi))
5533 if (result != null) {
5534 if (TypeManager.IsGenericType (result.ReturnType)) {
5535 if (!TypeManager.IsGenericType (mi.ReturnType))
5538 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5539 Report.SymbolRelatedToPreviousError (t);
5540 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5541 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5542 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5546 // Always prefer generics enumerators
5547 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5548 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5549 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5552 Report.SymbolRelatedToPreviousError (result);
5553 Report.SymbolRelatedToPreviousError (mi);
5554 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5555 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5560 tmp_move_next = move_next;
5561 tmp_get_cur = get_current;
5562 tmp_enumerator_type = enumerator_type;
5563 if (mi.DeclaringType == t)
5567 if (result != null) {
5568 move_next = tmp_move_next;
5569 get_current = tmp_get_cur;
5570 enumerator_type = tmp_enumerator_type;
5571 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5572 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5574 if (t != expr.Type) {
5575 expr = Convert.ExplicitConversion (
5578 throw new InternalErrorException ();
5581 get_enumerator.InstanceExpression = expr;
5582 get_enumerator.IsBase = t != expr.Type;
5590 bool ProbeCollectionType (EmitContext ec, Type t)
5592 int errors = Report.Errors;
5593 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5594 if (TryType (ec, tt))
5599 if (Report.Errors > errors)
5603 // Now try to find the method in the interfaces
5605 Type [] ifaces = TypeManager.GetInterfaces (t);
5606 foreach (Type i in ifaces){
5607 if (TryType (ec, i))
5614 public override bool Resolve (EmitContext ec)
5616 enumerator_type = TypeManager.ienumerator_type;
5618 if (!ProbeCollectionType (ec, expr.Type)) {
5619 Error_Enumerator ();
5623 VarExpr ve = var_type as VarExpr;
5625 // Infer implicitly typed local variable from foreach enumerable type
5626 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5629 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5630 if (var_type == null)
5633 enumerator = new TemporaryVariable (enumerator_type, loc);
5634 enumerator.Resolve (ec);
5636 init = new Invocation (get_enumerator, null);
5637 init = init.Resolve (ec);
5641 Expression move_next_expr;
5643 MemberInfo[] mi = new MemberInfo[] { move_next };
5644 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5645 mg.InstanceExpression = enumerator;
5647 move_next_expr = new Invocation (mg, null);
5650 get_current.InstanceExpression = enumerator;
5652 Statement block = new CollectionForeachStatement (
5653 var_type.Type, variable, get_current, statement, loc);
5655 loop = new While (move_next_expr, block, loc);
5658 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5659 if (implements_idisposable || !enumerator_type.IsSealed) {
5660 wrapper = new DisposableWrapper (this, implements_idisposable);
5662 wrapper = new NonDisposableWrapper (this);
5665 return wrapper.Resolve (ec);
5668 protected override void DoEmit (EmitContext ec)
5673 class NonDisposableWrapper : Statement {
5674 CollectionForeach parent;
5676 internal NonDisposableWrapper (CollectionForeach parent)
5678 this.parent = parent;
5681 protected override void CloneTo (CloneContext clonectx, Statement target)
5683 throw new NotSupportedException ();
5686 public override bool Resolve (EmitContext ec)
5688 return parent.ResolveLoop (ec);
5691 protected override void DoEmit (EmitContext ec)
5693 parent.EmitLoopInit (ec);
5694 parent.EmitLoopBody (ec);
5697 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5699 throw new NotSupportedException ();
5703 sealed class DisposableWrapper : ExceptionStatement
5705 CollectionForeach parent;
5706 bool implements_idisposable;
5708 internal DisposableWrapper (CollectionForeach parent, bool implements)
5710 this.parent = parent;
5711 this.implements_idisposable = implements;
5714 protected override void CloneTo (CloneContext clonectx, Statement target)
5716 throw new NotSupportedException ();
5719 public override bool Resolve (EmitContext ec)
5723 ec.StartFlowBranching (this);
5725 if (!parent.ResolveLoop (ec))
5728 ec.EndFlowBranching ();
5730 ResolveReachability (ec);
5732 if (TypeManager.void_dispose_void == null) {
5733 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5734 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5739 protected override void EmitPreTryBody (EmitContext ec)
5741 parent.EmitLoopInit (ec);
5744 protected override void EmitTryBody (EmitContext ec)
5746 parent.EmitLoopBody (ec);
5749 protected override void EmitFinallyBody (EmitContext ec)
5751 Expression instance = parent.enumerator;
5752 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5753 ILGenerator ig = ec.ig;
5755 parent.enumerator.Emit (ec);
5757 Label call_dispose = ig.DefineLabel ();
5759 if (!implements_idisposable) {
5760 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5761 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5767 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5769 // using 'endfinally' to empty the evaluation stack
5770 ig.Emit (OpCodes.Endfinally);
5771 ig.MarkLabel (call_dispose);
5774 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5777 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5779 throw new NotSupportedException ();
5783 bool ResolveLoop (EmitContext ec)
5785 return loop.Resolve (ec);
5788 void EmitLoopInit (EmitContext ec)
5790 enumerator.EmitAssign (ec, init);
5793 void EmitLoopBody (EmitContext ec)
5798 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5800 enumerator_type = storey.MutateType (enumerator_type);
5801 init.MutateHoistedGenericType (storey);
5802 loop.MutateHoistedGenericType (storey);
5807 Expression variable;
5809 Statement statement;
5811 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5812 Statement stmt, Location l)
5815 this.variable = var;
5821 public Statement Statement {
5822 get { return statement; }
5825 public override bool Resolve (EmitContext ec)
5827 expr = expr.Resolve (ec);
5832 Report.Error (186, loc, "Use of null is not valid in this context");
5836 if (expr.Type == TypeManager.string_type) {
5837 statement = new ArrayForeach (this, 1);
5838 } else if (expr.Type.IsArray) {
5839 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5841 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5842 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5843 expr.ExprClassName);
5847 statement = new CollectionForeach (type, variable, expr, statement, loc);
5850 return statement.Resolve (ec);
5853 protected override void DoEmit (EmitContext ec)
5855 ILGenerator ig = ec.ig;
5857 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5858 ec.LoopBegin = ig.DefineLabel ();
5859 ec.LoopEnd = ig.DefineLabel ();
5861 statement.Emit (ec);
5863 ec.LoopBegin = old_begin;
5864 ec.LoopEnd = old_end;
5867 protected override void CloneTo (CloneContext clonectx, Statement t)
5869 Foreach target = (Foreach) t;
5871 target.type = type.Clone (clonectx);
5872 target.variable = variable.Clone (clonectx);
5873 target.expr = expr.Clone (clonectx);
5874 target.statement = statement.Clone (clonectx);
5877 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5879 statement.MutateHoistedGenericType (storey);