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 (BlockContext 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 (BlockContext 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);
81 public virtual void Emit (EmitContext ec)
88 // This routine must be overrided in derived classes and make copies
89 // of all the data that might be modified if resolved
91 protected abstract void CloneTo (CloneContext clonectx, Statement target);
93 public Statement Clone (CloneContext clonectx)
95 Statement s = (Statement) this.MemberwiseClone ();
96 CloneTo (clonectx, s);
100 public virtual Expression CreateExpressionTree (ResolveContext ec)
102 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
106 public Statement PerformClone ()
108 CloneContext clonectx = new CloneContext ();
110 return Clone (clonectx);
113 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
116 public sealed class EmptyStatement : Statement {
118 private EmptyStatement () {}
120 public static readonly EmptyStatement Value = new EmptyStatement ();
122 public override bool Resolve (BlockContext ec)
127 public override bool ResolveUnreachable (BlockContext ec, bool warn)
132 protected override void DoEmit (EmitContext ec)
136 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
140 protected override void CloneTo (CloneContext clonectx, Statement target)
146 public class If : Statement {
148 public Statement TrueStatement;
149 public Statement FalseStatement;
153 public If (Expression expr, Statement true_statement, Location l)
156 TrueStatement = true_statement;
160 public If (Expression expr,
161 Statement true_statement,
162 Statement false_statement,
166 TrueStatement = true_statement;
167 FalseStatement = false_statement;
171 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
173 expr.MutateHoistedGenericType (storey);
174 TrueStatement.MutateHoistedGenericType (storey);
175 if (FalseStatement != null)
176 FalseStatement.MutateHoistedGenericType (storey);
179 public override bool Resolve (BlockContext ec)
183 Report.Debug (1, "START IF BLOCK", loc);
185 expr = Expression.ResolveBoolean (ec, expr, loc);
191 Assign ass = expr as Assign;
192 if (ass != null && ass.Source is Constant) {
193 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
197 // Dead code elimination
199 if (expr is Constant){
200 bool take = !((Constant) expr).IsDefaultValue;
203 if (!TrueStatement.Resolve (ec))
206 if ((FalseStatement != null) &&
207 !FalseStatement.ResolveUnreachable (ec, true))
209 FalseStatement = null;
211 if (!TrueStatement.ResolveUnreachable (ec, true))
213 TrueStatement = null;
215 if ((FalseStatement != null) &&
216 !FalseStatement.Resolve (ec))
223 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
225 ok &= TrueStatement.Resolve (ec);
227 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
229 ec.CurrentBranching.CreateSibling ();
231 if (FalseStatement != null)
232 ok &= FalseStatement.Resolve (ec);
234 ec.EndFlowBranching ();
236 Report.Debug (1, "END IF BLOCK", loc);
241 protected override void DoEmit (EmitContext ec)
243 ILGenerator ig = ec.ig;
244 Label false_target = ig.DefineLabel ();
248 // If we're a boolean constant, Resolve() already
249 // eliminated dead code for us.
251 Constant c = expr as Constant;
253 c.EmitSideEffect (ec);
255 if (!c.IsDefaultValue)
256 TrueStatement.Emit (ec);
257 else if (FalseStatement != null)
258 FalseStatement.Emit (ec);
263 expr.EmitBranchable (ec, false_target, false);
265 TrueStatement.Emit (ec);
267 if (FalseStatement != null){
268 bool branch_emitted = false;
270 end = ig.DefineLabel ();
272 ig.Emit (OpCodes.Br, end);
273 branch_emitted = true;
276 ig.MarkLabel (false_target);
277 FalseStatement.Emit (ec);
282 ig.MarkLabel (false_target);
286 protected override void CloneTo (CloneContext clonectx, Statement t)
290 target.expr = expr.Clone (clonectx);
291 target.TrueStatement = TrueStatement.Clone (clonectx);
292 if (FalseStatement != null)
293 target.FalseStatement = FalseStatement.Clone (clonectx);
297 public class Do : Statement {
298 public Expression expr;
299 public Statement EmbeddedStatement;
301 public Do (Statement statement, Expression bool_expr, Location l)
304 EmbeddedStatement = statement;
308 public override bool Resolve (BlockContext ec)
312 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
314 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
316 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
317 if (!EmbeddedStatement.Resolve (ec))
319 ec.EndFlowBranching ();
321 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
322 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
324 expr = Expression.ResolveBoolean (ec, expr, loc);
327 else if (expr is Constant){
328 bool infinite = !((Constant) expr).IsDefaultValue;
330 ec.CurrentBranching.CurrentUsageVector.Goto ();
333 ec.EndFlowBranching ();
338 protected override void DoEmit (EmitContext ec)
340 ILGenerator ig = ec.ig;
341 Label loop = ig.DefineLabel ();
342 Label old_begin = ec.LoopBegin;
343 Label old_end = ec.LoopEnd;
345 ec.LoopBegin = ig.DefineLabel ();
346 ec.LoopEnd = ig.DefineLabel ();
349 EmbeddedStatement.Emit (ec);
350 ig.MarkLabel (ec.LoopBegin);
353 // Dead code elimination
355 if (expr is Constant){
356 bool res = !((Constant) expr).IsDefaultValue;
358 expr.EmitSideEffect (ec);
360 ec.ig.Emit (OpCodes.Br, loop);
362 expr.EmitBranchable (ec, loop, true);
364 ig.MarkLabel (ec.LoopEnd);
366 ec.LoopBegin = old_begin;
367 ec.LoopEnd = old_end;
370 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
372 expr.MutateHoistedGenericType (storey);
373 EmbeddedStatement.MutateHoistedGenericType (storey);
376 protected override void CloneTo (CloneContext clonectx, Statement t)
380 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
381 target.expr = expr.Clone (clonectx);
385 public class While : Statement {
386 public Expression expr;
387 public Statement Statement;
388 bool infinite, empty;
390 public While (Expression bool_expr, Statement statement, Location l)
392 this.expr = bool_expr;
393 Statement = statement;
397 public override bool Resolve (BlockContext ec)
401 expr = Expression.ResolveBoolean (ec, expr, loc);
406 // Inform whether we are infinite or not
408 if (expr is Constant){
409 bool value = !((Constant) expr).IsDefaultValue;
412 if (!Statement.ResolveUnreachable (ec, true))
420 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
422 ec.CurrentBranching.CreateSibling ();
424 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
425 if (!Statement.Resolve (ec))
427 ec.EndFlowBranching ();
429 // There's no direct control flow from the end of the embedded statement to the end of the loop
430 ec.CurrentBranching.CurrentUsageVector.Goto ();
432 ec.EndFlowBranching ();
437 protected override void DoEmit (EmitContext ec)
440 expr.EmitSideEffect (ec);
444 ILGenerator ig = ec.ig;
445 Label old_begin = ec.LoopBegin;
446 Label old_end = ec.LoopEnd;
448 ec.LoopBegin = ig.DefineLabel ();
449 ec.LoopEnd = ig.DefineLabel ();
452 // Inform whether we are infinite or not
454 if (expr is Constant){
455 // expr is 'true', since the 'empty' case above handles the 'false' case
456 ig.MarkLabel (ec.LoopBegin);
457 expr.EmitSideEffect (ec);
459 ig.Emit (OpCodes.Br, ec.LoopBegin);
462 // Inform that we are infinite (ie, `we return'), only
463 // if we do not `break' inside the code.
465 ig.MarkLabel (ec.LoopEnd);
467 Label while_loop = ig.DefineLabel ();
469 ig.Emit (OpCodes.Br, ec.LoopBegin);
470 ig.MarkLabel (while_loop);
474 ig.MarkLabel (ec.LoopBegin);
477 expr.EmitBranchable (ec, while_loop, true);
479 ig.MarkLabel (ec.LoopEnd);
482 ec.LoopBegin = old_begin;
483 ec.LoopEnd = old_end;
486 public override void Emit (EmitContext ec)
491 protected override void CloneTo (CloneContext clonectx, Statement t)
493 While target = (While) t;
495 target.expr = expr.Clone (clonectx);
496 target.Statement = Statement.Clone (clonectx);
499 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
501 expr.MutateHoistedGenericType (storey);
502 Statement.MutateHoistedGenericType (storey);
506 public class For : Statement {
508 Statement InitStatement;
510 public Statement Statement;
511 bool infinite, empty;
513 public For (Statement init_statement,
519 InitStatement = init_statement;
521 Increment = increment;
522 Statement = statement;
526 public override bool Resolve (BlockContext ec)
530 if (InitStatement != null){
531 if (!InitStatement.Resolve (ec))
536 Test = Expression.ResolveBoolean (ec, Test, loc);
539 else if (Test is Constant){
540 bool value = !((Constant) Test).IsDefaultValue;
543 if (!Statement.ResolveUnreachable (ec, true))
545 if ((Increment != null) &&
546 !Increment.ResolveUnreachable (ec, false))
556 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
558 ec.CurrentBranching.CreateSibling ();
560 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
562 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
563 if (!Statement.Resolve (ec))
565 ec.EndFlowBranching ();
567 if (Increment != null){
568 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
569 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
572 if (!Increment.Resolve (ec))
577 // There's no direct control flow from the end of the embedded statement to the end of the loop
578 ec.CurrentBranching.CurrentUsageVector.Goto ();
580 ec.EndFlowBranching ();
585 protected override void DoEmit (EmitContext ec)
587 if (InitStatement != null && InitStatement != EmptyStatement.Value)
588 InitStatement.Emit (ec);
591 Test.EmitSideEffect (ec);
595 ILGenerator ig = ec.ig;
596 Label old_begin = ec.LoopBegin;
597 Label old_end = ec.LoopEnd;
598 Label loop = ig.DefineLabel ();
599 Label test = ig.DefineLabel ();
601 ec.LoopBegin = ig.DefineLabel ();
602 ec.LoopEnd = ig.DefineLabel ();
604 ig.Emit (OpCodes.Br, test);
608 ig.MarkLabel (ec.LoopBegin);
609 if (Increment != EmptyStatement.Value)
614 // If test is null, there is no test, and we are just
619 // The Resolve code already catches the case for
620 // Test == Constant (false) so we know that
623 if (Test is Constant) {
624 Test.EmitSideEffect (ec);
625 ig.Emit (OpCodes.Br, loop);
627 Test.EmitBranchable (ec, loop, true);
631 ig.Emit (OpCodes.Br, loop);
632 ig.MarkLabel (ec.LoopEnd);
634 ec.LoopBegin = old_begin;
635 ec.LoopEnd = old_end;
638 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
640 if (InitStatement != null)
641 InitStatement.MutateHoistedGenericType (storey);
643 Test.MutateHoistedGenericType (storey);
644 if (Increment != null)
645 Increment.MutateHoistedGenericType (storey);
647 Statement.MutateHoistedGenericType (storey);
650 protected override void CloneTo (CloneContext clonectx, Statement t)
652 For target = (For) t;
654 if (InitStatement != null)
655 target.InitStatement = InitStatement.Clone (clonectx);
657 target.Test = Test.Clone (clonectx);
658 if (Increment != null)
659 target.Increment = Increment.Clone (clonectx);
660 target.Statement = Statement.Clone (clonectx);
664 public class StatementExpression : Statement {
665 ExpressionStatement expr;
667 public StatementExpression (ExpressionStatement expr)
673 public override bool Resolve (BlockContext ec)
675 if (expr != null && expr.eclass == ExprClass.Invalid)
676 expr = expr.ResolveStatement (ec);
680 protected override void DoEmit (EmitContext ec)
682 expr.EmitStatement (ec);
685 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
687 expr.MutateHoistedGenericType (storey);
690 public override string ToString ()
692 return "StatementExpression (" + expr + ")";
695 protected override void CloneTo (CloneContext clonectx, Statement t)
697 StatementExpression target = (StatementExpression) t;
699 target.expr = (ExpressionStatement) expr.Clone (clonectx);
703 // A 'return' or a 'yield break'
704 public abstract class ExitStatement : Statement
706 protected bool unwind_protect;
707 protected abstract bool DoResolve (BlockContext ec);
709 public virtual void Error_FinallyClause ()
711 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
714 public sealed override bool Resolve (BlockContext ec)
719 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
721 ec.NeedReturnLabel ();
722 ec.CurrentBranching.CurrentUsageVector.Goto ();
728 /// Implements the return statement
730 public class Return : ExitStatement {
731 protected Expression Expr;
732 public Return (Expression expr, Location l)
738 protected override bool DoResolve (BlockContext ec)
741 if (ec.ReturnType == TypeManager.void_type)
744 Error (126, "An object of a type convertible to `{0}' is required " +
745 "for the return statement",
746 TypeManager.CSharpName (ec.ReturnType));
750 if (ec.CurrentBlock.Toplevel.IsIterator) {
751 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
752 "statement to return a value, or yield break to end the iteration");
755 AnonymousExpression am = ec.CurrentAnonymousMethod;
756 if (am == null && ec.ReturnType == TypeManager.void_type) {
757 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
758 ec.GetSignatureForError ());
761 Expr = Expr.Resolve (ec);
765 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
766 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
770 if (Expr.Type != ec.ReturnType) {
771 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
775 Report.Error (1662, loc,
776 "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",
777 am.ContainerType, am.GetSignatureForError ());
786 protected override void DoEmit (EmitContext ec)
792 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
796 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
798 ec.ig.Emit (OpCodes.Ret);
801 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
804 Expr.MutateHoistedGenericType (storey);
807 protected override void CloneTo (CloneContext clonectx, Statement t)
809 Return target = (Return) t;
810 // It's null for simple return;
812 target.Expr = Expr.Clone (clonectx);
816 public class Goto : Statement {
818 LabeledStatement label;
821 public override bool Resolve (BlockContext ec)
823 int errors = Report.Errors;
824 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
825 ec.CurrentBranching.CurrentUsageVector.Goto ();
826 return errors == Report.Errors;
829 public Goto (string label, Location l)
835 public string Target {
836 get { return target; }
839 public void SetResolvedTarget (LabeledStatement label)
842 label.AddReference ();
845 protected override void CloneTo (CloneContext clonectx, Statement target)
850 protected override void DoEmit (EmitContext ec)
853 throw new InternalErrorException ("goto emitted before target resolved");
854 Label l = label.LabelTarget (ec);
855 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
858 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
863 public class LabeledStatement : Statement {
870 FlowBranching.UsageVector vectors;
872 public LabeledStatement (string name, Location l)
878 public Label LabelTarget (EmitContext ec)
883 label = ec.ig.DefineLabel ();
893 public bool IsDefined {
894 get { return defined; }
897 public bool HasBeenReferenced {
898 get { return referenced; }
901 public FlowBranching.UsageVector JumpOrigins {
902 get { return vectors; }
905 public void AddUsageVector (FlowBranching.UsageVector vector)
907 vector = vector.Clone ();
908 vector.Next = vectors;
912 protected override void CloneTo (CloneContext clonectx, Statement target)
917 public override bool Resolve (BlockContext ec)
919 // this flow-branching will be terminated when the surrounding block ends
920 ec.StartFlowBranching (this);
924 protected override void DoEmit (EmitContext ec)
926 if (ig != null && ig != ec.ig)
927 throw new InternalErrorException ("cannot happen");
929 ec.ig.MarkLabel (label);
932 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
936 public void AddReference ()
944 /// `goto default' statement
946 public class GotoDefault : Statement {
948 public GotoDefault (Location l)
953 protected override void CloneTo (CloneContext clonectx, Statement target)
958 public override bool Resolve (BlockContext ec)
960 ec.CurrentBranching.CurrentUsageVector.Goto ();
964 protected override void DoEmit (EmitContext ec)
966 if (ec.Switch == null){
967 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
971 if (!ec.Switch.GotDefault){
972 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
975 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
978 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
984 /// `goto case' statement
986 public class GotoCase : Statement {
990 public GotoCase (Expression e, Location l)
996 public override bool Resolve (BlockContext ec)
998 if (ec.Switch == null){
999 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1003 ec.CurrentBranching.CurrentUsageVector.Goto ();
1005 expr = expr.Resolve (ec);
1009 Constant c = expr as Constant;
1011 Error (150, "A constant value is expected");
1015 Type type = ec.Switch.SwitchType;
1016 Constant res = c.TryReduce (ec, type, c.Location);
1018 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1022 if (!Convert.ImplicitStandardConversionExists (c, type))
1023 Report.Warning (469, 2, loc,
1024 "The `goto case' value is not implicitly convertible to type `{0}'",
1025 TypeManager.CSharpName (type));
1027 object val = res.GetValue ();
1029 val = SwitchLabel.NullStringCase;
1031 sl = (SwitchLabel) ec.Switch.Elements [val];
1034 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1035 (c.GetValue () == null ? "null" : val.ToString ()));
1042 protected override void DoEmit (EmitContext ec)
1044 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1047 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1049 expr.MutateHoistedGenericType (storey);
1052 protected override void CloneTo (CloneContext clonectx, Statement t)
1054 GotoCase target = (GotoCase) t;
1056 target.expr = expr.Clone (clonectx);
1060 public class Throw : Statement {
1063 public Throw (Expression expr, Location l)
1069 public override bool Resolve (BlockContext ec)
1072 ec.CurrentBranching.CurrentUsageVector.Goto ();
1073 return ec.CurrentBranching.CheckRethrow (loc);
1076 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1077 ec.CurrentBranching.CurrentUsageVector.Goto ();
1082 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1083 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1085 Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1090 protected override void DoEmit (EmitContext ec)
1093 ec.ig.Emit (OpCodes.Rethrow);
1097 ec.ig.Emit (OpCodes.Throw);
1101 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1104 expr.MutateHoistedGenericType (storey);
1107 protected override void CloneTo (CloneContext clonectx, Statement t)
1109 Throw target = (Throw) t;
1112 target.expr = expr.Clone (clonectx);
1116 public class Break : Statement {
1118 public Break (Location l)
1123 bool unwind_protect;
1125 public override bool Resolve (BlockContext ec)
1127 int errors = Report.Errors;
1128 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1129 ec.CurrentBranching.CurrentUsageVector.Goto ();
1130 return errors == Report.Errors;
1133 protected override void DoEmit (EmitContext ec)
1135 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1138 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1142 protected override void CloneTo (CloneContext clonectx, Statement t)
1148 public class Continue : Statement {
1150 public Continue (Location l)
1155 bool unwind_protect;
1157 public override bool Resolve (BlockContext ec)
1159 int errors = Report.Errors;
1160 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1161 ec.CurrentBranching.CurrentUsageVector.Goto ();
1162 return errors == Report.Errors;
1165 protected override void DoEmit (EmitContext ec)
1167 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1170 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1174 protected override void CloneTo (CloneContext clonectx, Statement t)
1180 public interface ILocalVariable
1182 void Emit (EmitContext ec);
1183 void EmitAssign (EmitContext ec);
1184 void EmitAddressOf (EmitContext ec);
1187 public interface IKnownVariable {
1188 Block Block { get; }
1189 Location Location { get; }
1193 // The information about a user-perceived local variable
1195 public class LocalInfo : IKnownVariable, ILocalVariable {
1196 public readonly FullNamedExpression Type;
1198 public Type VariableType;
1199 public readonly string Name;
1200 public readonly Location Location;
1201 public readonly Block Block;
1203 public VariableInfo VariableInfo;
1204 public HoistedVariable HoistedVariableReference;
1213 CompilerGenerated = 64,
1217 public enum ReadOnlyContext: byte {
1224 ReadOnlyContext ro_context;
1225 LocalBuilder builder;
1227 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1235 public LocalInfo (DeclSpace ds, Block block, Location l)
1237 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1242 public void ResolveVariable (EmitContext ec)
1244 if (HoistedVariableReference != null)
1247 if (builder == null) {
1250 // This is needed to compile on both .NET 1.x and .NET 2.x
1251 // the later introduced `DeclareLocal (Type t, bool pinned)'
1253 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1255 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1259 public void Emit (EmitContext ec)
1261 ec.ig.Emit (OpCodes.Ldloc, builder);
1264 public void EmitAssign (EmitContext ec)
1266 ec.ig.Emit (OpCodes.Stloc, builder);
1269 public void EmitAddressOf (EmitContext ec)
1271 ec.ig.Emit (OpCodes.Ldloca, builder);
1274 public void EmitSymbolInfo (EmitContext ec)
1276 if (builder != null)
1277 ec.DefineLocalVariable (Name, builder);
1280 public bool IsThisAssigned (BlockContext ec, Block block)
1282 if (VariableInfo == null)
1283 throw new Exception ();
1285 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1288 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, block.StartLocation);
1291 public bool IsAssigned (BlockContext ec)
1293 if (VariableInfo == null)
1294 throw new Exception ();
1296 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1299 public bool Resolve (ResolveContext ec)
1301 if (VariableType != null)
1304 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1308 VariableType = texpr.Type;
1310 if (TypeManager.IsGenericParameter (VariableType))
1313 if (VariableType.IsAbstract && VariableType.IsSealed) {
1314 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1318 if (VariableType.IsPointer && !ec.IsUnsafe)
1319 Expression.UnsafeError (Location);
1324 public bool IsConstant {
1325 get { return (flags & Flags.IsConstant) != 0; }
1326 set { flags |= Flags.IsConstant; }
1329 public bool AddressTaken {
1330 get { return (flags & Flags.AddressTaken) != 0; }
1331 set { flags |= Flags.AddressTaken; }
1334 public bool CompilerGenerated {
1335 get { return (flags & Flags.CompilerGenerated) != 0; }
1336 set { flags |= Flags.CompilerGenerated; }
1339 public override string ToString ()
1341 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1342 Name, Type, VariableInfo, Location);
1346 get { return (flags & Flags.Used) != 0; }
1347 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1350 public bool ReadOnly {
1351 get { return (flags & Flags.ReadOnly) != 0; }
1354 public void SetReadOnlyContext (ReadOnlyContext context)
1356 flags |= Flags.ReadOnly;
1357 ro_context = context;
1360 public string GetReadOnlyContext ()
1363 throw new InternalErrorException ("Variable is not readonly");
1365 switch (ro_context) {
1366 case ReadOnlyContext.Fixed:
1367 return "fixed variable";
1368 case ReadOnlyContext.Foreach:
1369 return "foreach iteration variable";
1370 case ReadOnlyContext.Using:
1371 return "using variable";
1373 throw new NotImplementedException ();
1377 // Whether the variable is pinned, if Pinned the variable has been
1378 // allocated in a pinned slot with DeclareLocal.
1380 public bool Pinned {
1381 get { return (flags & Flags.Pinned) != 0; }
1382 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1385 public bool IsThis {
1386 get { return (flags & Flags.IsThis) != 0; }
1387 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1390 Block IKnownVariable.Block {
1391 get { return Block; }
1394 Location IKnownVariable.Location {
1395 get { return Location; }
1398 public LocalInfo Clone (CloneContext clonectx)
1401 // Variables in anonymous block are not resolved yet
1403 if (VariableType == null)
1404 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1407 // Variables in method block are resolved
1409 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1410 li.VariableType = VariableType;
1416 /// Block represents a C# block.
1420 /// This class is used in a number of places: either to represent
1421 /// explicit blocks that the programmer places or implicit blocks.
1423 /// Implicit blocks are used as labels or to introduce variable
1426 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1427 /// they contain extra information that is not necessary on normal blocks.
1429 public class Block : Statement {
1430 public Block Parent;
1431 public Location StartLocation;
1432 public Location EndLocation = Location.Null;
1434 public ExplicitBlock Explicit;
1435 public ToplevelBlock Toplevel; // TODO: Use Explicit
1438 public enum Flags : byte {
1441 VariablesInitialized = 4,
1445 HasCapturedVariable = 64,
1446 HasCapturedThis = 128
1448 protected Flags flags;
1450 public bool Unchecked {
1451 get { return (flags & Flags.Unchecked) != 0; }
1452 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1455 public bool Unsafe {
1456 get { return (flags & Flags.Unsafe) != 0; }
1457 set { flags |= Flags.Unsafe; }
1461 // The statements in this block
1463 protected ArrayList statements;
1466 // An array of Blocks. We keep track of children just
1467 // to generate the local variable declarations.
1469 // Statements and child statements are handled through the
1475 // Labels. (label, block) pairs.
1477 protected HybridDictionary labels;
1480 // Keeps track of (name, type) pairs
1482 IDictionary variables;
1485 // Keeps track of constants
1486 HybridDictionary constants;
1489 // Temporary variables.
1491 ArrayList temporary_variables;
1494 // If this is a switch section, the enclosing switch block.
1498 protected ArrayList scope_initializers;
1500 ArrayList anonymous_children;
1502 protected static int id;
1506 int assignable_slots;
1507 bool unreachable_shown;
1510 public Block (Block parent)
1511 : this (parent, (Flags) 0, Location.Null, Location.Null)
1514 public Block (Block parent, Flags flags)
1515 : this (parent, flags, Location.Null, Location.Null)
1518 public Block (Block parent, Location start, Location end)
1519 : this (parent, (Flags) 0, start, end)
1523 // Useful when TopLevel block is downgraded to normal block
1525 public Block (ToplevelBlock parent, ToplevelBlock source)
1526 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1528 statements = source.statements;
1529 children = source.children;
1530 labels = source.labels;
1531 variables = source.variables;
1532 constants = source.constants;
1533 switch_block = source.switch_block;
1536 public Block (Block parent, Flags flags, Location start, Location end)
1538 if (parent != null) {
1539 parent.AddChild (this);
1541 // the appropriate constructors will fixup these fields
1542 Toplevel = parent.Toplevel;
1543 Explicit = parent.Explicit;
1546 this.Parent = parent;
1548 this.StartLocation = start;
1549 this.EndLocation = end;
1552 statements = new ArrayList (4);
1555 public Block CreateSwitchBlock (Location start)
1557 // FIXME: should this be implicit?
1558 Block new_block = new ExplicitBlock (this, start, start);
1559 new_block.switch_block = this;
1564 get { return this_id; }
1567 public IDictionary Variables {
1569 if (variables == null)
1570 variables = new ListDictionary ();
1575 void AddChild (Block b)
1577 if (children == null)
1578 children = new ArrayList (1);
1583 public void SetEndLocation (Location loc)
1588 protected static void Error_158 (string name, Location loc)
1590 Report.Error (158, loc, "The label `{0}' shadows another label " +
1591 "by the same name in a contained scope", name);
1595 /// Adds a label to the current block.
1599 /// false if the name already exists in this block. true
1603 public bool AddLabel (LabeledStatement target)
1605 if (switch_block != null)
1606 return switch_block.AddLabel (target);
1608 string name = target.Name;
1611 while (cur != null) {
1612 LabeledStatement s = cur.DoLookupLabel (name);
1614 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1615 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1619 if (this == Explicit)
1625 while (cur != null) {
1626 if (cur.DoLookupLabel (name) != null) {
1627 Error_158 (name, target.loc);
1631 if (children != null) {
1632 foreach (Block b in children) {
1633 LabeledStatement s = b.DoLookupLabel (name);
1637 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1638 Error_158 (name, target.loc);
1646 Toplevel.CheckError158 (name, target.loc);
1649 labels = new HybridDictionary();
1651 labels.Add (name, target);
1655 public LabeledStatement LookupLabel (string name)
1657 LabeledStatement s = DoLookupLabel (name);
1661 if (children == null)
1664 foreach (Block child in children) {
1665 if (Explicit != child.Explicit)
1668 s = child.LookupLabel (name);
1676 LabeledStatement DoLookupLabel (string name)
1678 if (switch_block != null)
1679 return switch_block.LookupLabel (name);
1682 if (labels.Contains (name))
1683 return ((LabeledStatement) labels [name]);
1688 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1691 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1692 while (kvi == null) {
1693 b = b.Explicit.Parent;
1696 kvi = b.Explicit.GetKnownVariable (name);
1702 // Is kvi.Block nested inside 'b'
1703 if (b.Explicit != kvi.Block.Explicit) {
1705 // If a variable by the same name it defined in a nested block of this
1706 // block, we violate the invariant meaning in a block.
1709 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1710 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1715 // It's ok if the definition is in a nested subblock of b, but not
1716 // nested inside this block -- a definition in a sibling block
1717 // should not affect us.
1723 // Block 'b' and kvi.Block are the same textual block.
1724 // However, different variables are extant.
1726 // Check if the variable is in scope in both blocks. We use
1727 // an indirect check that depends on AddVariable doing its
1728 // part in maintaining the invariant-meaning-in-block property.
1730 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1733 if (this is ToplevelBlock) {
1734 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1735 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1740 // Even though we detected the error when the name is used, we
1741 // treat it as if the variable declaration was in error.
1743 Report.SymbolRelatedToPreviousError (loc, name);
1744 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1748 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1750 LocalInfo vi = GetLocalInfo (name);
1752 Report.SymbolRelatedToPreviousError (vi.Location, name);
1753 if (Explicit == vi.Block.Explicit) {
1754 Error_AlreadyDeclared (l, name, null);
1756 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1757 "parent or current" : "parent");
1762 if (block != null) {
1763 Expression e = block.GetParameterReference (name, Location.Null);
1765 ParameterReference pr = e as ParameterReference;
1766 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1767 Error_AlreadyDeclared (loc, name);
1769 Error_AlreadyDeclared (loc, name, "parent or current");
1777 public LocalInfo AddVariable (Expression type, string name, Location l)
1779 if (!CheckParentConflictName (Toplevel, name, l))
1782 if (Toplevel.GenericMethod != null) {
1783 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1784 if (tp.Name == name) {
1785 Report.SymbolRelatedToPreviousError (tp);
1786 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1792 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1794 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1795 Error_AlreadyDeclared (l, name, "child");
1799 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1802 if ((flags & Flags.VariablesInitialized) != 0)
1803 throw new InternalErrorException ("block has already been resolved");
1808 protected virtual void AddVariable (LocalInfo li)
1810 Variables.Add (li.Name, li);
1811 Explicit.AddKnownVariable (li.Name, li);
1814 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1816 if (reason == null) {
1817 Error_AlreadyDeclared (loc, var);
1821 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1822 "in this scope because it would give a different meaning " +
1823 "to `{0}', which is already used in a `{1}' scope " +
1824 "to denote something else", var, reason);
1827 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1829 Report.Error (128, loc,
1830 "A local variable named `{0}' is already defined in this scope", name);
1833 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1835 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1839 public bool AddConstant (Expression type, string name, Expression value, Location l)
1841 if (AddVariable (type, name, l) == null)
1844 if (constants == null)
1845 constants = new HybridDictionary();
1847 constants.Add (name, value);
1849 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1854 static int next_temp_id = 0;
1856 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1858 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1860 if (temporary_variables == null)
1861 temporary_variables = new ArrayList ();
1863 int id = ++next_temp_id;
1864 string name = "$s_" + id.ToString ();
1866 LocalInfo li = new LocalInfo (te, name, this, loc);
1867 li.CompilerGenerated = true;
1868 temporary_variables.Add (li);
1872 public LocalInfo GetLocalInfo (string name)
1875 for (Block b = this; b != null; b = b.Parent) {
1876 if (b.variables != null) {
1877 ret = (LocalInfo) b.variables [name];
1886 public Expression GetVariableType (string name)
1888 LocalInfo vi = GetLocalInfo (name);
1889 return vi == null ? null : vi.Type;
1892 public Expression GetConstantExpression (string name)
1894 for (Block b = this; b != null; b = b.Parent) {
1895 if (b.constants != null) {
1896 Expression ret = b.constants [name] as Expression;
1905 // It should be used by expressions which require to
1906 // register a statement during resolve process.
1908 public void AddScopeStatement (Statement s)
1910 if (scope_initializers == null)
1911 scope_initializers = new ArrayList ();
1913 scope_initializers.Add (s);
1916 public void AddStatement (Statement s)
1919 flags |= Flags.BlockUsed;
1923 get { return (flags & Flags.BlockUsed) != 0; }
1928 flags |= Flags.BlockUsed;
1931 public bool HasRet {
1932 get { return (flags & Flags.HasRet) != 0; }
1935 public int AssignableSlots {
1938 // if ((flags & Flags.VariablesInitialized) == 0)
1939 // throw new Exception ("Variables have not been initialized yet");
1940 return assignable_slots;
1944 public ArrayList AnonymousChildren {
1945 get { return anonymous_children; }
1948 public void AddAnonymousChild (ToplevelBlock b)
1950 if (anonymous_children == null)
1951 anonymous_children = new ArrayList ();
1953 anonymous_children.Add (b);
1956 void DoResolveConstants (BlockContext ec)
1958 if (constants == null)
1961 if (variables == null)
1962 throw new InternalErrorException ("cannot happen");
1964 foreach (DictionaryEntry de in variables) {
1965 string name = (string) de.Key;
1966 LocalInfo vi = (LocalInfo) de.Value;
1967 Type variable_type = vi.VariableType;
1969 if (variable_type == null) {
1970 if (vi.Type is VarExpr)
1971 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1976 Expression cv = (Expression) constants [name];
1980 // Don't let 'const int Foo = Foo;' succeed.
1981 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1982 // which in turn causes the 'must be constant' error to be triggered.
1983 constants.Remove (name);
1985 if (!Const.IsConstantTypeValid (variable_type)) {
1986 Const.Error_InvalidConstantType (variable_type, loc);
1990 ec.CurrentBlock = this;
1992 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1993 e = cv.Resolve (ec);
1998 Constant ce = e as Constant;
2000 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2004 e = ce.ConvertImplicitly (variable_type);
2006 if (TypeManager.IsReferenceType (variable_type))
2007 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2009 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2013 constants.Add (name, e);
2014 vi.IsConstant = true;
2018 protected void ResolveMeta (BlockContext ec, int offset)
2020 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2022 // If some parent block was unsafe, we remain unsafe even if this block
2023 // isn't explicitly marked as such.
2024 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2025 flags |= Flags.VariablesInitialized;
2027 if (variables != null) {
2028 foreach (LocalInfo li in variables.Values) {
2029 if (!li.Resolve (ec))
2031 li.VariableInfo = new VariableInfo (li, offset);
2032 offset += li.VariableInfo.Length;
2035 assignable_slots = offset;
2037 DoResolveConstants (ec);
2039 if (children == null)
2041 foreach (Block b in children)
2042 b.ResolveMeta (ec, offset);
2047 // Emits the local variable declarations for a block
2049 public virtual void EmitMeta (EmitContext ec)
2051 if (variables != null){
2052 foreach (LocalInfo vi in variables.Values)
2053 vi.ResolveVariable (ec);
2056 if (temporary_variables != null) {
2057 for (int i = 0; i < temporary_variables.Count; i++)
2058 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2061 if (children != null) {
2062 for (int i = 0; i < children.Count; i++)
2063 ((Block)children[i]).EmitMeta(ec);
2067 void UsageWarning ()
2069 if (variables == null || Report.WarningLevel < 3)
2072 foreach (DictionaryEntry de in variables) {
2073 LocalInfo vi = (LocalInfo) de.Value;
2076 string name = (string) de.Key;
2078 // vi.VariableInfo can be null for 'catch' variables
2079 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2080 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2082 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2087 static void CheckPossibleMistakenEmptyStatement (Statement s)
2091 // Some statements are wrapped by a Block. Since
2092 // others' internal could be changed, here I treat
2093 // them as possibly wrapped by Block equally.
2094 Block b = s as Block;
2095 if (b != null && b.statements.Count == 1)
2096 s = (Statement) b.statements [0];
2099 body = ((Lock) s).Statement;
2101 body = ((For) s).Statement;
2102 else if (s is Foreach)
2103 body = ((Foreach) s).Statement;
2104 else if (s is While)
2105 body = ((While) s).Statement;
2106 else if (s is Fixed)
2107 body = ((Fixed) s).Statement;
2108 else if (s is Using)
2109 body = ((Using) s).EmbeddedStatement;
2110 else if (s is UsingTemporary)
2111 body = ((UsingTemporary) s).Statement;
2115 if (body == null || body is EmptyStatement)
2116 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2119 public override bool Resolve (BlockContext ec)
2121 Block prev_block = ec.CurrentBlock;
2124 int errors = Report.Errors;
2126 ec.CurrentBlock = this;
2127 ec.StartFlowBranching (this);
2129 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2132 // Compiler generated scope statements
2134 if (scope_initializers != null) {
2135 foreach (Statement s in scope_initializers)
2140 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2141 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2142 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2143 // responsible for handling the situation.
2145 int statement_count = statements.Count;
2146 for (int ix = 0; ix < statement_count; ix++){
2147 Statement s = (Statement) statements [ix];
2148 // Check possible empty statement (CS0642)
2149 if (Report.WarningLevel >= 3 &&
2150 ix + 1 < statement_count &&
2151 statements [ix + 1] is ExplicitBlock)
2152 CheckPossibleMistakenEmptyStatement (s);
2155 // Warn if we detect unreachable code.
2158 if (s is EmptyStatement)
2161 if (!unreachable_shown && !(s is LabeledStatement)) {
2162 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2163 unreachable_shown = true;
2166 Block c_block = s as Block;
2167 if (c_block != null)
2168 c_block.unreachable = c_block.unreachable_shown = true;
2172 // Note that we're not using ResolveUnreachable() for unreachable
2173 // statements here. ResolveUnreachable() creates a temporary
2174 // flow branching and kills it afterwards. This leads to problems
2175 // if you have two unreachable statements where the first one
2176 // assigns a variable and the second one tries to access it.
2179 if (!s.Resolve (ec)) {
2181 if (ec.IsInProbingMode)
2184 statements [ix] = EmptyStatement.Value;
2188 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2189 statements [ix] = EmptyStatement.Value;
2191 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2192 if (unreachable && s is LabeledStatement)
2193 throw new InternalErrorException ("should not happen");
2196 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2197 ec.CurrentBranching, statement_count);
2199 while (ec.CurrentBranching is FlowBranchingLabeled)
2200 ec.EndFlowBranching ();
2202 bool flow_unreachable = ec.EndFlowBranching ();
2204 ec.CurrentBlock = prev_block;
2206 if (flow_unreachable)
2207 flags |= Flags.HasRet;
2209 // If we're a non-static `struct' constructor which doesn't have an
2210 // initializer, then we must initialize all of the struct's fields.
2211 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2214 if ((labels != null) && (Report.WarningLevel >= 2)) {
2215 foreach (LabeledStatement label in labels.Values)
2216 if (!label.HasBeenReferenced)
2217 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2220 if (ok && errors == Report.Errors)
2226 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2228 unreachable_shown = true;
2232 Report.Warning (162, 2, loc, "Unreachable code detected");
2234 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2235 bool ok = Resolve (ec);
2236 ec.KillFlowBranching ();
2241 protected override void DoEmit (EmitContext ec)
2243 for (int ix = 0; ix < statements.Count; ix++){
2244 Statement s = (Statement) statements [ix];
2249 public override void Emit (EmitContext ec)
2251 if (scope_initializers != null)
2252 EmitScopeInitializers (ec);
2254 ec.Mark (StartLocation);
2257 if (SymbolWriter.HasSymbolWriter)
2258 EmitSymbolInfo (ec);
2261 protected void EmitScopeInitializers (EmitContext ec)
2263 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2265 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2266 foreach (Statement s in scope_initializers)
2270 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2273 protected virtual void EmitSymbolInfo (EmitContext ec)
2275 if (variables != null) {
2276 foreach (LocalInfo vi in variables.Values) {
2277 vi.EmitSymbolInfo (ec);
2282 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2284 MutateVariables (storey);
2286 if (scope_initializers != null) {
2287 foreach (Statement s in scope_initializers)
2288 s.MutateHoistedGenericType (storey);
2291 foreach (Statement s in statements)
2292 s.MutateHoistedGenericType (storey);
2295 void MutateVariables (AnonymousMethodStorey storey)
2297 if (variables != null) {
2298 foreach (LocalInfo vi in variables.Values) {
2299 vi.VariableType = storey.MutateType (vi.VariableType);
2303 if (temporary_variables != null) {
2304 foreach (LocalInfo vi in temporary_variables)
2305 vi.VariableType = storey.MutateType (vi.VariableType);
2309 public override string ToString ()
2311 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2314 protected override void CloneTo (CloneContext clonectx, Statement t)
2316 Block target = (Block) t;
2318 clonectx.AddBlockMap (this, target);
2320 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2321 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2323 target.Parent = clonectx.RemapBlockCopy (Parent);
2325 if (variables != null){
2326 target.variables = new Hashtable ();
2328 foreach (DictionaryEntry de in variables){
2329 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2330 target.variables [de.Key] = newlocal;
2331 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2335 target.statements = new ArrayList (statements.Count);
2336 foreach (Statement s in statements)
2337 target.statements.Add (s.Clone (clonectx));
2339 if (target.children != null){
2340 target.children = new ArrayList (children.Count);
2341 foreach (Block b in children){
2342 target.children.Add (clonectx.LookupBlock (b));
2347 // TODO: labels, switch_block, constants (?), anonymous_children
2352 public class ExplicitBlock : Block {
2353 HybridDictionary known_variables;
2354 protected AnonymousMethodStorey am_storey;
2356 public ExplicitBlock (Block parent, Location start, Location end)
2357 : this (parent, (Flags) 0, start, end)
2361 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2362 : base (parent, flags, start, end)
2364 this.Explicit = this;
2368 // Marks a variable with name @name as being used in this or a child block.
2369 // If a variable name has been used in a child block, it's illegal to
2370 // declare a variable with the same name in the current block.
2372 internal void AddKnownVariable (string name, IKnownVariable info)
2374 if (known_variables == null)
2375 known_variables = new HybridDictionary();
2377 known_variables [name] = info;
2380 Parent.Explicit.AddKnownVariable (name, info);
2383 public AnonymousMethodStorey AnonymousMethodStorey {
2384 get { return am_storey; }
2388 // Creates anonymous method storey in current block
2390 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2393 // When referencing a variable in iterator storey from children anonymous method
2395 if (Toplevel.am_storey is IteratorStorey) {
2396 return Toplevel.am_storey;
2400 // An iterator has only 1 storey block
2402 if (ec.CurrentIterator != null)
2403 return ec.CurrentIterator.Storey;
2405 if (am_storey == null) {
2406 MemberBase mc = ec.MemberContext as MemberBase;
2407 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2410 // Creates anonymous method storey for this block
2412 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2418 public override void Emit (EmitContext ec)
2420 if (am_storey != null)
2421 am_storey.EmitStoreyInstantiation (ec);
2423 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2424 if (emit_debug_info)
2429 if (emit_debug_info)
2433 public override void EmitMeta (EmitContext ec)
2436 // Creates anonymous method storey
2438 if (am_storey != null) {
2439 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2441 // Creates parent storey reference when hoisted this is accessible
2443 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2444 ExplicitBlock parent = Toplevel.Parent.Explicit;
2447 // Hoisted this exists in top-level parent storey only
2449 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2450 parent = parent.Parent.Explicit;
2452 am_storey.AddParentStoreyReference (parent.am_storey);
2455 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2458 am_storey.DefineType ();
2459 am_storey.ResolveType ();
2460 am_storey.Define ();
2461 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2463 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2464 if (ref_blocks != null) {
2465 foreach (ExplicitBlock ref_block in ref_blocks) {
2466 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2467 if (b.am_storey != null) {
2468 b.am_storey.AddParentStoreyReference (am_storey);
2470 // Stop propagation inside same top block
2471 if (b.Toplevel == Toplevel)
2476 b.HasCapturedVariable = true;
2485 internal IKnownVariable GetKnownVariable (string name)
2487 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2490 public bool HasCapturedThis
2492 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2493 get { return (flags & Flags.HasCapturedThis) != 0; }
2496 public bool HasCapturedVariable
2498 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2499 get { return (flags & Flags.HasCapturedVariable) != 0; }
2502 protected override void CloneTo (CloneContext clonectx, Statement t)
2504 ExplicitBlock target = (ExplicitBlock) t;
2505 target.known_variables = null;
2506 base.CloneTo (clonectx, t);
2510 public class ToplevelParameterInfo : IKnownVariable {
2511 public readonly ToplevelBlock Block;
2512 public readonly int Index;
2513 public VariableInfo VariableInfo;
2515 Block IKnownVariable.Block {
2516 get { return Block; }
2518 public Parameter Parameter {
2519 get { return Block.Parameters [Index]; }
2522 public Type ParameterType {
2523 get { return Block.Parameters.Types [Index]; }
2526 public Location Location {
2527 get { return Parameter.Location; }
2530 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2538 // A toplevel block contains extra information, the split is done
2539 // only to separate information that would otherwise bloat the more
2540 // lightweight Block.
2542 // In particular, this was introduced when the support for Anonymous
2543 // Methods was implemented.
2545 public class ToplevelBlock : ExplicitBlock
2548 // Block is converted to an expression
2550 sealed class BlockScopeExpression : Expression
2553 readonly ToplevelBlock block;
2555 public BlockScopeExpression (Expression child, ToplevelBlock block)
2561 public override Expression CreateExpressionTree (ResolveContext ec)
2563 throw new NotSupportedException ();
2566 public override Expression DoResolve (ResolveContext ec)
2571 child = child.Resolve (ec);
2575 eclass = child.eclass;
2580 public override void Emit (EmitContext ec)
2582 block.EmitMeta (ec);
2583 block.EmitScopeInitializers (ec);
2587 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2589 type = storey.MutateType (type);
2590 child.MutateHoistedGenericType (storey);
2591 block.MutateHoistedGenericType (storey);
2595 GenericMethod generic;
2596 protected ParametersCompiled parameters;
2597 ToplevelParameterInfo[] parameter_info;
2598 LocalInfo this_variable;
2602 public HoistedVariable HoistedThisVariable;
2604 public bool Resolved {
2611 // The parameters for the block.
2613 public ParametersCompiled Parameters {
2614 get { return parameters; }
2617 public GenericMethod GenericMethod {
2618 get { return generic; }
2621 public ToplevelBlock Container {
2622 get { return Parent == null ? null : Parent.Toplevel; }
2625 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2626 this (parent, (Flags) 0, parameters, start)
2630 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2631 this (parent, parameters, start)
2633 this.generic = generic;
2636 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2637 this (null, (Flags) 0, parameters, start)
2641 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2642 this (null, flags, parameters, start)
2646 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2647 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2648 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2649 base (null, flags, start, Location.Null)
2651 this.Toplevel = this;
2653 this.parameters = parameters;
2654 this.Parent = parent;
2656 parent.AddAnonymousChild (this);
2658 if (!this.parameters.IsEmpty)
2659 ProcessParameters ();
2662 public ToplevelBlock (Location loc)
2663 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2667 protected override void CloneTo (CloneContext clonectx, Statement t)
2669 ToplevelBlock target = (ToplevelBlock) t;
2670 base.CloneTo (clonectx, t);
2672 if (parameters.Count != 0)
2673 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2674 for (int i = 0; i < parameters.Count; ++i)
2675 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2678 public bool CheckError158 (string name, Location loc)
2680 if (AnonymousChildren != null) {
2681 foreach (ToplevelBlock child in AnonymousChildren) {
2682 if (!child.CheckError158 (name, loc))
2687 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2688 if (!c.DoCheckError158 (name, loc))
2695 void ProcessParameters ()
2697 int n = parameters.Count;
2698 parameter_info = new ToplevelParameterInfo [n];
2699 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2700 for (int i = 0; i < n; ++i) {
2701 parameter_info [i] = new ToplevelParameterInfo (this, i);
2703 Parameter p = parameters [i];
2707 string name = p.Name;
2708 if (CheckParentConflictName (top_parent, name, loc))
2709 AddKnownVariable (name, parameter_info [i]);
2712 // mark this block as "used" so that we create local declarations in a sub-block
2713 // FIXME: This appears to uncover a lot of bugs
2717 bool DoCheckError158 (string name, Location loc)
2719 LabeledStatement s = LookupLabel (name);
2721 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2722 Error_158 (name, loc);
2729 public override Expression CreateExpressionTree (ResolveContext ec)
2731 if (statements.Count == 1) {
2732 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2733 if (scope_initializers != null)
2734 expr = new BlockScopeExpression (expr, this);
2739 return base.CreateExpressionTree (ec);
2743 // Reformats this block to be top-level iterator block
2745 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2749 // Creates block with original statements
2750 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2752 source.statements = new ArrayList (1);
2753 source.AddStatement (new Return (iterator, iterator.Location));
2754 source.IsIterator = false;
2756 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2757 source.am_storey = iterator_storey;
2758 return iterator_storey;
2762 // Returns a parameter reference expression for the given name,
2763 // or null if there is no such parameter
2765 public Expression GetParameterReference (string name, Location loc)
2767 for (ToplevelBlock t = this; t != null; t = t.Container) {
2768 Expression expr = t.GetParameterReferenceExpression (name, loc);
2776 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2778 int idx = parameters.GetParameterIndexByName (name);
2780 null : new ParameterReference (parameter_info [idx], loc);
2784 // Returns the "this" instance variable of this block.
2785 // See AddThisVariable() for more information.
2787 public LocalInfo ThisVariable {
2788 get { return this_variable; }
2792 // This is used by non-static `struct' constructors which do not have an
2793 // initializer - in this case, the constructor must initialize all of the
2794 // struct's fields. To do this, we add a "this" variable and use the flow
2795 // analysis code to ensure that it's been fully initialized before control
2796 // leaves the constructor.
2798 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2800 if (this_variable == null) {
2801 this_variable = new LocalInfo (ds, this, l);
2802 this_variable.Used = true;
2803 this_variable.IsThis = true;
2805 Variables.Add ("this", this_variable);
2808 return this_variable;
2811 public bool IsIterator {
2812 get { return (flags & Flags.IsIterator) != 0; }
2813 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2816 public bool IsThisAssigned (BlockContext ec)
2818 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2821 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2829 if (!ResolveMeta (rc, ip))
2832 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2833 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2838 unreachable = top_level.End ();
2840 } catch (Exception) {
2842 if (rc.CurrentBlock != null) {
2843 Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2845 Report.Error (587, "Internal compiler error: Phase Resolve");
2851 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2852 if (rc.CurrentAnonymousMethod == null) {
2853 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2855 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2856 Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2857 rc.CurrentAnonymousMethod.GetSignatureForError ());
2865 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2867 int errors = Report.Errors;
2868 int orig_count = parameters.Count;
2873 // Assert: orig_count != parameter.Count => orig_count == 0
2874 if (orig_count != 0 && orig_count != parameters.Count)
2875 throw new InternalErrorException ("parameter information mismatch");
2877 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2879 for (int i = 0; i < orig_count; ++i) {
2880 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2882 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2885 VariableInfo vi = new VariableInfo (ip, i, offset);
2886 parameter_info [i].VariableInfo = vi;
2887 offset += vi.Length;
2890 ResolveMeta (ec, offset);
2892 return Report.Errors == errors;
2896 // Check whether all `out' parameters have been assigned.
2898 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2900 if (vector.IsUnreachable)
2903 int n = parameter_info == null ? 0 : parameter_info.Length;
2905 for (int i = 0; i < n; i++) {
2906 VariableInfo var = parameter_info [i].VariableInfo;
2911 if (vector.IsAssigned (var, false))
2914 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2919 public override void Emit (EmitContext ec)
2921 if (Report.Errors > 0)
2929 if (ec.HasReturnLabel)
2930 ec.ReturnLabel = ec.ig.DefineLabel ();
2934 ec.Mark (EndLocation);
2936 if (ec.HasReturnLabel)
2937 ec.ig.MarkLabel (ec.ReturnLabel);
2939 if (ec.return_value != null) {
2940 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2941 ec.ig.Emit (OpCodes.Ret);
2944 // If `HasReturnLabel' is set, then we already emitted a
2945 // jump to the end of the method, so we must emit a `ret'
2948 // Unfortunately, System.Reflection.Emit automatically emits
2949 // a leave to the end of a finally block. This is a problem
2950 // if no code is following the try/finally block since we may
2951 // jump to a point after the end of the method.
2952 // As a workaround, we're always creating a return label in
2956 if (ec.HasReturnLabel || !unreachable) {
2957 if (ec.ReturnType != TypeManager.void_type)
2958 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2959 ec.ig.Emit (OpCodes.Ret);
2964 } catch (Exception e){
2965 Console.WriteLine ("Exception caught by the compiler while emitting:");
2966 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2968 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2974 public override void EmitMeta (EmitContext ec)
2976 parameters.ResolveVariable ();
2978 // Avoid declaring an IL variable for this_variable since it is not accessed
2979 // from the generated IL
2980 if (this_variable != null)
2981 Variables.Remove ("this");
2985 protected override void EmitSymbolInfo (EmitContext ec)
2987 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2988 if ((ae != null) && (ae.Storey != null))
2989 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2991 base.EmitSymbolInfo (ec);
2995 public class SwitchLabel {
3002 Label il_label_code;
3003 bool il_label_code_set;
3005 public static readonly object NullStringCase = new object ();
3008 // if expr == null, then it is the default case.
3010 public SwitchLabel (Expression expr, Location l)
3016 public Expression Label {
3022 public Location Location {
3026 public object Converted {
3032 public Label GetILLabel (EmitContext ec)
3035 il_label = ec.ig.DefineLabel ();
3036 il_label_set = true;
3041 public Label GetILLabelCode (EmitContext ec)
3043 if (!il_label_code_set){
3044 il_label_code = ec.ig.DefineLabel ();
3045 il_label_code_set = true;
3047 return il_label_code;
3051 // Resolves the expression, reduces it to a literal if possible
3052 // and then converts it to the requested type.
3054 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3056 Expression e = label.Resolve (ec);
3061 Constant c = e as Constant;
3063 Report.Error (150, loc, "A constant value is expected");
3067 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3068 converted = NullStringCase;
3072 if (allow_nullable && c.GetValue () == null) {
3073 converted = NullStringCase;
3077 c = c.ImplicitConversionRequired (ec, required_type, loc);
3081 converted = c.GetValue ();
3085 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3088 if (converted == null)
3090 else if (converted == NullStringCase)
3093 label = converted.ToString ();
3095 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3096 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3099 public SwitchLabel Clone (CloneContext clonectx)
3101 return new SwitchLabel (label.Clone (clonectx), loc);
3105 public class SwitchSection {
3106 // An array of SwitchLabels.
3107 public readonly ArrayList Labels;
3108 public readonly Block Block;
3110 public SwitchSection (ArrayList labels, Block block)
3116 public SwitchSection Clone (CloneContext clonectx)
3118 ArrayList cloned_labels = new ArrayList ();
3120 foreach (SwitchLabel sl in cloned_labels)
3121 cloned_labels.Add (sl.Clone (clonectx));
3123 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3127 public class Switch : Statement {
3128 public ArrayList Sections;
3129 public Expression Expr;
3132 /// Maps constants whose type type SwitchType to their SwitchLabels.
3134 public IDictionary Elements;
3137 /// The governing switch type
3139 public Type SwitchType;
3144 Label default_target;
3146 Expression new_expr;
3149 SwitchSection constant_section;
3150 SwitchSection default_section;
3152 ExpressionStatement string_dictionary;
3153 FieldExpr switch_cache_field;
3154 static int unique_counter;
3157 // Nullable Types support
3159 Nullable.Unwrap unwrap;
3161 protected bool HaveUnwrap {
3162 get { return unwrap != null; }
3166 // The types allowed to be implicitly cast from
3167 // on the governing type
3169 static Type [] allowed_types;
3171 public Switch (Expression e, ArrayList sects, Location l)
3178 public bool GotDefault {
3180 return default_section != null;
3184 public Label DefaultTarget {
3186 return default_target;
3191 // Determines the governing type for a switch. The returned
3192 // expression might be the expression from the switch, or an
3193 // expression that includes any potential conversions to the
3194 // integral types or to string.
3196 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3200 if (t == TypeManager.byte_type ||
3201 t == TypeManager.sbyte_type ||
3202 t == TypeManager.ushort_type ||
3203 t == TypeManager.short_type ||
3204 t == TypeManager.uint32_type ||
3205 t == TypeManager.int32_type ||
3206 t == TypeManager.uint64_type ||
3207 t == TypeManager.int64_type ||
3208 t == TypeManager.char_type ||
3209 t == TypeManager.string_type ||
3210 t == TypeManager.bool_type ||
3211 TypeManager.IsEnumType (t))
3214 if (allowed_types == null){
3215 allowed_types = new Type [] {
3216 TypeManager.sbyte_type,
3217 TypeManager.byte_type,
3218 TypeManager.short_type,
3219 TypeManager.ushort_type,
3220 TypeManager.int32_type,
3221 TypeManager.uint32_type,
3222 TypeManager.int64_type,
3223 TypeManager.uint64_type,
3224 TypeManager.char_type,
3225 TypeManager.string_type
3230 // Try to find a *user* defined implicit conversion.
3232 // If there is no implicit conversion, or if there are multiple
3233 // conversions, we have to report an error
3235 Expression converted = null;
3236 foreach (Type tt in allowed_types){
3239 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3244 // Ignore over-worked ImplicitUserConversions that do
3245 // an implicit conversion in addition to the user conversion.
3247 if (!(e is UserCast))
3250 if (converted != null){
3251 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3261 // Performs the basic sanity checks on the switch statement
3262 // (looks for duplicate keys and non-constant expressions).
3264 // It also returns a hashtable with the keys that we will later
3265 // use to compute the switch tables
3267 bool CheckSwitch (ResolveContext ec)
3270 Elements = Sections.Count > 10 ?
3271 (IDictionary)new Hashtable () :
3272 (IDictionary)new ListDictionary ();
3274 foreach (SwitchSection ss in Sections){
3275 foreach (SwitchLabel sl in ss.Labels){
3276 if (sl.Label == null){
3277 if (default_section != null){
3278 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3281 default_section = ss;
3285 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3290 object key = sl.Converted;
3291 if (key == SwitchLabel.NullStringCase)
3292 has_null_case = true;
3295 Elements.Add (key, sl);
3296 } catch (ArgumentException) {
3297 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3305 void EmitObjectInteger (ILGenerator ig, object k)
3308 IntConstant.EmitInt (ig, (int) k);
3309 else if (k is Constant) {
3310 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3313 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3316 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3318 IntConstant.EmitInt (ig, (int) (long) k);
3319 ig.Emit (OpCodes.Conv_I8);
3322 LongConstant.EmitLong (ig, (long) k);
3324 else if (k is ulong)
3326 ulong ul = (ulong) k;
3329 IntConstant.EmitInt (ig, unchecked ((int) ul));
3330 ig.Emit (OpCodes.Conv_U8);
3334 LongConstant.EmitLong (ig, unchecked ((long) ul));
3338 IntConstant.EmitInt (ig, (int) ((char) k));
3339 else if (k is sbyte)
3340 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3342 IntConstant.EmitInt (ig, (int) ((byte) k));
3343 else if (k is short)
3344 IntConstant.EmitInt (ig, (int) ((short) k));
3345 else if (k is ushort)
3346 IntConstant.EmitInt (ig, (int) ((ushort) k));
3348 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3350 throw new Exception ("Unhandled case");
3353 // structure used to hold blocks of keys while calculating table switch
3354 class KeyBlock : IComparable
3356 public KeyBlock (long _first)
3358 first = last = _first;
3362 public ArrayList element_keys = null;
3363 // how many items are in the bucket
3364 public int Size = 1;
3367 get { return (int) (last - first + 1); }
3369 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3371 return kb_last.last - kb_first.first + 1;
3373 public int CompareTo (object obj)
3375 KeyBlock kb = (KeyBlock) obj;
3376 int nLength = Length;
3377 int nLengthOther = kb.Length;
3378 if (nLengthOther == nLength)
3379 return (int) (kb.first - first);
3380 return nLength - nLengthOther;
3385 /// This method emits code for a lookup-based switch statement (non-string)
3386 /// Basically it groups the cases into blocks that are at least half full,
3387 /// and then spits out individual lookup opcodes for each block.
3388 /// It emits the longest blocks first, and short blocks are just
3389 /// handled with direct compares.
3391 /// <param name="ec"></param>
3392 /// <param name="val"></param>
3393 /// <returns></returns>
3394 void TableSwitchEmit (EmitContext ec, Expression val)
3396 int element_count = Elements.Count;
3397 object [] element_keys = new object [element_count];
3398 Elements.Keys.CopyTo (element_keys, 0);
3399 Array.Sort (element_keys);
3401 // initialize the block list with one element per key
3402 ArrayList key_blocks = new ArrayList (element_count);
3403 foreach (object key in element_keys)
3404 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3406 KeyBlock current_kb;
3407 // iteratively merge the blocks while they are at least half full
3408 // there's probably a really cool way to do this with a tree...
3409 while (key_blocks.Count > 1)
3411 ArrayList key_blocks_new = new ArrayList ();
3412 current_kb = (KeyBlock) key_blocks [0];
3413 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3415 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3416 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3419 current_kb.last = kb.last;
3420 current_kb.Size += kb.Size;
3424 // start a new block
3425 key_blocks_new.Add (current_kb);
3429 key_blocks_new.Add (current_kb);
3430 if (key_blocks.Count == key_blocks_new.Count)
3432 key_blocks = key_blocks_new;
3435 // initialize the key lists
3436 foreach (KeyBlock kb in key_blocks)
3437 kb.element_keys = new ArrayList ();
3439 // fill the key lists
3441 if (key_blocks.Count > 0) {
3442 current_kb = (KeyBlock) key_blocks [0];
3443 foreach (object key in element_keys)
3445 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3446 System.Convert.ToInt64 (key) > current_kb.last;
3448 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3449 current_kb.element_keys.Add (key);
3453 // sort the blocks so we can tackle the largest ones first
3456 // okay now we can start...
3457 ILGenerator ig = ec.ig;
3458 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3459 Label lbl_default = default_target;
3461 Type type_keys = null;
3462 if (element_keys.Length > 0)
3463 type_keys = element_keys [0].GetType (); // used for conversions
3467 if (TypeManager.IsEnumType (SwitchType))
3468 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3470 compare_type = SwitchType;
3472 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3474 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3475 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3478 foreach (object key in kb.element_keys) {
3479 SwitchLabel sl = (SwitchLabel) Elements [key];
3480 if (key is int && (int) key == 0) {
3481 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3484 EmitObjectInteger (ig, key);
3485 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3491 // TODO: if all the keys in the block are the same and there are
3492 // no gaps/defaults then just use a range-check.
3493 if (compare_type == TypeManager.int64_type ||
3494 compare_type == TypeManager.uint64_type)
3496 // TODO: optimize constant/I4 cases
3498 // check block range (could be > 2^31)
3500 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3501 ig.Emit (OpCodes.Blt, lbl_default);
3503 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3504 ig.Emit (OpCodes.Bgt, lbl_default);
3510 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3511 ig.Emit (OpCodes.Sub);
3513 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3519 int first = (int) kb.first;
3522 IntConstant.EmitInt (ig, first);
3523 ig.Emit (OpCodes.Sub);
3527 IntConstant.EmitInt (ig, -first);
3528 ig.Emit (OpCodes.Add);
3532 // first, build the list of labels for the switch
3534 int cJumps = kb.Length;
3535 Label [] switch_labels = new Label [cJumps];
3536 for (int iJump = 0; iJump < cJumps; iJump++)
3538 object key = kb.element_keys [iKey];
3539 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3541 SwitchLabel sl = (SwitchLabel) Elements [key];
3542 switch_labels [iJump] = sl.GetILLabel (ec);
3546 switch_labels [iJump] = lbl_default;
3548 // emit the switch opcode
3549 ig.Emit (OpCodes.Switch, switch_labels);
3552 // mark the default for this block
3554 ig.MarkLabel (lbl_default);
3557 // TODO: find the default case and emit it here,
3558 // to prevent having to do the following jump.
3559 // make sure to mark other labels in the default section
3561 // the last default just goes to the end
3562 if (element_keys.Length > 0)
3563 ig.Emit (OpCodes.Br, lbl_default);
3565 // now emit the code for the sections
3566 bool found_default = false;
3568 foreach (SwitchSection ss in Sections) {
3569 foreach (SwitchLabel sl in ss.Labels) {
3570 if (sl.Converted == SwitchLabel.NullStringCase) {
3571 ig.MarkLabel (null_target);
3572 } else if (sl.Label == null) {
3573 ig.MarkLabel (lbl_default);
3574 found_default = true;
3576 ig.MarkLabel (null_target);
3578 ig.MarkLabel (sl.GetILLabel (ec));
3579 ig.MarkLabel (sl.GetILLabelCode (ec));
3584 if (!found_default) {
3585 ig.MarkLabel (lbl_default);
3586 if (!has_null_case) {
3587 ig.MarkLabel (null_target);
3591 ig.MarkLabel (lbl_end);
3594 SwitchSection FindSection (SwitchLabel label)
3596 foreach (SwitchSection ss in Sections){
3597 foreach (SwitchLabel sl in ss.Labels){
3606 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3608 foreach (SwitchSection ss in Sections)
3609 ss.Block.MutateHoistedGenericType (storey);
3612 public static void Reset ()
3615 allowed_types = null;
3618 public override bool Resolve (BlockContext ec)
3620 Expr = Expr.Resolve (ec);
3624 new_expr = SwitchGoverningType (ec, Expr);
3626 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3627 unwrap = Nullable.Unwrap.Create (Expr, false);
3631 new_expr = SwitchGoverningType (ec, unwrap);
3634 if (new_expr == null){
3635 Report.Error (151, loc,
3636 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3637 TypeManager.CSharpName (Expr.Type));
3642 SwitchType = new_expr.Type;
3644 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3645 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3649 if (!CheckSwitch (ec))
3653 Elements.Remove (SwitchLabel.NullStringCase);
3655 Switch old_switch = ec.Switch;
3657 ec.Switch.SwitchType = SwitchType;
3659 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3660 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3662 is_constant = new_expr is Constant;
3664 object key = ((Constant) new_expr).GetValue ();
3665 SwitchLabel label = (SwitchLabel) Elements [key];
3667 constant_section = FindSection (label);
3668 if (constant_section == null)
3669 constant_section = default_section;
3674 foreach (SwitchSection ss in Sections){
3676 ec.CurrentBranching.CreateSibling (
3677 null, FlowBranching.SiblingType.SwitchSection);
3681 if (is_constant && (ss != constant_section)) {
3682 // If we're a constant switch, we're only emitting
3683 // one single section - mark all the others as
3685 ec.CurrentBranching.CurrentUsageVector.Goto ();
3686 if (!ss.Block.ResolveUnreachable (ec, true)) {
3690 if (!ss.Block.Resolve (ec))
3695 if (default_section == null)
3696 ec.CurrentBranching.CreateSibling (
3697 null, FlowBranching.SiblingType.SwitchSection);
3699 ec.EndFlowBranching ();
3700 ec.Switch = old_switch;
3702 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3707 if (SwitchType == TypeManager.string_type && !is_constant) {
3708 // TODO: Optimize single case, and single+default case
3709 ResolveStringSwitchMap (ec);
3715 void ResolveStringSwitchMap (ResolveContext ec)
3717 FullNamedExpression string_dictionary_type;
3718 if (TypeManager.generic_ienumerable_type != null) {
3719 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3720 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3722 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3724 new TypeExpression (TypeManager.string_type, loc),
3725 new TypeExpression (TypeManager.int32_type, loc)), loc);
3727 MemberAccess system_collections_generic = new MemberAccess (
3728 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3730 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3733 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3734 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3735 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3736 if (!field.Define ())
3738 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3740 ArrayList init = new ArrayList ();
3743 string value = null;
3744 foreach (SwitchSection section in Sections) {
3745 int last_count = init.Count;
3746 foreach (SwitchLabel sl in section.Labels) {
3747 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3750 value = (string) sl.Converted;
3751 ArrayList init_args = new ArrayList (2);
3752 init_args.Add (new StringLiteral (value, sl.Location));
3753 init_args.Add (new IntConstant (counter, loc));
3754 init.Add (new CollectionElementInitializer (init_args, loc));
3758 // Don't add empty sections
3760 if (last_count == init.Count)
3763 Elements.Add (counter, section.Labels [0]);
3767 Arguments args = new Arguments (1);
3768 args.Add (new Argument (new IntConstant (init.Count, loc)));
3769 Expression initializer = new NewInitialize (string_dictionary_type, args,
3770 new CollectionOrObjectInitializers (init, loc), loc);
3772 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3773 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3776 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3778 ILGenerator ig = ec.ig;
3779 Label l_initialized = ig.DefineLabel ();
3782 // Skip initialization when value is null
3784 value.EmitBranchable (ec, null_target, false);
3787 // Check if string dictionary is initialized and initialize
3789 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3790 string_dictionary.EmitStatement (ec);
3791 ig.MarkLabel (l_initialized);
3793 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3795 ResolveContext rc = new ResolveContext (ec.MemberContext);
3797 if (TypeManager.generic_ienumerable_type != null) {
3798 Arguments get_value_args = new Arguments (2);
3799 get_value_args.Add (new Argument (value));
3800 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3801 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3802 if (get_item == null)
3806 // A value was not found, go to default case
3808 get_item.EmitBranchable (ec, default_target, false);
3810 Arguments get_value_args = new Arguments (1);
3811 get_value_args.Add (new Argument (value));
3813 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3814 if (get_item == null)
3817 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3818 get_item_object.EmitAssign (ec, get_item, true, false);
3819 ec.ig.Emit (OpCodes.Brfalse, default_target);
3821 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3822 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3824 get_item_int.EmitStatement (ec);
3825 get_item_object.Release (ec);
3828 TableSwitchEmit (ec, string_switch_variable);
3829 string_switch_variable.Release (ec);
3832 protected override void DoEmit (EmitContext ec)
3834 ILGenerator ig = ec.ig;
3836 default_target = ig.DefineLabel ();
3837 null_target = ig.DefineLabel ();
3839 // Store variable for comparission purposes
3840 // TODO: Don't duplicate non-captured VariableReference
3841 LocalTemporary value;
3843 value = new LocalTemporary (SwitchType);
3844 unwrap.EmitCheck (ec);
3845 ig.Emit (OpCodes.Brfalse, null_target);
3848 } else if (!is_constant) {
3849 value = new LocalTemporary (SwitchType);
3856 // Setup the codegen context
3858 Label old_end = ec.LoopEnd;
3859 Switch old_switch = ec.Switch;
3861 ec.LoopEnd = ig.DefineLabel ();
3866 if (constant_section != null)
3867 constant_section.Block.Emit (ec);
3868 } else if (string_dictionary != null) {
3869 DoEmitStringSwitch (value, ec);
3871 TableSwitchEmit (ec, value);
3877 // Restore context state.
3878 ig.MarkLabel (ec.LoopEnd);
3881 // Restore the previous context
3883 ec.LoopEnd = old_end;
3884 ec.Switch = old_switch;
3887 protected override void CloneTo (CloneContext clonectx, Statement t)
3889 Switch target = (Switch) t;
3891 target.Expr = Expr.Clone (clonectx);
3892 target.Sections = new ArrayList ();
3893 foreach (SwitchSection ss in Sections){
3894 target.Sections.Add (ss.Clone (clonectx));
3899 // A place where execution can restart in an iterator
3900 public abstract class ResumableStatement : Statement
3903 protected Label resume_point;
3905 public Label PrepareForEmit (EmitContext ec)
3909 resume_point = ec.ig.DefineLabel ();
3911 return resume_point;
3914 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3918 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3923 // Base class for statements that are implemented in terms of try...finally
3924 public abstract class ExceptionStatement : ResumableStatement
3929 protected abstract void EmitPreTryBody (EmitContext ec);
3930 protected abstract void EmitTryBody (EmitContext ec);
3931 protected abstract void EmitFinallyBody (EmitContext ec);
3933 protected sealed override void DoEmit (EmitContext ec)
3935 ILGenerator ig = ec.ig;
3937 EmitPreTryBody (ec);
3939 if (resume_points != null) {
3940 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3941 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3944 ig.BeginExceptionBlock ();
3946 if (resume_points != null) {
3947 ig.MarkLabel (resume_point);
3949 // For normal control flow, we want to fall-through the Switch
3950 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3951 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3952 IntConstant.EmitInt (ig, first_resume_pc);
3953 ig.Emit (OpCodes.Sub);
3955 Label [] labels = new Label [resume_points.Count];
3956 for (int i = 0; i < resume_points.Count; ++i)
3957 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3958 ig.Emit (OpCodes.Switch, labels);
3963 ig.BeginFinallyBlock ();
3965 Label start_finally = ec.ig.DefineLabel ();
3966 if (resume_points != null) {
3967 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3968 ig.Emit (OpCodes.Brfalse_S, start_finally);
3969 ig.Emit (OpCodes.Endfinally);
3972 ig.MarkLabel (start_finally);
3973 EmitFinallyBody (ec);
3975 ig.EndExceptionBlock ();
3978 public void SomeCodeFollows ()
3980 code_follows = true;
3983 public override bool Resolve (BlockContext ec)
3985 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3986 // So, ensure there's some IL code after this statement.
3987 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3988 ec.NeedReturnLabel ();
3990 iter = ec.CurrentIterator;
3994 ArrayList resume_points;
3995 int first_resume_pc;
3996 public void AddResumePoint (ResumableStatement stmt, int pc)
3998 if (resume_points == null) {
3999 resume_points = new ArrayList ();
4000 first_resume_pc = pc;
4003 if (pc != first_resume_pc + resume_points.Count)
4004 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4006 resume_points.Add (stmt);
4009 Label dispose_try_block;
4010 bool prepared_for_dispose, emitted_dispose;
4011 public override Label PrepareForDispose (EmitContext ec, Label end)
4013 if (!prepared_for_dispose) {
4014 prepared_for_dispose = true;
4015 dispose_try_block = ec.ig.DefineLabel ();
4017 return dispose_try_block;
4020 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4022 if (emitted_dispose)
4025 emitted_dispose = true;
4027 ILGenerator ig = ec.ig;
4029 Label end_of_try = ig.DefineLabel ();
4031 // Ensure that the only way we can get into this code is through a dispatcher
4032 if (have_dispatcher)
4033 ig.Emit (OpCodes.Br, end);
4035 ig.BeginExceptionBlock ();
4037 ig.MarkLabel (dispose_try_block);
4039 Label [] labels = null;
4040 for (int i = 0; i < resume_points.Count; ++i) {
4041 ResumableStatement s = (ResumableStatement) resume_points [i];
4042 Label ret = s.PrepareForDispose (ec, end_of_try);
4043 if (ret.Equals (end_of_try) && labels == null)
4045 if (labels == null) {
4046 labels = new Label [resume_points.Count];
4047 for (int j = 0; j < i; ++j)
4048 labels [j] = end_of_try;
4053 if (labels != null) {
4055 for (j = 1; j < labels.Length; ++j)
4056 if (!labels [0].Equals (labels [j]))
4058 bool emit_dispatcher = j < labels.Length;
4060 if (emit_dispatcher) {
4061 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4062 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4063 IntConstant.EmitInt (ig, first_resume_pc);
4064 ig.Emit (OpCodes.Sub);
4065 ig.Emit (OpCodes.Switch, labels);
4066 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4069 foreach (ResumableStatement s in resume_points)
4070 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4073 ig.MarkLabel (end_of_try);
4075 ig.BeginFinallyBlock ();
4077 EmitFinallyBody (ec);
4079 ig.EndExceptionBlock ();
4083 public class Lock : ExceptionStatement {
4085 public Statement Statement;
4086 TemporaryVariable temp;
4088 public Lock (Expression expr, Statement stmt, Location l)
4095 public override bool Resolve (BlockContext ec)
4097 expr = expr.Resolve (ec);
4101 if (!TypeManager.IsReferenceType (expr.Type)){
4102 Report.Error (185, loc,
4103 "`{0}' is not a reference type as required by the lock statement",
4104 TypeManager.CSharpName (expr.Type));
4108 ec.StartFlowBranching (this);
4109 bool ok = Statement.Resolve (ec);
4110 ec.EndFlowBranching ();
4112 ok &= base.Resolve (ec);
4114 // Avoid creating libraries that reference the internal
4117 if (t == TypeManager.null_type)
4118 t = TypeManager.object_type;
4120 temp = new TemporaryVariable (t, loc);
4123 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4124 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4125 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4126 monitor_type, "Enter", loc, TypeManager.object_type);
4127 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4128 monitor_type, "Exit", loc, TypeManager.object_type);
4134 protected override void EmitPreTryBody (EmitContext ec)
4136 ILGenerator ig = ec.ig;
4138 temp.EmitAssign (ec, expr);
4140 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4143 protected override void EmitTryBody (EmitContext ec)
4145 Statement.Emit (ec);
4148 protected override void EmitFinallyBody (EmitContext ec)
4151 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4154 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4156 expr.MutateHoistedGenericType (storey);
4157 temp.MutateHoistedGenericType (storey);
4158 Statement.MutateHoistedGenericType (storey);
4161 protected override void CloneTo (CloneContext clonectx, Statement t)
4163 Lock target = (Lock) t;
4165 target.expr = expr.Clone (clonectx);
4166 target.Statement = Statement.Clone (clonectx);
4170 public class Unchecked : Statement {
4173 public Unchecked (Block b)
4179 public override bool Resolve (BlockContext ec)
4181 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4182 return Block.Resolve (ec);
4185 protected override void DoEmit (EmitContext ec)
4187 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4191 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4193 Block.MutateHoistedGenericType (storey);
4196 protected override void CloneTo (CloneContext clonectx, Statement t)
4198 Unchecked target = (Unchecked) t;
4200 target.Block = clonectx.LookupBlock (Block);
4204 public class Checked : Statement {
4207 public Checked (Block b)
4210 b.Unchecked = false;
4213 public override bool Resolve (BlockContext ec)
4215 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4216 return Block.Resolve (ec);
4219 protected override void DoEmit (EmitContext ec)
4221 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4225 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4227 Block.MutateHoistedGenericType (storey);
4230 protected override void CloneTo (CloneContext clonectx, Statement t)
4232 Checked target = (Checked) t;
4234 target.Block = clonectx.LookupBlock (Block);
4238 public class Unsafe : Statement {
4241 public Unsafe (Block b)
4244 Block.Unsafe = true;
4245 loc = b.StartLocation;
4248 public override bool Resolve (BlockContext ec)
4250 if (ec.CurrentIterator != null)
4251 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4253 using (ec.Set (ResolveContext.Options.UnsafeScope))
4254 return Block.Resolve (ec);
4257 protected override void DoEmit (EmitContext ec)
4262 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4264 Block.MutateHoistedGenericType (storey);
4267 protected override void CloneTo (CloneContext clonectx, Statement t)
4269 Unsafe target = (Unsafe) t;
4271 target.Block = clonectx.LookupBlock (Block);
4278 public class Fixed : Statement {
4280 ArrayList declarators;
4281 Statement statement;
4286 abstract class Emitter
4288 protected LocalInfo vi;
4289 protected Expression converted;
4291 protected Emitter (Expression expr, LocalInfo li)
4297 public abstract void Emit (EmitContext ec);
4298 public abstract void EmitExit (EmitContext ec);
4301 class ExpressionEmitter : Emitter {
4302 public ExpressionEmitter (Expression converted, LocalInfo li) :
4303 base (converted, li)
4307 public override void Emit (EmitContext ec) {
4309 // Store pointer in pinned location
4311 converted.Emit (ec);
4315 public override void EmitExit (EmitContext ec)
4317 ec.ig.Emit (OpCodes.Ldc_I4_0);
4318 ec.ig.Emit (OpCodes.Conv_U);
4323 class StringEmitter : Emitter
4325 LocalInfo pinned_string;
4327 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4330 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4331 pinned_string.Pinned = true;
4334 public StringEmitter Resolve (ResolveContext rc)
4336 pinned_string.Resolve (rc);
4338 if (TypeManager.int_get_offset_to_string_data == null) {
4339 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4340 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4346 public override void Emit (EmitContext ec)
4348 pinned_string.ResolveVariable (ec);
4350 converted.Emit (ec);
4351 pinned_string.EmitAssign (ec);
4353 // TODO: Should use Binary::Add
4354 pinned_string.Emit (ec);
4355 ec.ig.Emit (OpCodes.Conv_I);
4357 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4358 //pe.InstanceExpression = pinned_string;
4359 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4361 ec.ig.Emit (OpCodes.Add);
4365 public override void EmitExit (EmitContext ec)
4367 ec.ig.Emit (OpCodes.Ldnull);
4368 pinned_string.EmitAssign (ec);
4372 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4375 declarators = decls;
4380 public Statement Statement {
4381 get { return statement; }
4384 public override bool Resolve (BlockContext ec)
4387 Expression.UnsafeError (loc);
4391 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4392 if (texpr == null) {
4393 if (type is VarExpr)
4394 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4399 expr_type = texpr.Type;
4401 data = new Emitter [declarators.Count];
4403 if (!expr_type.IsPointer){
4404 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4409 foreach (Pair p in declarators){
4410 LocalInfo vi = (LocalInfo) p.First;
4411 Expression e = (Expression) p.Second;
4413 vi.VariableInfo.SetAssigned (ec);
4414 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4417 // The rules for the possible declarators are pretty wise,
4418 // but the production on the grammar is more concise.
4420 // So we have to enforce these rules here.
4422 // We do not resolve before doing the case 1 test,
4423 // because the grammar is explicit in that the token &
4424 // is present, so we need to test for this particular case.
4428 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4432 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4442 if (e.Type.IsArray){
4443 Type array_type = TypeManager.GetElementType (e.Type);
4446 // Provided that array_type is unmanaged,
4448 if (!TypeManager.VerifyUnManaged (array_type, loc))
4452 // and T* is implicitly convertible to the
4453 // pointer type given in the fixed statement.
4455 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4457 Expression converted = Convert.ImplicitConversionRequired (
4458 ec, array_ptr, vi.VariableType, loc);
4459 if (converted == null)
4463 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4465 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4466 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4467 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4468 new NullPointer (loc),
4471 converted = converted.Resolve (ec);
4473 data [i] = new ExpressionEmitter (converted, vi);
4482 if (e.Type == TypeManager.string_type){
4483 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4488 // Case 4: fixed buffer
4489 if (e is FixedBufferPtr) {
4490 data [i++] = new ExpressionEmitter (e, vi);
4495 // Case 1: & object.
4497 Unary u = e as Unary;
4498 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4499 IVariableReference vr = u.Expr as IVariableReference;
4500 if (vr == null || !vr.IsFixed) {
4501 data [i] = new ExpressionEmitter (e, vi);
4505 if (data [i++] == null)
4506 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4508 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4511 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4512 bool ok = statement.Resolve (ec);
4513 bool flow_unreachable = ec.EndFlowBranching ();
4514 has_ret = flow_unreachable;
4519 protected override void DoEmit (EmitContext ec)
4521 for (int i = 0; i < data.Length; i++) {
4525 statement.Emit (ec);
4531 // Clear the pinned variable
4533 for (int i = 0; i < data.Length; i++) {
4534 data [i].EmitExit (ec);
4538 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4540 // Fixed statement cannot be used inside anonymous methods or lambdas
4541 throw new NotSupportedException ();
4544 protected override void CloneTo (CloneContext clonectx, Statement t)
4546 Fixed target = (Fixed) t;
4548 target.type = type.Clone (clonectx);
4549 target.declarators = new ArrayList (declarators.Count);
4550 foreach (Pair p in declarators) {
4551 LocalInfo vi = (LocalInfo) p.First;
4552 Expression e = (Expression) p.Second;
4554 target.declarators.Add (
4555 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4558 target.statement = statement.Clone (clonectx);
4562 public class Catch : Statement {
4563 public readonly string Name;
4565 public Block VarBlock;
4567 Expression type_expr;
4570 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4575 VarBlock = var_block;
4579 public Type CatchType {
4585 public bool IsGeneral {
4587 return type_expr == null;
4591 protected override void DoEmit (EmitContext ec)
4593 ILGenerator ig = ec.ig;
4595 if (CatchType != null)
4596 ig.BeginCatchBlock (CatchType);
4598 ig.BeginCatchBlock (TypeManager.object_type);
4600 if (VarBlock != null)
4604 // TODO: Move to resolve
4605 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4606 lvr.Resolve (new ResolveContext (ec.MemberContext));
4609 // Only to make verifier happy
4610 if (TypeManager.IsGenericParameter (lvr.Type))
4611 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4615 if (lvr.IsHoisted) {
4616 LocalTemporary lt = new LocalTemporary (lvr.Type);
4620 // Variable is at the top of the stack
4621 source = EmptyExpression.Null;
4624 lvr.EmitAssign (ec, source, false, false);
4626 ig.Emit (OpCodes.Pop);
4631 public override bool Resolve (BlockContext ec)
4633 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4634 if (type_expr != null) {
4635 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4641 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4642 Error (155, "The type caught or thrown must be derived from System.Exception");
4648 if (!Block.Resolve (ec))
4651 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4652 // emit the "unused variable" warnings.
4653 if (VarBlock != null)
4654 return VarBlock.Resolve (ec);
4660 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4663 type = storey.MutateType (type);
4664 if (VarBlock != null)
4665 VarBlock.MutateHoistedGenericType (storey);
4666 Block.MutateHoistedGenericType (storey);
4669 protected override void CloneTo (CloneContext clonectx, Statement t)
4671 Catch target = (Catch) t;
4673 if (type_expr != null)
4674 target.type_expr = type_expr.Clone (clonectx);
4675 if (VarBlock != null)
4676 target.VarBlock = clonectx.LookupBlock (VarBlock);
4677 target.Block = clonectx.LookupBlock (Block);
4681 public class TryFinally : ExceptionStatement {
4685 public TryFinally (Statement stmt, Block fini, Location l)
4692 public override bool Resolve (BlockContext ec)
4696 ec.StartFlowBranching (this);
4698 if (!stmt.Resolve (ec))
4702 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4703 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4704 if (!fini.Resolve (ec))
4708 ec.EndFlowBranching ();
4710 ok &= base.Resolve (ec);
4715 protected override void EmitPreTryBody (EmitContext ec)
4719 protected override void EmitTryBody (EmitContext ec)
4724 protected override void EmitFinallyBody (EmitContext ec)
4729 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4731 stmt.MutateHoistedGenericType (storey);
4732 fini.MutateHoistedGenericType (storey);
4735 protected override void CloneTo (CloneContext clonectx, Statement t)
4737 TryFinally target = (TryFinally) t;
4739 target.stmt = (Statement) stmt.Clone (clonectx);
4741 target.fini = clonectx.LookupBlock (fini);
4745 public class TryCatch : Statement {
4747 public ArrayList Specific;
4748 public Catch General;
4749 bool inside_try_finally, code_follows;
4751 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4754 this.Specific = catch_clauses;
4755 this.General = null;
4756 this.inside_try_finally = inside_try_finally;
4758 for (int i = 0; i < catch_clauses.Count; ++i) {
4759 Catch c = (Catch) catch_clauses [i];
4761 if (i != catch_clauses.Count - 1)
4762 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4764 catch_clauses.RemoveAt (i);
4772 public override bool Resolve (BlockContext ec)
4776 ec.StartFlowBranching (this);
4778 if (!Block.Resolve (ec))
4781 Type[] prev_catches = new Type [Specific.Count];
4783 foreach (Catch c in Specific){
4784 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4786 if (c.Name != null) {
4787 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4789 throw new Exception ();
4791 vi.VariableInfo = null;
4794 if (!c.Resolve (ec)) {
4799 Type resolved_type = c.CatchType;
4800 for (int ii = 0; ii < last_index; ++ii) {
4801 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4802 Report.Error (160, c.loc,
4803 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4804 TypeManager.CSharpName (prev_catches [ii]));
4809 prev_catches [last_index++] = resolved_type;
4812 if (General != null) {
4813 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4814 foreach (Catch c in Specific){
4815 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4816 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'");
4821 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4823 if (!General.Resolve (ec))
4827 ec.EndFlowBranching ();
4829 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4830 // So, ensure there's some IL code after this statement
4831 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4832 ec.NeedReturnLabel ();
4837 public void SomeCodeFollows ()
4839 code_follows = true;
4842 protected override void DoEmit (EmitContext ec)
4844 ILGenerator ig = ec.ig;
4846 if (!inside_try_finally)
4847 ig.BeginExceptionBlock ();
4851 foreach (Catch c in Specific)
4854 if (General != null)
4857 if (!inside_try_finally)
4858 ig.EndExceptionBlock ();
4861 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4863 Block.MutateHoistedGenericType (storey);
4865 if (General != null)
4866 General.MutateHoistedGenericType (storey);
4867 if (Specific != null) {
4868 foreach (Catch c in Specific)
4869 c.MutateHoistedGenericType (storey);
4873 protected override void CloneTo (CloneContext clonectx, Statement t)
4875 TryCatch target = (TryCatch) t;
4877 target.Block = clonectx.LookupBlock (Block);
4878 if (General != null)
4879 target.General = (Catch) General.Clone (clonectx);
4880 if (Specific != null){
4881 target.Specific = new ArrayList ();
4882 foreach (Catch c in Specific)
4883 target.Specific.Add (c.Clone (clonectx));
4888 // FIXME: Why is it almost exact copy of Using ??
4889 public class UsingTemporary : ExceptionStatement {
4890 TemporaryVariable local_copy;
4891 public Statement Statement;
4895 public UsingTemporary (Expression expr, Statement stmt, Location l)
4902 public override bool Resolve (BlockContext ec)
4904 expr = expr.Resolve (ec);
4908 expr_type = expr.Type;
4910 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4911 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4912 Using.Error_IsNotConvertibleToIDisposable (expr);
4917 local_copy = new TemporaryVariable (expr_type, loc);
4918 local_copy.Resolve (ec);
4920 ec.StartFlowBranching (this);
4922 bool ok = Statement.Resolve (ec);
4924 ec.EndFlowBranching ();
4926 ok &= base.Resolve (ec);
4928 if (TypeManager.void_dispose_void == null) {
4929 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4930 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4936 protected override void EmitPreTryBody (EmitContext ec)
4938 local_copy.EmitAssign (ec, expr);
4941 protected override void EmitTryBody (EmitContext ec)
4943 Statement.Emit (ec);
4946 protected override void EmitFinallyBody (EmitContext ec)
4948 ILGenerator ig = ec.ig;
4949 if (!TypeManager.IsStruct (expr_type)) {
4950 Label skip = ig.DefineLabel ();
4951 local_copy.Emit (ec);
4952 ig.Emit (OpCodes.Brfalse, skip);
4953 local_copy.Emit (ec);
4954 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4955 ig.MarkLabel (skip);
4959 Expression ml = Expression.MemberLookup (
4960 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4961 "Dispose", Location.Null);
4963 if (!(ml is MethodGroupExpr)) {
4964 local_copy.Emit (ec);
4965 ig.Emit (OpCodes.Box, expr_type);
4966 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4970 MethodInfo mi = null;
4972 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4973 if (TypeManager.GetParameterData (mk).Count == 0) {
4980 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4984 local_copy.AddressOf (ec, AddressOp.Load);
4985 ig.Emit (OpCodes.Call, mi);
4988 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4990 expr_type = storey.MutateType (expr_type);
4991 local_copy.MutateHoistedGenericType (storey);
4992 Statement.MutateHoistedGenericType (storey);
4995 protected override void CloneTo (CloneContext clonectx, Statement t)
4997 UsingTemporary target = (UsingTemporary) t;
4999 target.expr = expr.Clone (clonectx);
5000 target.Statement = Statement.Clone (clonectx);
5004 public class Using : ExceptionStatement {
5006 public Statement EmbeddedStatement {
5007 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5013 ExpressionStatement assign;
5015 public Using (Expression var, Expression init, Statement stmt, Location l)
5023 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5025 Report.SymbolRelatedToPreviousError (expr.Type);
5026 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5027 expr.GetSignatureForError ());
5030 protected override void EmitPreTryBody (EmitContext ec)
5032 assign.EmitStatement (ec);
5035 protected override void EmitTryBody (EmitContext ec)
5040 protected override void EmitFinallyBody (EmitContext ec)
5042 ILGenerator ig = ec.ig;
5043 Label skip = ig.DefineLabel ();
5045 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5046 if (emit_null_check) {
5048 ig.Emit (OpCodes.Brfalse, skip);
5051 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5053 if (emit_null_check)
5054 ig.MarkLabel (skip);
5057 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5059 assign.MutateHoistedGenericType (storey);
5060 var.MutateHoistedGenericType (storey);
5061 stmt.MutateHoistedGenericType (storey);
5064 public override bool Resolve (BlockContext ec)
5066 if (!ResolveVariable (ec))
5069 ec.StartFlowBranching (this);
5071 bool ok = stmt.Resolve (ec);
5073 ec.EndFlowBranching ();
5075 ok &= base.Resolve (ec);
5077 if (TypeManager.void_dispose_void == null) {
5078 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5079 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5085 bool ResolveVariable (BlockContext ec)
5087 assign = new SimpleAssign (var, init, loc);
5088 assign = assign.ResolveStatement (ec);
5092 if (assign.Type == TypeManager.idisposable_type ||
5093 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5097 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5099 Error_IsNotConvertibleToIDisposable (var);
5103 throw new NotImplementedException ("covariance?");
5106 protected override void CloneTo (CloneContext clonectx, Statement t)
5108 Using target = (Using) t;
5110 target.var = var.Clone (clonectx);
5111 target.init = init.Clone (clonectx);
5112 target.stmt = stmt.Clone (clonectx);
5117 /// Implementation of the foreach C# statement
5119 public class Foreach : Statement {
5121 sealed class ArrayForeach : Statement
5123 class ArrayCounter : TemporaryVariable
5125 StatementExpression increment;
5127 public ArrayCounter (Location loc)
5128 : base (TypeManager.int32_type, loc)
5132 public void ResolveIncrement (BlockContext ec)
5134 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5135 increment.Resolve (ec);
5138 public void EmitIncrement (EmitContext ec)
5140 increment.Emit (ec);
5144 readonly Foreach for_each;
5145 readonly Statement statement;
5148 TemporaryVariable[] lengths;
5149 Expression [] length_exprs;
5150 ArrayCounter[] counter;
5152 TemporaryVariable copy;
5155 public ArrayForeach (Foreach @foreach, int rank)
5157 for_each = @foreach;
5158 statement = for_each.statement;
5161 counter = new ArrayCounter [rank];
5162 length_exprs = new Expression [rank];
5165 // Only use temporary length variables when dealing with
5166 // multi-dimensional arrays
5169 lengths = new TemporaryVariable [rank];
5172 protected override void CloneTo (CloneContext clonectx, Statement target)
5174 throw new NotImplementedException ();
5177 public override bool Resolve (BlockContext ec)
5179 copy = new TemporaryVariable (for_each.expr.Type, loc);
5182 int rank = length_exprs.Length;
5183 Arguments list = new Arguments (rank);
5184 for (int i = 0; i < rank; i++) {
5185 counter [i] = new ArrayCounter (loc);
5186 counter [i].ResolveIncrement (ec);
5189 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5191 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5192 lengths [i].Resolve (ec);
5194 Arguments args = new Arguments (1);
5195 args.Add (new Argument (new IntConstant (i, loc)));
5196 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5199 list.Add (new Argument (counter [i]));
5202 access = new ElementAccess (copy, list).Resolve (ec);
5206 Expression var_type = for_each.type;
5207 VarExpr ve = var_type as VarExpr;
5209 // Infer implicitly typed local variable from foreach array type
5210 var_type = new TypeExpression (access.Type, ve.Location);
5213 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5214 if (var_type == null)
5217 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5223 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5224 ec.CurrentBranching.CreateSibling ();
5226 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5227 if (for_each.variable == null)
5230 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5231 if (!statement.Resolve (ec))
5233 ec.EndFlowBranching ();
5235 // There's no direct control flow from the end of the embedded statement to the end of the loop
5236 ec.CurrentBranching.CurrentUsageVector.Goto ();
5238 ec.EndFlowBranching ();
5243 protected override void DoEmit (EmitContext ec)
5245 ILGenerator ig = ec.ig;
5247 copy.EmitAssign (ec, for_each.expr);
5249 int rank = length_exprs.Length;
5250 Label[] test = new Label [rank];
5251 Label[] loop = new Label [rank];
5253 for (int i = 0; i < rank; i++) {
5254 test [i] = ig.DefineLabel ();
5255 loop [i] = ig.DefineLabel ();
5257 if (lengths != null)
5258 lengths [i].EmitAssign (ec, length_exprs [i]);
5261 IntConstant zero = new IntConstant (0, loc);
5262 for (int i = 0; i < rank; i++) {
5263 counter [i].EmitAssign (ec, zero);
5265 ig.Emit (OpCodes.Br, test [i]);
5266 ig.MarkLabel (loop [i]);
5269 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5271 statement.Emit (ec);
5273 ig.MarkLabel (ec.LoopBegin);
5275 for (int i = rank - 1; i >= 0; i--){
5276 counter [i].EmitIncrement (ec);
5278 ig.MarkLabel (test [i]);
5279 counter [i].Emit (ec);
5281 if (lengths != null)
5282 lengths [i].Emit (ec);
5284 length_exprs [i].Emit (ec);
5286 ig.Emit (OpCodes.Blt, loop [i]);
5289 ig.MarkLabel (ec.LoopEnd);
5292 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5294 for_each.expr.MutateHoistedGenericType (storey);
5296 copy.MutateHoistedGenericType (storey);
5297 conv.MutateHoistedGenericType (storey);
5298 statement.MutateHoistedGenericType (storey);
5300 for (int i = 0; i < counter.Length; i++) {
5301 counter [i].MutateHoistedGenericType (storey);
5302 if (lengths != null)
5303 lengths [i].MutateHoistedGenericType (storey);
5308 sealed class CollectionForeach : Statement
5310 class CollectionForeachStatement : Statement
5313 Expression variable, current, conv;
5314 Statement statement;
5317 public CollectionForeachStatement (Type type, Expression variable,
5318 Expression current, Statement statement,
5322 this.variable = variable;
5323 this.current = current;
5324 this.statement = statement;
5328 protected override void CloneTo (CloneContext clonectx, Statement target)
5330 throw new NotImplementedException ();
5333 public override bool Resolve (BlockContext ec)
5335 current = current.Resolve (ec);
5336 if (current == null)
5339 conv = Convert.ExplicitConversion (ec, current, type, loc);
5343 assign = new SimpleAssign (variable, conv, loc);
5344 if (assign.Resolve (ec) == null)
5347 if (!statement.Resolve (ec))
5353 protected override void DoEmit (EmitContext ec)
5355 assign.EmitStatement (ec);
5356 statement.Emit (ec);
5359 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5361 assign.MutateHoistedGenericType (storey);
5362 statement.MutateHoistedGenericType (storey);
5366 Expression variable, expr;
5367 Statement statement;
5369 TemporaryVariable enumerator;
5374 MethodGroupExpr get_enumerator;
5375 PropertyExpr get_current;
5376 MethodInfo move_next;
5377 Expression var_type;
5378 Type enumerator_type;
5379 bool enumerator_found;
5381 public CollectionForeach (Expression var_type, Expression var,
5382 Expression expr, Statement stmt, Location l)
5384 this.var_type = var_type;
5385 this.variable = var;
5391 protected override void CloneTo (CloneContext clonectx, Statement target)
5393 throw new NotImplementedException ();
5396 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5398 Type return_type = mi.ReturnType;
5401 // Ok, we can access it, now make sure that we can do something
5402 // with this `GetEnumerator'
5405 if (return_type == TypeManager.ienumerator_type ||
5406 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5408 // If it is not an interface, lets try to find the methods ourselves.
5409 // For example, if we have:
5410 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5411 // We can avoid the iface call. This is a runtime perf boost.
5412 // even bigger if we have a ValueType, because we avoid the cost
5415 // We have to make sure that both methods exist for us to take
5416 // this path. If one of the methods does not exist, we will just
5417 // use the interface. Sadly, this complex if statement is the only
5418 // way I could do this without a goto
5421 if (TypeManager.bool_movenext_void == null) {
5422 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5423 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5426 if (TypeManager.ienumerator_getcurrent == null) {
5427 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5428 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5432 // Prefer a generic enumerator over a non-generic one.
5434 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5435 enumerator_type = return_type;
5436 if (!FetchGetCurrent (ec, return_type))
5437 get_current = new PropertyExpr (
5438 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5439 if (!FetchMoveNext (return_type))
5440 move_next = TypeManager.bool_movenext_void;
5444 if (return_type.IsInterface ||
5445 !FetchMoveNext (return_type) ||
5446 !FetchGetCurrent (ec, return_type)) {
5447 enumerator_type = return_type;
5448 move_next = TypeManager.bool_movenext_void;
5449 get_current = new PropertyExpr (
5450 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5455 // Ok, so they dont return an IEnumerable, we will have to
5456 // find if they support the GetEnumerator pattern.
5459 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5460 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",
5461 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5466 enumerator_type = return_type;
5472 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5474 bool FetchMoveNext (Type t)
5476 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5478 BindingFlags.Public | BindingFlags.Instance,
5481 if (move_next_list == null)
5484 foreach (MemberInfo m in move_next_list){
5485 MethodInfo mi = (MethodInfo) m;
5487 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5488 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5498 // Retrieves a `public T get_Current ()' method from the Type `t'
5500 bool FetchGetCurrent (ResolveContext ec, Type t)
5502 PropertyExpr pe = Expression.MemberLookup (
5503 ec.CurrentType, t, "Current", MemberTypes.Property,
5504 Expression.AllBindingFlags, loc) as PropertyExpr;
5512 void Error_Enumerator ()
5514 if (enumerator_found) {
5518 Report.Error (1579, loc,
5519 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5520 TypeManager.CSharpName (expr.Type));
5523 bool IsOverride (MethodInfo m)
5525 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5527 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5529 if (m is MethodBuilder)
5532 MethodInfo base_method = m.GetBaseDefinition ();
5533 return base_method != m;
5536 bool TryType (ResolveContext ec, Type t)
5538 MethodGroupExpr mg = Expression.MemberLookup (
5539 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5540 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5544 MethodInfo result = null;
5545 MethodInfo tmp_move_next = null;
5546 PropertyExpr tmp_get_cur = null;
5547 Type tmp_enumerator_type = enumerator_type;
5548 foreach (MethodInfo mi in mg.Methods) {
5549 if (TypeManager.GetParameterData (mi).Count != 0)
5552 // Check whether GetEnumerator is public
5553 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5556 if (IsOverride (mi))
5559 enumerator_found = true;
5561 if (!GetEnumeratorFilter (ec, mi))
5564 if (result != null) {
5565 if (TypeManager.IsGenericType (result.ReturnType)) {
5566 if (!TypeManager.IsGenericType (mi.ReturnType))
5569 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5570 Report.SymbolRelatedToPreviousError (t);
5571 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5572 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5573 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5577 // Always prefer generics enumerators
5578 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5579 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5580 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5583 Report.SymbolRelatedToPreviousError (result);
5584 Report.SymbolRelatedToPreviousError (mi);
5585 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5586 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5591 tmp_move_next = move_next;
5592 tmp_get_cur = get_current;
5593 tmp_enumerator_type = enumerator_type;
5594 if (mi.DeclaringType == t)
5598 if (result != null) {
5599 move_next = tmp_move_next;
5600 get_current = tmp_get_cur;
5601 enumerator_type = tmp_enumerator_type;
5602 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5603 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5605 if (t != expr.Type) {
5606 expr = Convert.ExplicitConversion (
5609 throw new InternalErrorException ();
5612 get_enumerator.InstanceExpression = expr;
5613 get_enumerator.IsBase = t != expr.Type;
5621 bool ProbeCollectionType (ResolveContext ec, Type t)
5623 int errors = Report.Errors;
5624 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5625 if (TryType (ec, tt))
5630 if (Report.Errors > errors)
5634 // Now try to find the method in the interfaces
5636 Type [] ifaces = TypeManager.GetInterfaces (t);
5637 foreach (Type i in ifaces){
5638 if (TryType (ec, i))
5645 public override bool Resolve (BlockContext ec)
5647 enumerator_type = TypeManager.ienumerator_type;
5649 if (!ProbeCollectionType (ec, expr.Type)) {
5650 Error_Enumerator ();
5654 VarExpr ve = var_type as VarExpr;
5656 // Infer implicitly typed local variable from foreach enumerable type
5657 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5660 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5661 if (var_type == null)
5664 enumerator = new TemporaryVariable (enumerator_type, loc);
5665 enumerator.Resolve (ec);
5667 init = new Invocation (get_enumerator, null);
5668 init = init.Resolve (ec);
5672 Expression move_next_expr;
5674 MemberInfo[] mi = new MemberInfo[] { move_next };
5675 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5676 mg.InstanceExpression = enumerator;
5678 move_next_expr = new Invocation (mg, null);
5681 get_current.InstanceExpression = enumerator;
5683 Statement block = new CollectionForeachStatement (
5684 var_type.Type, variable, get_current, statement, loc);
5686 loop = new While (move_next_expr, block, loc);
5689 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5690 if (implements_idisposable || !enumerator_type.IsSealed) {
5691 wrapper = new DisposableWrapper (this, implements_idisposable);
5693 wrapper = new NonDisposableWrapper (this);
5696 return wrapper.Resolve (ec);
5699 protected override void DoEmit (EmitContext ec)
5704 class NonDisposableWrapper : Statement {
5705 CollectionForeach parent;
5707 internal NonDisposableWrapper (CollectionForeach parent)
5709 this.parent = parent;
5712 protected override void CloneTo (CloneContext clonectx, Statement target)
5714 throw new NotSupportedException ();
5717 public override bool Resolve (BlockContext ec)
5719 return parent.ResolveLoop (ec);
5722 protected override void DoEmit (EmitContext ec)
5724 parent.EmitLoopInit (ec);
5725 parent.EmitLoopBody (ec);
5728 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5730 throw new NotSupportedException ();
5734 sealed class DisposableWrapper : ExceptionStatement
5736 CollectionForeach parent;
5737 bool implements_idisposable;
5739 internal DisposableWrapper (CollectionForeach parent, bool implements)
5741 this.parent = parent;
5742 this.implements_idisposable = implements;
5745 protected override void CloneTo (CloneContext clonectx, Statement target)
5747 throw new NotSupportedException ();
5750 public override bool Resolve (BlockContext ec)
5754 ec.StartFlowBranching (this);
5756 if (!parent.ResolveLoop (ec))
5759 ec.EndFlowBranching ();
5761 ok &= base.Resolve (ec);
5763 if (TypeManager.void_dispose_void == null) {
5764 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5765 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5770 protected override void EmitPreTryBody (EmitContext ec)
5772 parent.EmitLoopInit (ec);
5775 protected override void EmitTryBody (EmitContext ec)
5777 parent.EmitLoopBody (ec);
5780 protected override void EmitFinallyBody (EmitContext ec)
5782 Expression instance = parent.enumerator;
5783 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5784 ILGenerator ig = ec.ig;
5786 parent.enumerator.Emit (ec);
5788 Label call_dispose = ig.DefineLabel ();
5790 if (!implements_idisposable) {
5791 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5792 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5798 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5800 // using 'endfinally' to empty the evaluation stack
5801 ig.Emit (OpCodes.Endfinally);
5802 ig.MarkLabel (call_dispose);
5805 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5808 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5810 throw new NotSupportedException ();
5814 bool ResolveLoop (BlockContext ec)
5816 return loop.Resolve (ec);
5819 void EmitLoopInit (EmitContext ec)
5821 enumerator.EmitAssign (ec, init);
5824 void EmitLoopBody (EmitContext ec)
5829 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5831 enumerator_type = storey.MutateType (enumerator_type);
5832 init.MutateHoistedGenericType (storey);
5833 loop.MutateHoistedGenericType (storey);
5838 Expression variable;
5840 Statement statement;
5842 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5843 Statement stmt, Location l)
5846 this.variable = var;
5852 public Statement Statement {
5853 get { return statement; }
5856 public override bool Resolve (BlockContext ec)
5858 expr = expr.Resolve (ec);
5863 Report.Error (186, loc, "Use of null is not valid in this context");
5867 if (expr.Type == TypeManager.string_type) {
5868 statement = new ArrayForeach (this, 1);
5869 } else if (expr.Type.IsArray) {
5870 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5872 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5873 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5874 expr.ExprClassName);
5878 statement = new CollectionForeach (type, variable, expr, statement, loc);
5881 return statement.Resolve (ec);
5884 protected override void DoEmit (EmitContext ec)
5886 ILGenerator ig = ec.ig;
5888 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5889 ec.LoopBegin = ig.DefineLabel ();
5890 ec.LoopEnd = ig.DefineLabel ();
5892 statement.Emit (ec);
5894 ec.LoopBegin = old_begin;
5895 ec.LoopEnd = old_end;
5898 protected override void CloneTo (CloneContext clonectx, Statement t)
5900 Foreach target = (Foreach) t;
5902 target.type = type.Clone (clonectx);
5903 target.variable = variable.Clone (clonectx);
5904 target.expr = expr.Clone (clonectx);
5905 target.statement = statement.Clone (clonectx);
5908 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5910 statement.MutateHoistedGenericType (storey);