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);
65 public virtual void Emit (EmitContext ec)
72 // This routine must be overrided in derived classes and make copies
73 // of all the data that might be modified if resolved
75 protected abstract void CloneTo (CloneContext clonectx, Statement target);
77 public Statement Clone (CloneContext clonectx)
79 Statement s = (Statement) this.MemberwiseClone ();
80 CloneTo (clonectx, s);
84 public virtual Expression CreateExpressionTree (ResolveContext ec)
86 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
90 public Statement PerformClone ()
92 CloneContext clonectx = new CloneContext ();
94 return Clone (clonectx);
97 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
100 public sealed class EmptyStatement : Statement {
102 private EmptyStatement () {}
104 public static readonly EmptyStatement Value = new EmptyStatement ();
106 public override bool Resolve (BlockContext ec)
111 public override bool ResolveUnreachable (BlockContext ec, bool warn)
116 protected override void DoEmit (EmitContext ec)
120 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
124 protected override void CloneTo (CloneContext clonectx, Statement target)
130 public class If : Statement {
132 public Statement TrueStatement;
133 public Statement FalseStatement;
137 public If (Expression expr, Statement true_statement, Location l)
140 TrueStatement = true_statement;
144 public If (Expression expr,
145 Statement true_statement,
146 Statement false_statement,
150 TrueStatement = true_statement;
151 FalseStatement = false_statement;
155 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
157 expr.MutateHoistedGenericType (storey);
158 TrueStatement.MutateHoistedGenericType (storey);
159 if (FalseStatement != null)
160 FalseStatement.MutateHoistedGenericType (storey);
163 public override bool Resolve (BlockContext ec)
167 Report.Debug (1, "START IF BLOCK", loc);
169 expr = Expression.ResolveBoolean (ec, expr, loc);
175 Assign ass = expr as Assign;
176 if (ass != null && ass.Source is Constant) {
177 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
181 // Dead code elimination
183 if (expr is Constant){
184 bool take = !((Constant) expr).IsDefaultValue;
187 if (!TrueStatement.Resolve (ec))
190 if ((FalseStatement != null) &&
191 !FalseStatement.ResolveUnreachable (ec, true))
193 FalseStatement = null;
195 if (!TrueStatement.ResolveUnreachable (ec, true))
197 TrueStatement = null;
199 if ((FalseStatement != null) &&
200 !FalseStatement.Resolve (ec))
207 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
209 ok &= TrueStatement.Resolve (ec);
211 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
213 ec.CurrentBranching.CreateSibling ();
215 if (FalseStatement != null)
216 ok &= FalseStatement.Resolve (ec);
218 ec.EndFlowBranching ();
220 Report.Debug (1, "END IF BLOCK", loc);
225 protected override void DoEmit (EmitContext ec)
227 ILGenerator ig = ec.ig;
228 Label false_target = ig.DefineLabel ();
232 // If we're a boolean constant, Resolve() already
233 // eliminated dead code for us.
235 Constant c = expr as Constant;
237 c.EmitSideEffect (ec);
239 if (!c.IsDefaultValue)
240 TrueStatement.Emit (ec);
241 else if (FalseStatement != null)
242 FalseStatement.Emit (ec);
247 expr.EmitBranchable (ec, false_target, false);
249 TrueStatement.Emit (ec);
251 if (FalseStatement != null){
252 bool branch_emitted = false;
254 end = ig.DefineLabel ();
256 ig.Emit (OpCodes.Br, end);
257 branch_emitted = true;
260 ig.MarkLabel (false_target);
261 FalseStatement.Emit (ec);
266 ig.MarkLabel (false_target);
270 protected override void CloneTo (CloneContext clonectx, Statement t)
274 target.expr = expr.Clone (clonectx);
275 target.TrueStatement = TrueStatement.Clone (clonectx);
276 if (FalseStatement != null)
277 target.FalseStatement = FalseStatement.Clone (clonectx);
281 public class Do : Statement {
282 public Expression expr;
283 public Statement EmbeddedStatement;
285 public Do (Statement statement, Expression bool_expr, Location l)
288 EmbeddedStatement = statement;
292 public override bool Resolve (BlockContext ec)
296 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
298 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
300 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
301 if (!EmbeddedStatement.Resolve (ec))
303 ec.EndFlowBranching ();
305 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
306 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
308 expr = Expression.ResolveBoolean (ec, expr, loc);
311 else if (expr is Constant){
312 bool infinite = !((Constant) expr).IsDefaultValue;
314 ec.CurrentBranching.CurrentUsageVector.Goto ();
317 ec.EndFlowBranching ();
322 protected override void DoEmit (EmitContext ec)
324 ILGenerator ig = ec.ig;
325 Label loop = ig.DefineLabel ();
326 Label old_begin = ec.LoopBegin;
327 Label old_end = ec.LoopEnd;
329 ec.LoopBegin = ig.DefineLabel ();
330 ec.LoopEnd = ig.DefineLabel ();
333 EmbeddedStatement.Emit (ec);
334 ig.MarkLabel (ec.LoopBegin);
337 // Dead code elimination
339 if (expr is Constant){
340 bool res = !((Constant) expr).IsDefaultValue;
342 expr.EmitSideEffect (ec);
344 ec.ig.Emit (OpCodes.Br, loop);
346 expr.EmitBranchable (ec, loop, true);
348 ig.MarkLabel (ec.LoopEnd);
350 ec.LoopBegin = old_begin;
351 ec.LoopEnd = old_end;
354 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
356 expr.MutateHoistedGenericType (storey);
357 EmbeddedStatement.MutateHoistedGenericType (storey);
360 protected override void CloneTo (CloneContext clonectx, Statement t)
364 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
365 target.expr = expr.Clone (clonectx);
369 public class While : Statement {
370 public Expression expr;
371 public Statement Statement;
372 bool infinite, empty;
374 public While (Expression bool_expr, Statement statement, Location l)
376 this.expr = bool_expr;
377 Statement = statement;
381 public override bool Resolve (BlockContext ec)
385 expr = Expression.ResolveBoolean (ec, expr, loc);
390 // Inform whether we are infinite or not
392 if (expr is Constant){
393 bool value = !((Constant) expr).IsDefaultValue;
396 if (!Statement.ResolveUnreachable (ec, true))
404 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
406 ec.CurrentBranching.CreateSibling ();
408 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
409 if (!Statement.Resolve (ec))
411 ec.EndFlowBranching ();
413 // There's no direct control flow from the end of the embedded statement to the end of the loop
414 ec.CurrentBranching.CurrentUsageVector.Goto ();
416 ec.EndFlowBranching ();
421 protected override void DoEmit (EmitContext ec)
424 expr.EmitSideEffect (ec);
428 ILGenerator ig = ec.ig;
429 Label old_begin = ec.LoopBegin;
430 Label old_end = ec.LoopEnd;
432 ec.LoopBegin = ig.DefineLabel ();
433 ec.LoopEnd = ig.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr is Constant){
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ig.MarkLabel (ec.LoopBegin);
441 expr.EmitSideEffect (ec);
443 ig.Emit (OpCodes.Br, ec.LoopBegin);
446 // Inform that we are infinite (ie, `we return'), only
447 // if we do not `break' inside the code.
449 ig.MarkLabel (ec.LoopEnd);
451 Label while_loop = ig.DefineLabel ();
453 ig.Emit (OpCodes.Br, ec.LoopBegin);
454 ig.MarkLabel (while_loop);
458 ig.MarkLabel (ec.LoopBegin);
461 expr.EmitBranchable (ec, while_loop, true);
463 ig.MarkLabel (ec.LoopEnd);
466 ec.LoopBegin = old_begin;
467 ec.LoopEnd = old_end;
470 public override void Emit (EmitContext ec)
475 protected override void CloneTo (CloneContext clonectx, Statement t)
477 While target = (While) t;
479 target.expr = expr.Clone (clonectx);
480 target.Statement = Statement.Clone (clonectx);
483 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
485 expr.MutateHoistedGenericType (storey);
486 Statement.MutateHoistedGenericType (storey);
490 public class For : Statement {
492 Statement InitStatement;
494 public Statement Statement;
495 bool infinite, empty;
497 public For (Statement init_statement,
503 InitStatement = init_statement;
505 Increment = increment;
506 Statement = statement;
510 public override bool Resolve (BlockContext ec)
514 if (InitStatement != null){
515 if (!InitStatement.Resolve (ec))
520 Test = Expression.ResolveBoolean (ec, Test, loc);
523 else if (Test is Constant){
524 bool value = !((Constant) Test).IsDefaultValue;
527 if (!Statement.ResolveUnreachable (ec, true))
529 if ((Increment != null) &&
530 !Increment.ResolveUnreachable (ec, false))
540 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
542 ec.CurrentBranching.CreateSibling ();
544 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
546 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
547 if (!Statement.Resolve (ec))
549 ec.EndFlowBranching ();
551 if (Increment != null){
552 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
553 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
556 if (!Increment.Resolve (ec))
561 // There's no direct control flow from the end of the embedded statement to the end of the loop
562 ec.CurrentBranching.CurrentUsageVector.Goto ();
564 ec.EndFlowBranching ();
569 protected override void DoEmit (EmitContext ec)
571 if (InitStatement != null && InitStatement != EmptyStatement.Value)
572 InitStatement.Emit (ec);
575 Test.EmitSideEffect (ec);
579 ILGenerator ig = ec.ig;
580 Label old_begin = ec.LoopBegin;
581 Label old_end = ec.LoopEnd;
582 Label loop = ig.DefineLabel ();
583 Label test = ig.DefineLabel ();
585 ec.LoopBegin = ig.DefineLabel ();
586 ec.LoopEnd = ig.DefineLabel ();
588 ig.Emit (OpCodes.Br, test);
592 ig.MarkLabel (ec.LoopBegin);
593 if (Increment != EmptyStatement.Value)
598 // If test is null, there is no test, and we are just
603 // The Resolve code already catches the case for
604 // Test == Constant (false) so we know that
607 if (Test is Constant) {
608 Test.EmitSideEffect (ec);
609 ig.Emit (OpCodes.Br, loop);
611 Test.EmitBranchable (ec, loop, true);
615 ig.Emit (OpCodes.Br, loop);
616 ig.MarkLabel (ec.LoopEnd);
618 ec.LoopBegin = old_begin;
619 ec.LoopEnd = old_end;
622 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
624 if (InitStatement != null)
625 InitStatement.MutateHoistedGenericType (storey);
627 Test.MutateHoistedGenericType (storey);
628 if (Increment != null)
629 Increment.MutateHoistedGenericType (storey);
631 Statement.MutateHoistedGenericType (storey);
634 protected override void CloneTo (CloneContext clonectx, Statement t)
636 For target = (For) t;
638 if (InitStatement != null)
639 target.InitStatement = InitStatement.Clone (clonectx);
641 target.Test = Test.Clone (clonectx);
642 if (Increment != null)
643 target.Increment = Increment.Clone (clonectx);
644 target.Statement = Statement.Clone (clonectx);
648 public class StatementExpression : Statement {
649 ExpressionStatement expr;
651 public StatementExpression (ExpressionStatement expr)
657 public override bool Resolve (BlockContext ec)
659 if (expr != null && expr.eclass == ExprClass.Invalid)
660 expr = expr.ResolveStatement (ec);
664 protected override void DoEmit (EmitContext ec)
666 expr.EmitStatement (ec);
669 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
671 expr.MutateHoistedGenericType (storey);
674 public override string ToString ()
676 return "StatementExpression (" + expr + ")";
679 protected override void CloneTo (CloneContext clonectx, Statement t)
681 StatementExpression target = (StatementExpression) t;
683 target.expr = (ExpressionStatement) expr.Clone (clonectx);
687 // A 'return' or a 'yield break'
688 public abstract class ExitStatement : Statement
690 protected bool unwind_protect;
691 protected abstract bool DoResolve (BlockContext ec);
693 public virtual void Error_FinallyClause ()
695 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
698 public sealed override bool Resolve (BlockContext ec)
703 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
705 ec.NeedReturnLabel ();
706 ec.CurrentBranching.CurrentUsageVector.Goto ();
712 /// Implements the return statement
714 public class Return : ExitStatement {
715 protected Expression Expr;
716 public Return (Expression expr, Location l)
722 protected override bool DoResolve (BlockContext ec)
725 if (ec.ReturnType == TypeManager.void_type)
728 Report.Error (126, loc,
729 "An object of a type convertible to `{0}' is required for the return statement",
730 TypeManager.CSharpName (ec.ReturnType));
734 if (ec.CurrentBlock.Toplevel.IsIterator) {
735 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
736 "statement to return a value, or yield break to end the iteration");
739 AnonymousExpression am = ec.CurrentAnonymousMethod;
740 if (am == null && ec.ReturnType == TypeManager.void_type) {
741 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
742 ec.GetSignatureForError ());
745 Expr = Expr.Resolve (ec);
749 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
750 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
754 if (Expr.Type != ec.ReturnType) {
755 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
759 Report.Error (1662, loc,
760 "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",
761 am.ContainerType, am.GetSignatureForError ());
770 protected override void DoEmit (EmitContext ec)
776 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
780 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
782 ec.ig.Emit (OpCodes.Ret);
785 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
788 Expr.MutateHoistedGenericType (storey);
791 protected override void CloneTo (CloneContext clonectx, Statement t)
793 Return target = (Return) t;
794 // It's null for simple return;
796 target.Expr = Expr.Clone (clonectx);
800 public class Goto : Statement {
802 LabeledStatement label;
805 public override bool Resolve (BlockContext ec)
807 int errors = Report.Errors;
808 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
809 ec.CurrentBranching.CurrentUsageVector.Goto ();
810 return errors == Report.Errors;
813 public Goto (string label, Location l)
819 public string Target {
820 get { return target; }
823 public void SetResolvedTarget (LabeledStatement label)
826 label.AddReference ();
829 protected override void CloneTo (CloneContext clonectx, Statement target)
834 protected override void DoEmit (EmitContext ec)
837 throw new InternalErrorException ("goto emitted before target resolved");
838 Label l = label.LabelTarget (ec);
839 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
842 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
847 public class LabeledStatement : Statement {
854 FlowBranching.UsageVector vectors;
856 public LabeledStatement (string name, Location l)
862 public Label LabelTarget (EmitContext ec)
867 label = ec.ig.DefineLabel ();
877 public bool IsDefined {
878 get { return defined; }
881 public bool HasBeenReferenced {
882 get { return referenced; }
885 public FlowBranching.UsageVector JumpOrigins {
886 get { return vectors; }
889 public void AddUsageVector (FlowBranching.UsageVector vector)
891 vector = vector.Clone ();
892 vector.Next = vectors;
896 protected override void CloneTo (CloneContext clonectx, Statement target)
901 public override bool Resolve (BlockContext ec)
903 // this flow-branching will be terminated when the surrounding block ends
904 ec.StartFlowBranching (this);
908 protected override void DoEmit (EmitContext ec)
910 if (ig != null && ig != ec.ig)
911 throw new InternalErrorException ("cannot happen");
913 ec.ig.MarkLabel (label);
916 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
920 public void AddReference ()
928 /// `goto default' statement
930 public class GotoDefault : Statement {
932 public GotoDefault (Location l)
937 protected override void CloneTo (CloneContext clonectx, Statement target)
942 public override bool Resolve (BlockContext ec)
944 ec.CurrentBranching.CurrentUsageVector.Goto ();
948 protected override void DoEmit (EmitContext ec)
950 if (ec.Switch == null){
951 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
955 if (!ec.Switch.GotDefault){
956 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
959 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
962 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
968 /// `goto case' statement
970 public class GotoCase : Statement {
974 public GotoCase (Expression e, Location l)
980 public override bool Resolve (BlockContext ec)
982 if (ec.Switch == null){
983 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
987 ec.CurrentBranching.CurrentUsageVector.Goto ();
989 expr = expr.Resolve (ec);
993 Constant c = expr as Constant;
995 Report.Error (150, expr.Location, "A constant value is expected");
999 Type type = ec.Switch.SwitchType;
1000 Constant res = c.TryReduce (ec, type, c.Location);
1002 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1006 if (!Convert.ImplicitStandardConversionExists (c, type))
1007 Report.Warning (469, 2, loc,
1008 "The `goto case' value is not implicitly convertible to type `{0}'",
1009 TypeManager.CSharpName (type));
1011 object val = res.GetValue ();
1013 val = SwitchLabel.NullStringCase;
1015 sl = (SwitchLabel) ec.Switch.Elements [val];
1018 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1019 (c.GetValue () == null ? "null" : val.ToString ()));
1026 protected override void DoEmit (EmitContext ec)
1028 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1031 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1033 expr.MutateHoistedGenericType (storey);
1036 protected override void CloneTo (CloneContext clonectx, Statement t)
1038 GotoCase target = (GotoCase) t;
1040 target.expr = expr.Clone (clonectx);
1044 public class Throw : Statement {
1047 public Throw (Expression expr, Location l)
1053 public override bool Resolve (BlockContext ec)
1056 ec.CurrentBranching.CurrentUsageVector.Goto ();
1057 return ec.CurrentBranching.CheckRethrow (loc);
1060 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1061 ec.CurrentBranching.CurrentUsageVector.Goto ();
1066 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1067 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1069 Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1074 protected override void DoEmit (EmitContext ec)
1077 ec.ig.Emit (OpCodes.Rethrow);
1081 ec.ig.Emit (OpCodes.Throw);
1085 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1088 expr.MutateHoistedGenericType (storey);
1091 protected override void CloneTo (CloneContext clonectx, Statement t)
1093 Throw target = (Throw) t;
1096 target.expr = expr.Clone (clonectx);
1100 public class Break : Statement {
1102 public Break (Location l)
1107 bool unwind_protect;
1109 public override bool Resolve (BlockContext ec)
1111 int errors = Report.Errors;
1112 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1113 ec.CurrentBranching.CurrentUsageVector.Goto ();
1114 return errors == Report.Errors;
1117 protected override void DoEmit (EmitContext ec)
1119 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1122 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1126 protected override void CloneTo (CloneContext clonectx, Statement t)
1132 public class Continue : Statement {
1134 public Continue (Location l)
1139 bool unwind_protect;
1141 public override bool Resolve (BlockContext ec)
1143 int errors = Report.Errors;
1144 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1145 ec.CurrentBranching.CurrentUsageVector.Goto ();
1146 return errors == Report.Errors;
1149 protected override void DoEmit (EmitContext ec)
1151 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1154 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1158 protected override void CloneTo (CloneContext clonectx, Statement t)
1164 public interface ILocalVariable
1166 void Emit (EmitContext ec);
1167 void EmitAssign (EmitContext ec);
1168 void EmitAddressOf (EmitContext ec);
1171 public interface IKnownVariable {
1172 Block Block { get; }
1173 Location Location { get; }
1177 // The information about a user-perceived local variable
1179 public class LocalInfo : IKnownVariable, ILocalVariable {
1180 public readonly FullNamedExpression Type;
1182 public Type VariableType;
1183 public readonly string Name;
1184 public readonly Location Location;
1185 public readonly Block Block;
1187 public VariableInfo VariableInfo;
1188 public HoistedVariable HoistedVariableReference;
1197 CompilerGenerated = 64,
1201 public enum ReadOnlyContext: byte {
1208 ReadOnlyContext ro_context;
1209 LocalBuilder builder;
1211 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1219 public LocalInfo (DeclSpace ds, Block block, Location l)
1221 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1226 public void ResolveVariable (EmitContext ec)
1228 if (HoistedVariableReference != null)
1231 if (builder == null) {
1234 // This is needed to compile on both .NET 1.x and .NET 2.x
1235 // the later introduced `DeclareLocal (Type t, bool pinned)'
1237 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1239 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1243 public void Emit (EmitContext ec)
1245 ec.ig.Emit (OpCodes.Ldloc, builder);
1248 public void EmitAssign (EmitContext ec)
1250 ec.ig.Emit (OpCodes.Stloc, builder);
1253 public void EmitAddressOf (EmitContext ec)
1255 ec.ig.Emit (OpCodes.Ldloca, builder);
1258 public void EmitSymbolInfo (EmitContext ec)
1260 if (builder != null)
1261 ec.DefineLocalVariable (Name, builder);
1264 public bool IsThisAssigned (BlockContext ec, Block block)
1266 if (VariableInfo == null)
1267 throw new Exception ();
1269 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1272 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, block.StartLocation);
1275 public bool IsAssigned (BlockContext ec)
1277 if (VariableInfo == null)
1278 throw new Exception ();
1280 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1283 public bool Resolve (ResolveContext ec)
1285 if (VariableType != null)
1288 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1292 VariableType = texpr.Type;
1294 if (TypeManager.IsGenericParameter (VariableType))
1297 if (VariableType.IsAbstract && VariableType.IsSealed) {
1298 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1302 if (VariableType.IsPointer && !ec.IsUnsafe)
1303 Expression.UnsafeError (Location);
1308 public bool IsConstant {
1309 get { return (flags & Flags.IsConstant) != 0; }
1310 set { flags |= Flags.IsConstant; }
1313 public bool AddressTaken {
1314 get { return (flags & Flags.AddressTaken) != 0; }
1315 set { flags |= Flags.AddressTaken; }
1318 public bool CompilerGenerated {
1319 get { return (flags & Flags.CompilerGenerated) != 0; }
1320 set { flags |= Flags.CompilerGenerated; }
1323 public override string ToString ()
1325 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1326 Name, Type, VariableInfo, Location);
1330 get { return (flags & Flags.Used) != 0; }
1331 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1334 public bool ReadOnly {
1335 get { return (flags & Flags.ReadOnly) != 0; }
1338 public void SetReadOnlyContext (ReadOnlyContext context)
1340 flags |= Flags.ReadOnly;
1341 ro_context = context;
1344 public string GetReadOnlyContext ()
1347 throw new InternalErrorException ("Variable is not readonly");
1349 switch (ro_context) {
1350 case ReadOnlyContext.Fixed:
1351 return "fixed variable";
1352 case ReadOnlyContext.Foreach:
1353 return "foreach iteration variable";
1354 case ReadOnlyContext.Using:
1355 return "using variable";
1357 throw new NotImplementedException ();
1361 // Whether the variable is pinned, if Pinned the variable has been
1362 // allocated in a pinned slot with DeclareLocal.
1364 public bool Pinned {
1365 get { return (flags & Flags.Pinned) != 0; }
1366 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1369 public bool IsThis {
1370 get { return (flags & Flags.IsThis) != 0; }
1371 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1374 Block IKnownVariable.Block {
1375 get { return Block; }
1378 Location IKnownVariable.Location {
1379 get { return Location; }
1382 public LocalInfo Clone (CloneContext clonectx)
1385 // Variables in anonymous block are not resolved yet
1387 if (VariableType == null)
1388 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1391 // Variables in method block are resolved
1393 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1394 li.VariableType = VariableType;
1400 /// Block represents a C# block.
1404 /// This class is used in a number of places: either to represent
1405 /// explicit blocks that the programmer places or implicit blocks.
1407 /// Implicit blocks are used as labels or to introduce variable
1410 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1411 /// they contain extra information that is not necessary on normal blocks.
1413 public class Block : Statement {
1414 public Block Parent;
1415 public Location StartLocation;
1416 public Location EndLocation = Location.Null;
1418 public ExplicitBlock Explicit;
1419 public ToplevelBlock Toplevel; // TODO: Use Explicit
1422 public enum Flags : byte {
1425 VariablesInitialized = 4,
1429 HasCapturedVariable = 64,
1430 HasCapturedThis = 128
1432 protected Flags flags;
1434 public bool Unchecked {
1435 get { return (flags & Flags.Unchecked) != 0; }
1436 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1439 public bool Unsafe {
1440 get { return (flags & Flags.Unsafe) != 0; }
1441 set { flags |= Flags.Unsafe; }
1445 // The statements in this block
1447 protected ArrayList statements;
1450 // An array of Blocks. We keep track of children just
1451 // to generate the local variable declarations.
1453 // Statements and child statements are handled through the
1459 // Labels. (label, block) pairs.
1461 protected HybridDictionary labels;
1464 // Keeps track of (name, type) pairs
1466 IDictionary variables;
1469 // Keeps track of constants
1470 HybridDictionary constants;
1473 // Temporary variables.
1475 ArrayList temporary_variables;
1478 // If this is a switch section, the enclosing switch block.
1482 protected ArrayList scope_initializers;
1484 ArrayList anonymous_children;
1486 protected static int id;
1490 int assignable_slots;
1491 bool unreachable_shown;
1494 public Block (Block parent)
1495 : this (parent, (Flags) 0, Location.Null, Location.Null)
1498 public Block (Block parent, Flags flags)
1499 : this (parent, flags, Location.Null, Location.Null)
1502 public Block (Block parent, Location start, Location end)
1503 : this (parent, (Flags) 0, start, end)
1507 // Useful when TopLevel block is downgraded to normal block
1509 public Block (ToplevelBlock parent, ToplevelBlock source)
1510 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1512 statements = source.statements;
1513 children = source.children;
1514 labels = source.labels;
1515 variables = source.variables;
1516 constants = source.constants;
1517 switch_block = source.switch_block;
1520 public Block (Block parent, Flags flags, Location start, Location end)
1522 if (parent != null) {
1523 parent.AddChild (this);
1525 // the appropriate constructors will fixup these fields
1526 Toplevel = parent.Toplevel;
1527 Explicit = parent.Explicit;
1530 this.Parent = parent;
1532 this.StartLocation = start;
1533 this.EndLocation = end;
1536 statements = new ArrayList (4);
1539 public Block CreateSwitchBlock (Location start)
1541 // FIXME: should this be implicit?
1542 Block new_block = new ExplicitBlock (this, start, start);
1543 new_block.switch_block = this;
1548 get { return this_id; }
1551 public IDictionary Variables {
1553 if (variables == null)
1554 variables = new ListDictionary ();
1559 void AddChild (Block b)
1561 if (children == null)
1562 children = new ArrayList (1);
1567 public void SetEndLocation (Location loc)
1572 protected static void Error_158 (string name, Location loc)
1574 Report.Error (158, loc, "The label `{0}' shadows another label " +
1575 "by the same name in a contained scope", name);
1579 /// Adds a label to the current block.
1583 /// false if the name already exists in this block. true
1587 public bool AddLabel (LabeledStatement target)
1589 if (switch_block != null)
1590 return switch_block.AddLabel (target);
1592 string name = target.Name;
1595 while (cur != null) {
1596 LabeledStatement s = cur.DoLookupLabel (name);
1598 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1599 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1603 if (this == Explicit)
1609 while (cur != null) {
1610 if (cur.DoLookupLabel (name) != null) {
1611 Error_158 (name, target.loc);
1615 if (children != null) {
1616 foreach (Block b in children) {
1617 LabeledStatement s = b.DoLookupLabel (name);
1621 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1622 Error_158 (name, target.loc);
1630 Toplevel.CheckError158 (name, target.loc);
1633 labels = new HybridDictionary();
1635 labels.Add (name, target);
1639 public LabeledStatement LookupLabel (string name)
1641 LabeledStatement s = DoLookupLabel (name);
1645 if (children == null)
1648 foreach (Block child in children) {
1649 if (Explicit != child.Explicit)
1652 s = child.LookupLabel (name);
1660 LabeledStatement DoLookupLabel (string name)
1662 if (switch_block != null)
1663 return switch_block.LookupLabel (name);
1666 if (labels.Contains (name))
1667 return ((LabeledStatement) labels [name]);
1672 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1675 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1676 while (kvi == null) {
1677 b = b.Explicit.Parent;
1680 kvi = b.Explicit.GetKnownVariable (name);
1686 // Is kvi.Block nested inside 'b'
1687 if (b.Explicit != kvi.Block.Explicit) {
1689 // If a variable by the same name it defined in a nested block of this
1690 // block, we violate the invariant meaning in a block.
1693 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1694 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1699 // It's ok if the definition is in a nested subblock of b, but not
1700 // nested inside this block -- a definition in a sibling block
1701 // should not affect us.
1707 // Block 'b' and kvi.Block are the same textual block.
1708 // However, different variables are extant.
1710 // Check if the variable is in scope in both blocks. We use
1711 // an indirect check that depends on AddVariable doing its
1712 // part in maintaining the invariant-meaning-in-block property.
1714 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1717 if (this is ToplevelBlock) {
1718 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1719 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1724 // Even though we detected the error when the name is used, we
1725 // treat it as if the variable declaration was in error.
1727 Report.SymbolRelatedToPreviousError (loc, name);
1728 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1732 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1734 LocalInfo vi = GetLocalInfo (name);
1736 Report.SymbolRelatedToPreviousError (vi.Location, name);
1737 if (Explicit == vi.Block.Explicit) {
1738 Error_AlreadyDeclared (l, name, null);
1740 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1741 "parent or current" : "parent");
1746 if (block != null) {
1747 Expression e = block.GetParameterReference (name, Location.Null);
1749 ParameterReference pr = e as ParameterReference;
1750 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1751 Error_AlreadyDeclared (loc, name);
1753 Error_AlreadyDeclared (loc, name, "parent or current");
1761 public LocalInfo AddVariable (Expression type, string name, Location l)
1763 if (!CheckParentConflictName (Toplevel, name, l))
1766 if (Toplevel.GenericMethod != null) {
1767 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1768 if (tp.Name == name) {
1769 Report.SymbolRelatedToPreviousError (tp);
1770 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1776 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1778 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1779 Error_AlreadyDeclared (l, name, "child");
1783 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1786 if ((flags & Flags.VariablesInitialized) != 0)
1787 throw new InternalErrorException ("block has already been resolved");
1792 protected virtual void AddVariable (LocalInfo li)
1794 Variables.Add (li.Name, li);
1795 Explicit.AddKnownVariable (li.Name, li);
1798 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1800 if (reason == null) {
1801 Error_AlreadyDeclared (loc, var);
1805 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1806 "in this scope because it would give a different meaning " +
1807 "to `{0}', which is already used in a `{1}' scope " +
1808 "to denote something else", var, reason);
1811 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1813 Report.Error (128, loc,
1814 "A local variable named `{0}' is already defined in this scope", name);
1817 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1819 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1823 public bool AddConstant (Expression type, string name, Expression value, Location l)
1825 if (AddVariable (type, name, l) == null)
1828 if (constants == null)
1829 constants = new HybridDictionary();
1831 constants.Add (name, value);
1833 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1838 static int next_temp_id = 0;
1840 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1842 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1844 if (temporary_variables == null)
1845 temporary_variables = new ArrayList ();
1847 int id = ++next_temp_id;
1848 string name = "$s_" + id.ToString ();
1850 LocalInfo li = new LocalInfo (te, name, this, loc);
1851 li.CompilerGenerated = true;
1852 temporary_variables.Add (li);
1856 public LocalInfo GetLocalInfo (string name)
1859 for (Block b = this; b != null; b = b.Parent) {
1860 if (b.variables != null) {
1861 ret = (LocalInfo) b.variables [name];
1870 public Expression GetVariableType (string name)
1872 LocalInfo vi = GetLocalInfo (name);
1873 return vi == null ? null : vi.Type;
1876 public Expression GetConstantExpression (string name)
1878 for (Block b = this; b != null; b = b.Parent) {
1879 if (b.constants != null) {
1880 Expression ret = b.constants [name] as Expression;
1889 // It should be used by expressions which require to
1890 // register a statement during resolve process.
1892 public void AddScopeStatement (Statement s)
1894 if (scope_initializers == null)
1895 scope_initializers = new ArrayList ();
1897 scope_initializers.Add (s);
1900 public void AddStatement (Statement s)
1903 flags |= Flags.BlockUsed;
1907 get { return (flags & Flags.BlockUsed) != 0; }
1912 flags |= Flags.BlockUsed;
1915 public bool HasRet {
1916 get { return (flags & Flags.HasRet) != 0; }
1919 public int AssignableSlots {
1922 // if ((flags & Flags.VariablesInitialized) == 0)
1923 // throw new Exception ("Variables have not been initialized yet");
1924 return assignable_slots;
1928 public ArrayList AnonymousChildren {
1929 get { return anonymous_children; }
1932 public void AddAnonymousChild (ToplevelBlock b)
1934 if (anonymous_children == null)
1935 anonymous_children = new ArrayList ();
1937 anonymous_children.Add (b);
1940 void DoResolveConstants (BlockContext ec)
1942 if (constants == null)
1945 if (variables == null)
1946 throw new InternalErrorException ("cannot happen");
1948 foreach (DictionaryEntry de in variables) {
1949 string name = (string) de.Key;
1950 LocalInfo vi = (LocalInfo) de.Value;
1951 Type variable_type = vi.VariableType;
1953 if (variable_type == null) {
1954 if (vi.Type is VarExpr)
1955 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1960 Expression cv = (Expression) constants [name];
1964 // Don't let 'const int Foo = Foo;' succeed.
1965 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1966 // which in turn causes the 'must be constant' error to be triggered.
1967 constants.Remove (name);
1969 if (!Const.IsConstantTypeValid (variable_type)) {
1970 Const.Error_InvalidConstantType (variable_type, loc);
1974 ec.CurrentBlock = this;
1976 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1977 e = cv.Resolve (ec);
1982 Constant ce = e as Constant;
1984 Const.Error_ExpressionMustBeConstant (vi.Location, name);
1988 e = ce.ConvertImplicitly (variable_type);
1990 if (TypeManager.IsReferenceType (variable_type))
1991 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
1993 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1997 constants.Add (name, e);
1998 vi.IsConstant = true;
2002 protected void ResolveMeta (BlockContext ec, int offset)
2004 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2006 // If some parent block was unsafe, we remain unsafe even if this block
2007 // isn't explicitly marked as such.
2008 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2009 flags |= Flags.VariablesInitialized;
2011 if (variables != null) {
2012 foreach (LocalInfo li in variables.Values) {
2013 if (!li.Resolve (ec))
2015 li.VariableInfo = new VariableInfo (li, offset);
2016 offset += li.VariableInfo.Length;
2019 assignable_slots = offset;
2021 DoResolveConstants (ec);
2023 if (children == null)
2025 foreach (Block b in children)
2026 b.ResolveMeta (ec, offset);
2031 // Emits the local variable declarations for a block
2033 public virtual void EmitMeta (EmitContext ec)
2035 if (variables != null){
2036 foreach (LocalInfo vi in variables.Values)
2037 vi.ResolveVariable (ec);
2040 if (temporary_variables != null) {
2041 for (int i = 0; i < temporary_variables.Count; i++)
2042 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2045 if (children != null) {
2046 for (int i = 0; i < children.Count; i++)
2047 ((Block)children[i]).EmitMeta(ec);
2051 void UsageWarning ()
2053 if (variables == null || Report.WarningLevel < 3)
2056 foreach (DictionaryEntry de in variables) {
2057 LocalInfo vi = (LocalInfo) de.Value;
2060 string name = (string) de.Key;
2062 // vi.VariableInfo can be null for 'catch' variables
2063 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2064 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2066 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2071 static void CheckPossibleMistakenEmptyStatement (Statement s)
2075 // Some statements are wrapped by a Block. Since
2076 // others' internal could be changed, here I treat
2077 // them as possibly wrapped by Block equally.
2078 Block b = s as Block;
2079 if (b != null && b.statements.Count == 1)
2080 s = (Statement) b.statements [0];
2083 body = ((Lock) s).Statement;
2085 body = ((For) s).Statement;
2086 else if (s is Foreach)
2087 body = ((Foreach) s).Statement;
2088 else if (s is While)
2089 body = ((While) s).Statement;
2090 else if (s is Fixed)
2091 body = ((Fixed) s).Statement;
2092 else if (s is Using)
2093 body = ((Using) s).EmbeddedStatement;
2094 else if (s is UsingTemporary)
2095 body = ((UsingTemporary) s).Statement;
2099 if (body == null || body is EmptyStatement)
2100 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2103 public override bool Resolve (BlockContext ec)
2105 Block prev_block = ec.CurrentBlock;
2108 int errors = Report.Errors;
2110 ec.CurrentBlock = this;
2111 ec.StartFlowBranching (this);
2113 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2116 // Compiler generated scope statements
2118 if (scope_initializers != null) {
2119 foreach (Statement s in scope_initializers)
2124 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2125 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2126 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2127 // responsible for handling the situation.
2129 int statement_count = statements.Count;
2130 for (int ix = 0; ix < statement_count; ix++){
2131 Statement s = (Statement) statements [ix];
2132 // Check possible empty statement (CS0642)
2133 if (Report.WarningLevel >= 3 &&
2134 ix + 1 < statement_count &&
2135 statements [ix + 1] is ExplicitBlock)
2136 CheckPossibleMistakenEmptyStatement (s);
2139 // Warn if we detect unreachable code.
2142 if (s is EmptyStatement)
2145 if (!unreachable_shown && !(s is LabeledStatement)) {
2146 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2147 unreachable_shown = true;
2150 Block c_block = s as Block;
2151 if (c_block != null)
2152 c_block.unreachable = c_block.unreachable_shown = true;
2156 // Note that we're not using ResolveUnreachable() for unreachable
2157 // statements here. ResolveUnreachable() creates a temporary
2158 // flow branching and kills it afterwards. This leads to problems
2159 // if you have two unreachable statements where the first one
2160 // assigns a variable and the second one tries to access it.
2163 if (!s.Resolve (ec)) {
2165 if (ec.IsInProbingMode)
2168 statements [ix] = EmptyStatement.Value;
2172 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2173 statements [ix] = EmptyStatement.Value;
2175 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2176 if (unreachable && s is LabeledStatement)
2177 throw new InternalErrorException ("should not happen");
2180 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2181 ec.CurrentBranching, statement_count);
2183 while (ec.CurrentBranching is FlowBranchingLabeled)
2184 ec.EndFlowBranching ();
2186 bool flow_unreachable = ec.EndFlowBranching ();
2188 ec.CurrentBlock = prev_block;
2190 if (flow_unreachable)
2191 flags |= Flags.HasRet;
2193 // If we're a non-static `struct' constructor which doesn't have an
2194 // initializer, then we must initialize all of the struct's fields.
2195 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2198 if ((labels != null) && (Report.WarningLevel >= 2)) {
2199 foreach (LabeledStatement label in labels.Values)
2200 if (!label.HasBeenReferenced)
2201 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2204 if (ok && errors == Report.Errors)
2210 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2212 unreachable_shown = true;
2216 Report.Warning (162, 2, loc, "Unreachable code detected");
2218 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2219 bool ok = Resolve (ec);
2220 ec.KillFlowBranching ();
2225 protected override void DoEmit (EmitContext ec)
2227 for (int ix = 0; ix < statements.Count; ix++){
2228 Statement s = (Statement) statements [ix];
2233 public override void Emit (EmitContext ec)
2235 if (scope_initializers != null)
2236 EmitScopeInitializers (ec);
2238 ec.Mark (StartLocation);
2241 if (SymbolWriter.HasSymbolWriter)
2242 EmitSymbolInfo (ec);
2245 protected void EmitScopeInitializers (EmitContext ec)
2247 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2249 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2250 foreach (Statement s in scope_initializers)
2254 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2257 protected virtual void EmitSymbolInfo (EmitContext ec)
2259 if (variables != null) {
2260 foreach (LocalInfo vi in variables.Values) {
2261 vi.EmitSymbolInfo (ec);
2266 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2268 MutateVariables (storey);
2270 if (scope_initializers != null) {
2271 foreach (Statement s in scope_initializers)
2272 s.MutateHoistedGenericType (storey);
2275 foreach (Statement s in statements)
2276 s.MutateHoistedGenericType (storey);
2279 void MutateVariables (AnonymousMethodStorey storey)
2281 if (variables != null) {
2282 foreach (LocalInfo vi in variables.Values) {
2283 vi.VariableType = storey.MutateType (vi.VariableType);
2287 if (temporary_variables != null) {
2288 foreach (LocalInfo vi in temporary_variables)
2289 vi.VariableType = storey.MutateType (vi.VariableType);
2293 public override string ToString ()
2295 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2298 protected override void CloneTo (CloneContext clonectx, Statement t)
2300 Block target = (Block) t;
2302 clonectx.AddBlockMap (this, target);
2304 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2305 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2307 target.Parent = clonectx.RemapBlockCopy (Parent);
2309 if (variables != null){
2310 target.variables = new Hashtable ();
2312 foreach (DictionaryEntry de in variables){
2313 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2314 target.variables [de.Key] = newlocal;
2315 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2319 target.statements = new ArrayList (statements.Count);
2320 foreach (Statement s in statements)
2321 target.statements.Add (s.Clone (clonectx));
2323 if (target.children != null){
2324 target.children = new ArrayList (children.Count);
2325 foreach (Block b in children){
2326 target.children.Add (clonectx.LookupBlock (b));
2331 // TODO: labels, switch_block, constants (?), anonymous_children
2336 public class ExplicitBlock : Block {
2337 HybridDictionary known_variables;
2338 protected AnonymousMethodStorey am_storey;
2340 public ExplicitBlock (Block parent, Location start, Location end)
2341 : this (parent, (Flags) 0, start, end)
2345 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2346 : base (parent, flags, start, end)
2348 this.Explicit = this;
2352 // Marks a variable with name @name as being used in this or a child block.
2353 // If a variable name has been used in a child block, it's illegal to
2354 // declare a variable with the same name in the current block.
2356 internal void AddKnownVariable (string name, IKnownVariable info)
2358 if (known_variables == null)
2359 known_variables = new HybridDictionary();
2361 known_variables [name] = info;
2364 Parent.Explicit.AddKnownVariable (name, info);
2367 public AnonymousMethodStorey AnonymousMethodStorey {
2368 get { return am_storey; }
2372 // Creates anonymous method storey in current block
2374 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2377 // When referencing a variable in iterator storey from children anonymous method
2379 if (Toplevel.am_storey is IteratorStorey) {
2380 return Toplevel.am_storey;
2384 // An iterator has only 1 storey block
2386 if (ec.CurrentIterator != null)
2387 return ec.CurrentIterator.Storey;
2389 if (am_storey == null) {
2390 MemberBase mc = ec.MemberContext as MemberBase;
2391 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2394 // Creates anonymous method storey for this block
2396 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2402 public override void Emit (EmitContext ec)
2404 if (am_storey != null)
2405 am_storey.EmitStoreyInstantiation (ec);
2407 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2408 if (emit_debug_info)
2413 if (emit_debug_info)
2417 public override void EmitMeta (EmitContext ec)
2420 // Creates anonymous method storey
2422 if (am_storey != null) {
2423 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2425 // Creates parent storey reference when hoisted this is accessible
2427 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2428 ExplicitBlock parent = Toplevel.Parent.Explicit;
2431 // Hoisted this exists in top-level parent storey only
2433 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2434 parent = parent.Parent.Explicit;
2436 am_storey.AddParentStoreyReference (parent.am_storey);
2439 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2442 am_storey.DefineType ();
2443 am_storey.ResolveType ();
2444 am_storey.Define ();
2445 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2447 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2448 if (ref_blocks != null) {
2449 foreach (ExplicitBlock ref_block in ref_blocks) {
2450 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2451 if (b.am_storey != null) {
2452 b.am_storey.AddParentStoreyReference (am_storey);
2454 // Stop propagation inside same top block
2455 if (b.Toplevel == Toplevel)
2460 b.HasCapturedVariable = true;
2469 internal IKnownVariable GetKnownVariable (string name)
2471 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2474 public bool HasCapturedThis
2476 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2477 get { return (flags & Flags.HasCapturedThis) != 0; }
2480 public bool HasCapturedVariable
2482 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2483 get { return (flags & Flags.HasCapturedVariable) != 0; }
2486 protected override void CloneTo (CloneContext clonectx, Statement t)
2488 ExplicitBlock target = (ExplicitBlock) t;
2489 target.known_variables = null;
2490 base.CloneTo (clonectx, t);
2494 public class ToplevelParameterInfo : IKnownVariable {
2495 public readonly ToplevelBlock Block;
2496 public readonly int Index;
2497 public VariableInfo VariableInfo;
2499 Block IKnownVariable.Block {
2500 get { return Block; }
2502 public Parameter Parameter {
2503 get { return Block.Parameters [Index]; }
2506 public Type ParameterType {
2507 get { return Block.Parameters.Types [Index]; }
2510 public Location Location {
2511 get { return Parameter.Location; }
2514 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2522 // A toplevel block contains extra information, the split is done
2523 // only to separate information that would otherwise bloat the more
2524 // lightweight Block.
2526 // In particular, this was introduced when the support for Anonymous
2527 // Methods was implemented.
2529 public class ToplevelBlock : ExplicitBlock
2532 // Block is converted to an expression
2534 sealed class BlockScopeExpression : Expression
2537 readonly ToplevelBlock block;
2539 public BlockScopeExpression (Expression child, ToplevelBlock block)
2545 public override Expression CreateExpressionTree (ResolveContext ec)
2547 throw new NotSupportedException ();
2550 public override Expression DoResolve (ResolveContext ec)
2555 child = child.Resolve (ec);
2559 eclass = child.eclass;
2564 public override void Emit (EmitContext ec)
2566 block.EmitMeta (ec);
2567 block.EmitScopeInitializers (ec);
2571 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2573 type = storey.MutateType (type);
2574 child.MutateHoistedGenericType (storey);
2575 block.MutateHoistedGenericType (storey);
2579 GenericMethod generic;
2580 protected ParametersCompiled parameters;
2581 ToplevelParameterInfo[] parameter_info;
2582 LocalInfo this_variable;
2586 public HoistedVariable HoistedThisVariable;
2588 public bool Resolved {
2595 // The parameters for the block.
2597 public ParametersCompiled Parameters {
2598 get { return parameters; }
2601 public GenericMethod GenericMethod {
2602 get { return generic; }
2605 public ToplevelBlock Container {
2606 get { return Parent == null ? null : Parent.Toplevel; }
2609 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2610 this (parent, (Flags) 0, parameters, start)
2614 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2615 this (parent, parameters, start)
2617 this.generic = generic;
2620 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2621 this (null, (Flags) 0, parameters, start)
2625 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2626 this (null, flags, parameters, start)
2630 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2631 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2632 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2633 base (null, flags, start, Location.Null)
2635 this.Toplevel = this;
2637 this.parameters = parameters;
2638 this.Parent = parent;
2640 parent.AddAnonymousChild (this);
2642 if (!this.parameters.IsEmpty)
2643 ProcessParameters ();
2646 public ToplevelBlock (Location loc)
2647 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2651 protected override void CloneTo (CloneContext clonectx, Statement t)
2653 ToplevelBlock target = (ToplevelBlock) t;
2654 base.CloneTo (clonectx, t);
2656 if (parameters.Count != 0)
2657 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2658 for (int i = 0; i < parameters.Count; ++i)
2659 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2662 public bool CheckError158 (string name, Location loc)
2664 if (AnonymousChildren != null) {
2665 foreach (ToplevelBlock child in AnonymousChildren) {
2666 if (!child.CheckError158 (name, loc))
2671 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2672 if (!c.DoCheckError158 (name, loc))
2679 void ProcessParameters ()
2681 int n = parameters.Count;
2682 parameter_info = new ToplevelParameterInfo [n];
2683 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2684 for (int i = 0; i < n; ++i) {
2685 parameter_info [i] = new ToplevelParameterInfo (this, i);
2687 Parameter p = parameters [i];
2691 string name = p.Name;
2692 if (CheckParentConflictName (top_parent, name, loc))
2693 AddKnownVariable (name, parameter_info [i]);
2696 // mark this block as "used" so that we create local declarations in a sub-block
2697 // FIXME: This appears to uncover a lot of bugs
2701 bool DoCheckError158 (string name, Location loc)
2703 LabeledStatement s = LookupLabel (name);
2705 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2706 Error_158 (name, loc);
2713 public override Expression CreateExpressionTree (ResolveContext ec)
2715 if (statements.Count == 1) {
2716 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2717 if (scope_initializers != null)
2718 expr = new BlockScopeExpression (expr, this);
2723 return base.CreateExpressionTree (ec);
2727 // Reformats this block to be top-level iterator block
2729 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2733 // Creates block with original statements
2734 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2736 source.statements = new ArrayList (1);
2737 source.AddStatement (new Return (iterator, iterator.Location));
2738 source.IsIterator = false;
2740 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2741 source.am_storey = iterator_storey;
2742 return iterator_storey;
2746 // Returns a parameter reference expression for the given name,
2747 // or null if there is no such parameter
2749 public Expression GetParameterReference (string name, Location loc)
2751 for (ToplevelBlock t = this; t != null; t = t.Container) {
2752 Expression expr = t.GetParameterReferenceExpression (name, loc);
2760 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2762 int idx = parameters.GetParameterIndexByName (name);
2764 null : new ParameterReference (parameter_info [idx], loc);
2768 // Returns the "this" instance variable of this block.
2769 // See AddThisVariable() for more information.
2771 public LocalInfo ThisVariable {
2772 get { return this_variable; }
2776 // This is used by non-static `struct' constructors which do not have an
2777 // initializer - in this case, the constructor must initialize all of the
2778 // struct's fields. To do this, we add a "this" variable and use the flow
2779 // analysis code to ensure that it's been fully initialized before control
2780 // leaves the constructor.
2782 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2784 if (this_variable == null) {
2785 this_variable = new LocalInfo (ds, this, l);
2786 this_variable.Used = true;
2787 this_variable.IsThis = true;
2789 Variables.Add ("this", this_variable);
2792 return this_variable;
2795 public bool IsIterator {
2796 get { return (flags & Flags.IsIterator) != 0; }
2797 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2800 public bool IsThisAssigned (BlockContext ec)
2802 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2805 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2813 if (!ResolveMeta (rc, ip))
2816 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2817 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2822 unreachable = top_level.End ();
2824 } catch (Exception) {
2826 if (rc.CurrentBlock != null) {
2827 Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2829 Report.Error (587, "Internal compiler error: Phase Resolve");
2835 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2836 if (rc.CurrentAnonymousMethod == null) {
2837 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2839 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2840 Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2841 rc.CurrentAnonymousMethod.GetSignatureForError ());
2849 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2851 int errors = Report.Errors;
2852 int orig_count = parameters.Count;
2857 // Assert: orig_count != parameter.Count => orig_count == 0
2858 if (orig_count != 0 && orig_count != parameters.Count)
2859 throw new InternalErrorException ("parameter information mismatch");
2861 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2863 for (int i = 0; i < orig_count; ++i) {
2864 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2866 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2869 VariableInfo vi = new VariableInfo (ip, i, offset);
2870 parameter_info [i].VariableInfo = vi;
2871 offset += vi.Length;
2874 ResolveMeta (ec, offset);
2876 return Report.Errors == errors;
2880 // Check whether all `out' parameters have been assigned.
2882 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2884 if (vector.IsUnreachable)
2887 int n = parameter_info == null ? 0 : parameter_info.Length;
2889 for (int i = 0; i < n; i++) {
2890 VariableInfo var = parameter_info [i].VariableInfo;
2895 if (vector.IsAssigned (var, false))
2898 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2903 public override void Emit (EmitContext ec)
2905 if (Report.Errors > 0)
2913 if (ec.HasReturnLabel)
2914 ec.ReturnLabel = ec.ig.DefineLabel ();
2918 ec.Mark (EndLocation);
2920 if (ec.HasReturnLabel)
2921 ec.ig.MarkLabel (ec.ReturnLabel);
2923 if (ec.return_value != null) {
2924 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2925 ec.ig.Emit (OpCodes.Ret);
2928 // If `HasReturnLabel' is set, then we already emitted a
2929 // jump to the end of the method, so we must emit a `ret'
2932 // Unfortunately, System.Reflection.Emit automatically emits
2933 // a leave to the end of a finally block. This is a problem
2934 // if no code is following the try/finally block since we may
2935 // jump to a point after the end of the method.
2936 // As a workaround, we're always creating a return label in
2940 if (ec.HasReturnLabel || !unreachable) {
2941 if (ec.ReturnType != TypeManager.void_type)
2942 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2943 ec.ig.Emit (OpCodes.Ret);
2948 } catch (Exception e){
2949 Console.WriteLine ("Exception caught by the compiler while emitting:");
2950 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2952 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2958 public override void EmitMeta (EmitContext ec)
2960 parameters.ResolveVariable ();
2962 // Avoid declaring an IL variable for this_variable since it is not accessed
2963 // from the generated IL
2964 if (this_variable != null)
2965 Variables.Remove ("this");
2969 protected override void EmitSymbolInfo (EmitContext ec)
2971 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2972 if ((ae != null) && (ae.Storey != null))
2973 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2975 base.EmitSymbolInfo (ec);
2979 public class SwitchLabel {
2986 Label il_label_code;
2987 bool il_label_code_set;
2989 public static readonly object NullStringCase = new object ();
2992 // if expr == null, then it is the default case.
2994 public SwitchLabel (Expression expr, Location l)
3000 public Expression Label {
3006 public Location Location {
3010 public object Converted {
3016 public Label GetILLabel (EmitContext ec)
3019 il_label = ec.ig.DefineLabel ();
3020 il_label_set = true;
3025 public Label GetILLabelCode (EmitContext ec)
3027 if (!il_label_code_set){
3028 il_label_code = ec.ig.DefineLabel ();
3029 il_label_code_set = true;
3031 return il_label_code;
3035 // Resolves the expression, reduces it to a literal if possible
3036 // and then converts it to the requested type.
3038 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3040 Expression e = label.Resolve (ec);
3045 Constant c = e as Constant;
3047 Report.Error (150, loc, "A constant value is expected");
3051 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3052 converted = NullStringCase;
3056 if (allow_nullable && c.GetValue () == null) {
3057 converted = NullStringCase;
3061 c = c.ImplicitConversionRequired (ec, required_type, loc);
3065 converted = c.GetValue ();
3069 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3072 if (converted == null)
3074 else if (converted == NullStringCase)
3077 label = converted.ToString ();
3079 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3080 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3083 public SwitchLabel Clone (CloneContext clonectx)
3085 return new SwitchLabel (label.Clone (clonectx), loc);
3089 public class SwitchSection {
3090 // An array of SwitchLabels.
3091 public readonly ArrayList Labels;
3092 public readonly Block Block;
3094 public SwitchSection (ArrayList labels, Block block)
3100 public SwitchSection Clone (CloneContext clonectx)
3102 ArrayList cloned_labels = new ArrayList ();
3104 foreach (SwitchLabel sl in cloned_labels)
3105 cloned_labels.Add (sl.Clone (clonectx));
3107 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3111 public class Switch : Statement {
3112 public ArrayList Sections;
3113 public Expression Expr;
3116 /// Maps constants whose type type SwitchType to their SwitchLabels.
3118 public IDictionary Elements;
3121 /// The governing switch type
3123 public Type SwitchType;
3128 Label default_target;
3130 Expression new_expr;
3133 SwitchSection constant_section;
3134 SwitchSection default_section;
3136 ExpressionStatement string_dictionary;
3137 FieldExpr switch_cache_field;
3138 static int unique_counter;
3141 // Nullable Types support
3143 Nullable.Unwrap unwrap;
3145 protected bool HaveUnwrap {
3146 get { return unwrap != null; }
3150 // The types allowed to be implicitly cast from
3151 // on the governing type
3153 static Type [] allowed_types;
3155 public Switch (Expression e, ArrayList sects, Location l)
3162 public bool GotDefault {
3164 return default_section != null;
3168 public Label DefaultTarget {
3170 return default_target;
3175 // Determines the governing type for a switch. The returned
3176 // expression might be the expression from the switch, or an
3177 // expression that includes any potential conversions to the
3178 // integral types or to string.
3180 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3184 if (t == TypeManager.byte_type ||
3185 t == TypeManager.sbyte_type ||
3186 t == TypeManager.ushort_type ||
3187 t == TypeManager.short_type ||
3188 t == TypeManager.uint32_type ||
3189 t == TypeManager.int32_type ||
3190 t == TypeManager.uint64_type ||
3191 t == TypeManager.int64_type ||
3192 t == TypeManager.char_type ||
3193 t == TypeManager.string_type ||
3194 t == TypeManager.bool_type ||
3195 TypeManager.IsEnumType (t))
3198 if (allowed_types == null){
3199 allowed_types = new Type [] {
3200 TypeManager.sbyte_type,
3201 TypeManager.byte_type,
3202 TypeManager.short_type,
3203 TypeManager.ushort_type,
3204 TypeManager.int32_type,
3205 TypeManager.uint32_type,
3206 TypeManager.int64_type,
3207 TypeManager.uint64_type,
3208 TypeManager.char_type,
3209 TypeManager.string_type
3214 // Try to find a *user* defined implicit conversion.
3216 // If there is no implicit conversion, or if there are multiple
3217 // conversions, we have to report an error
3219 Expression converted = null;
3220 foreach (Type tt in allowed_types){
3223 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3228 // Ignore over-worked ImplicitUserConversions that do
3229 // an implicit conversion in addition to the user conversion.
3231 if (!(e is UserCast))
3234 if (converted != null){
3235 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3245 // Performs the basic sanity checks on the switch statement
3246 // (looks for duplicate keys and non-constant expressions).
3248 // It also returns a hashtable with the keys that we will later
3249 // use to compute the switch tables
3251 bool CheckSwitch (ResolveContext ec)
3254 Elements = Sections.Count > 10 ?
3255 (IDictionary)new Hashtable () :
3256 (IDictionary)new ListDictionary ();
3258 foreach (SwitchSection ss in Sections){
3259 foreach (SwitchLabel sl in ss.Labels){
3260 if (sl.Label == null){
3261 if (default_section != null){
3262 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3265 default_section = ss;
3269 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3274 object key = sl.Converted;
3275 if (key == SwitchLabel.NullStringCase)
3276 has_null_case = true;
3279 Elements.Add (key, sl);
3280 } catch (ArgumentException) {
3281 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3289 void EmitObjectInteger (ILGenerator ig, object k)
3292 IntConstant.EmitInt (ig, (int) k);
3293 else if (k is Constant) {
3294 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3297 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3300 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3302 IntConstant.EmitInt (ig, (int) (long) k);
3303 ig.Emit (OpCodes.Conv_I8);
3306 LongConstant.EmitLong (ig, (long) k);
3308 else if (k is ulong)
3310 ulong ul = (ulong) k;
3313 IntConstant.EmitInt (ig, unchecked ((int) ul));
3314 ig.Emit (OpCodes.Conv_U8);
3318 LongConstant.EmitLong (ig, unchecked ((long) ul));
3322 IntConstant.EmitInt (ig, (int) ((char) k));
3323 else if (k is sbyte)
3324 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3326 IntConstant.EmitInt (ig, (int) ((byte) k));
3327 else if (k is short)
3328 IntConstant.EmitInt (ig, (int) ((short) k));
3329 else if (k is ushort)
3330 IntConstant.EmitInt (ig, (int) ((ushort) k));
3332 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3334 throw new Exception ("Unhandled case");
3337 // structure used to hold blocks of keys while calculating table switch
3338 class KeyBlock : IComparable
3340 public KeyBlock (long _first)
3342 first = last = _first;
3346 public ArrayList element_keys = null;
3347 // how many items are in the bucket
3348 public int Size = 1;
3351 get { return (int) (last - first + 1); }
3353 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3355 return kb_last.last - kb_first.first + 1;
3357 public int CompareTo (object obj)
3359 KeyBlock kb = (KeyBlock) obj;
3360 int nLength = Length;
3361 int nLengthOther = kb.Length;
3362 if (nLengthOther == nLength)
3363 return (int) (kb.first - first);
3364 return nLength - nLengthOther;
3369 /// This method emits code for a lookup-based switch statement (non-string)
3370 /// Basically it groups the cases into blocks that are at least half full,
3371 /// and then spits out individual lookup opcodes for each block.
3372 /// It emits the longest blocks first, and short blocks are just
3373 /// handled with direct compares.
3375 /// <param name="ec"></param>
3376 /// <param name="val"></param>
3377 /// <returns></returns>
3378 void TableSwitchEmit (EmitContext ec, Expression val)
3380 int element_count = Elements.Count;
3381 object [] element_keys = new object [element_count];
3382 Elements.Keys.CopyTo (element_keys, 0);
3383 Array.Sort (element_keys);
3385 // initialize the block list with one element per key
3386 ArrayList key_blocks = new ArrayList (element_count);
3387 foreach (object key in element_keys)
3388 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3390 KeyBlock current_kb;
3391 // iteratively merge the blocks while they are at least half full
3392 // there's probably a really cool way to do this with a tree...
3393 while (key_blocks.Count > 1)
3395 ArrayList key_blocks_new = new ArrayList ();
3396 current_kb = (KeyBlock) key_blocks [0];
3397 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3399 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3400 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3403 current_kb.last = kb.last;
3404 current_kb.Size += kb.Size;
3408 // start a new block
3409 key_blocks_new.Add (current_kb);
3413 key_blocks_new.Add (current_kb);
3414 if (key_blocks.Count == key_blocks_new.Count)
3416 key_blocks = key_blocks_new;
3419 // initialize the key lists
3420 foreach (KeyBlock kb in key_blocks)
3421 kb.element_keys = new ArrayList ();
3423 // fill the key lists
3425 if (key_blocks.Count > 0) {
3426 current_kb = (KeyBlock) key_blocks [0];
3427 foreach (object key in element_keys)
3429 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3430 System.Convert.ToInt64 (key) > current_kb.last;
3432 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3433 current_kb.element_keys.Add (key);
3437 // sort the blocks so we can tackle the largest ones first
3440 // okay now we can start...
3441 ILGenerator ig = ec.ig;
3442 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3443 Label lbl_default = default_target;
3445 Type type_keys = null;
3446 if (element_keys.Length > 0)
3447 type_keys = element_keys [0].GetType (); // used for conversions
3451 if (TypeManager.IsEnumType (SwitchType))
3452 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3454 compare_type = SwitchType;
3456 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3458 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3459 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3462 foreach (object key in kb.element_keys) {
3463 SwitchLabel sl = (SwitchLabel) Elements [key];
3464 if (key is int && (int) key == 0) {
3465 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3468 EmitObjectInteger (ig, key);
3469 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3475 // TODO: if all the keys in the block are the same and there are
3476 // no gaps/defaults then just use a range-check.
3477 if (compare_type == TypeManager.int64_type ||
3478 compare_type == TypeManager.uint64_type)
3480 // TODO: optimize constant/I4 cases
3482 // check block range (could be > 2^31)
3484 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3485 ig.Emit (OpCodes.Blt, lbl_default);
3487 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3488 ig.Emit (OpCodes.Bgt, lbl_default);
3494 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3495 ig.Emit (OpCodes.Sub);
3497 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3503 int first = (int) kb.first;
3506 IntConstant.EmitInt (ig, first);
3507 ig.Emit (OpCodes.Sub);
3511 IntConstant.EmitInt (ig, -first);
3512 ig.Emit (OpCodes.Add);
3516 // first, build the list of labels for the switch
3518 int cJumps = kb.Length;
3519 Label [] switch_labels = new Label [cJumps];
3520 for (int iJump = 0; iJump < cJumps; iJump++)
3522 object key = kb.element_keys [iKey];
3523 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3525 SwitchLabel sl = (SwitchLabel) Elements [key];
3526 switch_labels [iJump] = sl.GetILLabel (ec);
3530 switch_labels [iJump] = lbl_default;
3532 // emit the switch opcode
3533 ig.Emit (OpCodes.Switch, switch_labels);
3536 // mark the default for this block
3538 ig.MarkLabel (lbl_default);
3541 // TODO: find the default case and emit it here,
3542 // to prevent having to do the following jump.
3543 // make sure to mark other labels in the default section
3545 // the last default just goes to the end
3546 if (element_keys.Length > 0)
3547 ig.Emit (OpCodes.Br, lbl_default);
3549 // now emit the code for the sections
3550 bool found_default = false;
3552 foreach (SwitchSection ss in Sections) {
3553 foreach (SwitchLabel sl in ss.Labels) {
3554 if (sl.Converted == SwitchLabel.NullStringCase) {
3555 ig.MarkLabel (null_target);
3556 } else if (sl.Label == null) {
3557 ig.MarkLabel (lbl_default);
3558 found_default = true;
3560 ig.MarkLabel (null_target);
3562 ig.MarkLabel (sl.GetILLabel (ec));
3563 ig.MarkLabel (sl.GetILLabelCode (ec));
3568 if (!found_default) {
3569 ig.MarkLabel (lbl_default);
3570 if (!has_null_case) {
3571 ig.MarkLabel (null_target);
3575 ig.MarkLabel (lbl_end);
3578 SwitchSection FindSection (SwitchLabel label)
3580 foreach (SwitchSection ss in Sections){
3581 foreach (SwitchLabel sl in ss.Labels){
3590 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3592 foreach (SwitchSection ss in Sections)
3593 ss.Block.MutateHoistedGenericType (storey);
3596 public static void Reset ()
3599 allowed_types = null;
3602 public override bool Resolve (BlockContext ec)
3604 Expr = Expr.Resolve (ec);
3608 new_expr = SwitchGoverningType (ec, Expr);
3610 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3611 unwrap = Nullable.Unwrap.Create (Expr, false);
3615 new_expr = SwitchGoverningType (ec, unwrap);
3618 if (new_expr == null){
3619 Report.Error (151, loc,
3620 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3621 TypeManager.CSharpName (Expr.Type));
3626 SwitchType = new_expr.Type;
3628 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3629 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3633 if (!CheckSwitch (ec))
3637 Elements.Remove (SwitchLabel.NullStringCase);
3639 Switch old_switch = ec.Switch;
3641 ec.Switch.SwitchType = SwitchType;
3643 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3644 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3646 is_constant = new_expr is Constant;
3648 object key = ((Constant) new_expr).GetValue ();
3649 SwitchLabel label = (SwitchLabel) Elements [key];
3651 constant_section = FindSection (label);
3652 if (constant_section == null)
3653 constant_section = default_section;
3658 foreach (SwitchSection ss in Sections){
3660 ec.CurrentBranching.CreateSibling (
3661 null, FlowBranching.SiblingType.SwitchSection);
3665 if (is_constant && (ss != constant_section)) {
3666 // If we're a constant switch, we're only emitting
3667 // one single section - mark all the others as
3669 ec.CurrentBranching.CurrentUsageVector.Goto ();
3670 if (!ss.Block.ResolveUnreachable (ec, true)) {
3674 if (!ss.Block.Resolve (ec))
3679 if (default_section == null)
3680 ec.CurrentBranching.CreateSibling (
3681 null, FlowBranching.SiblingType.SwitchSection);
3683 ec.EndFlowBranching ();
3684 ec.Switch = old_switch;
3686 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3691 if (SwitchType == TypeManager.string_type && !is_constant) {
3692 // TODO: Optimize single case, and single+default case
3693 ResolveStringSwitchMap (ec);
3699 void ResolveStringSwitchMap (ResolveContext ec)
3701 FullNamedExpression string_dictionary_type;
3702 if (TypeManager.generic_ienumerable_type != null) {
3703 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3704 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3706 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3708 new TypeExpression (TypeManager.string_type, loc),
3709 new TypeExpression (TypeManager.int32_type, loc)), loc);
3711 MemberAccess system_collections_generic = new MemberAccess (
3712 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3714 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3717 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3718 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3719 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3720 if (!field.Define ())
3722 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3724 ArrayList init = new ArrayList ();
3727 string value = null;
3728 foreach (SwitchSection section in Sections) {
3729 int last_count = init.Count;
3730 foreach (SwitchLabel sl in section.Labels) {
3731 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3734 value = (string) sl.Converted;
3735 ArrayList init_args = new ArrayList (2);
3736 init_args.Add (new StringLiteral (value, sl.Location));
3737 init_args.Add (new IntConstant (counter, loc));
3738 init.Add (new CollectionElementInitializer (init_args, loc));
3742 // Don't add empty sections
3744 if (last_count == init.Count)
3747 Elements.Add (counter, section.Labels [0]);
3751 Arguments args = new Arguments (1);
3752 args.Add (new Argument (new IntConstant (init.Count, loc)));
3753 Expression initializer = new NewInitialize (string_dictionary_type, args,
3754 new CollectionOrObjectInitializers (init, loc), loc);
3756 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3757 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3760 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3762 ILGenerator ig = ec.ig;
3763 Label l_initialized = ig.DefineLabel ();
3766 // Skip initialization when value is null
3768 value.EmitBranchable (ec, null_target, false);
3771 // Check if string dictionary is initialized and initialize
3773 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3774 string_dictionary.EmitStatement (ec);
3775 ig.MarkLabel (l_initialized);
3777 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3779 ResolveContext rc = new ResolveContext (ec.MemberContext);
3781 if (TypeManager.generic_ienumerable_type != null) {
3782 Arguments get_value_args = new Arguments (2);
3783 get_value_args.Add (new Argument (value));
3784 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3785 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3786 if (get_item == null)
3790 // A value was not found, go to default case
3792 get_item.EmitBranchable (ec, default_target, false);
3794 Arguments get_value_args = new Arguments (1);
3795 get_value_args.Add (new Argument (value));
3797 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3798 if (get_item == null)
3801 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3802 get_item_object.EmitAssign (ec, get_item, true, false);
3803 ec.ig.Emit (OpCodes.Brfalse, default_target);
3805 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3806 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3808 get_item_int.EmitStatement (ec);
3809 get_item_object.Release (ec);
3812 TableSwitchEmit (ec, string_switch_variable);
3813 string_switch_variable.Release (ec);
3816 protected override void DoEmit (EmitContext ec)
3818 ILGenerator ig = ec.ig;
3820 default_target = ig.DefineLabel ();
3821 null_target = ig.DefineLabel ();
3823 // Store variable for comparission purposes
3824 // TODO: Don't duplicate non-captured VariableReference
3825 LocalTemporary value;
3827 value = new LocalTemporary (SwitchType);
3828 unwrap.EmitCheck (ec);
3829 ig.Emit (OpCodes.Brfalse, null_target);
3832 } else if (!is_constant) {
3833 value = new LocalTemporary (SwitchType);
3840 // Setup the codegen context
3842 Label old_end = ec.LoopEnd;
3843 Switch old_switch = ec.Switch;
3845 ec.LoopEnd = ig.DefineLabel ();
3850 if (constant_section != null)
3851 constant_section.Block.Emit (ec);
3852 } else if (string_dictionary != null) {
3853 DoEmitStringSwitch (value, ec);
3855 TableSwitchEmit (ec, value);
3861 // Restore context state.
3862 ig.MarkLabel (ec.LoopEnd);
3865 // Restore the previous context
3867 ec.LoopEnd = old_end;
3868 ec.Switch = old_switch;
3871 protected override void CloneTo (CloneContext clonectx, Statement t)
3873 Switch target = (Switch) t;
3875 target.Expr = Expr.Clone (clonectx);
3876 target.Sections = new ArrayList ();
3877 foreach (SwitchSection ss in Sections){
3878 target.Sections.Add (ss.Clone (clonectx));
3883 // A place where execution can restart in an iterator
3884 public abstract class ResumableStatement : Statement
3887 protected Label resume_point;
3889 public Label PrepareForEmit (EmitContext ec)
3893 resume_point = ec.ig.DefineLabel ();
3895 return resume_point;
3898 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3902 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3907 // Base class for statements that are implemented in terms of try...finally
3908 public abstract class ExceptionStatement : ResumableStatement
3913 protected abstract void EmitPreTryBody (EmitContext ec);
3914 protected abstract void EmitTryBody (EmitContext ec);
3915 protected abstract void EmitFinallyBody (EmitContext ec);
3917 protected sealed override void DoEmit (EmitContext ec)
3919 ILGenerator ig = ec.ig;
3921 EmitPreTryBody (ec);
3923 if (resume_points != null) {
3924 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3925 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3928 ig.BeginExceptionBlock ();
3930 if (resume_points != null) {
3931 ig.MarkLabel (resume_point);
3933 // For normal control flow, we want to fall-through the Switch
3934 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3935 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3936 IntConstant.EmitInt (ig, first_resume_pc);
3937 ig.Emit (OpCodes.Sub);
3939 Label [] labels = new Label [resume_points.Count];
3940 for (int i = 0; i < resume_points.Count; ++i)
3941 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3942 ig.Emit (OpCodes.Switch, labels);
3947 ig.BeginFinallyBlock ();
3949 Label start_finally = ec.ig.DefineLabel ();
3950 if (resume_points != null) {
3951 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3952 ig.Emit (OpCodes.Brfalse_S, start_finally);
3953 ig.Emit (OpCodes.Endfinally);
3956 ig.MarkLabel (start_finally);
3957 EmitFinallyBody (ec);
3959 ig.EndExceptionBlock ();
3962 public void SomeCodeFollows ()
3964 code_follows = true;
3967 public override bool Resolve (BlockContext ec)
3969 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3970 // So, ensure there's some IL code after this statement.
3971 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3972 ec.NeedReturnLabel ();
3974 iter = ec.CurrentIterator;
3978 ArrayList resume_points;
3979 int first_resume_pc;
3980 public void AddResumePoint (ResumableStatement stmt, int pc)
3982 if (resume_points == null) {
3983 resume_points = new ArrayList ();
3984 first_resume_pc = pc;
3987 if (pc != first_resume_pc + resume_points.Count)
3988 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3990 resume_points.Add (stmt);
3993 Label dispose_try_block;
3994 bool prepared_for_dispose, emitted_dispose;
3995 public override Label PrepareForDispose (EmitContext ec, Label end)
3997 if (!prepared_for_dispose) {
3998 prepared_for_dispose = true;
3999 dispose_try_block = ec.ig.DefineLabel ();
4001 return dispose_try_block;
4004 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4006 if (emitted_dispose)
4009 emitted_dispose = true;
4011 ILGenerator ig = ec.ig;
4013 Label end_of_try = ig.DefineLabel ();
4015 // Ensure that the only way we can get into this code is through a dispatcher
4016 if (have_dispatcher)
4017 ig.Emit (OpCodes.Br, end);
4019 ig.BeginExceptionBlock ();
4021 ig.MarkLabel (dispose_try_block);
4023 Label [] labels = null;
4024 for (int i = 0; i < resume_points.Count; ++i) {
4025 ResumableStatement s = (ResumableStatement) resume_points [i];
4026 Label ret = s.PrepareForDispose (ec, end_of_try);
4027 if (ret.Equals (end_of_try) && labels == null)
4029 if (labels == null) {
4030 labels = new Label [resume_points.Count];
4031 for (int j = 0; j < i; ++j)
4032 labels [j] = end_of_try;
4037 if (labels != null) {
4039 for (j = 1; j < labels.Length; ++j)
4040 if (!labels [0].Equals (labels [j]))
4042 bool emit_dispatcher = j < labels.Length;
4044 if (emit_dispatcher) {
4045 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4046 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4047 IntConstant.EmitInt (ig, first_resume_pc);
4048 ig.Emit (OpCodes.Sub);
4049 ig.Emit (OpCodes.Switch, labels);
4050 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4053 foreach (ResumableStatement s in resume_points)
4054 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4057 ig.MarkLabel (end_of_try);
4059 ig.BeginFinallyBlock ();
4061 EmitFinallyBody (ec);
4063 ig.EndExceptionBlock ();
4067 public class Lock : ExceptionStatement {
4069 public Statement Statement;
4070 TemporaryVariable temp;
4072 public Lock (Expression expr, Statement stmt, Location l)
4079 public override bool Resolve (BlockContext ec)
4081 expr = expr.Resolve (ec);
4085 if (!TypeManager.IsReferenceType (expr.Type)){
4086 Report.Error (185, loc,
4087 "`{0}' is not a reference type as required by the lock statement",
4088 TypeManager.CSharpName (expr.Type));
4092 ec.StartFlowBranching (this);
4093 bool ok = Statement.Resolve (ec);
4094 ec.EndFlowBranching ();
4096 ok &= base.Resolve (ec);
4098 // Avoid creating libraries that reference the internal
4101 if (t == TypeManager.null_type)
4102 t = TypeManager.object_type;
4104 temp = new TemporaryVariable (t, loc);
4107 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4108 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4109 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4110 monitor_type, "Enter", loc, TypeManager.object_type);
4111 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4112 monitor_type, "Exit", loc, TypeManager.object_type);
4118 protected override void EmitPreTryBody (EmitContext ec)
4120 ILGenerator ig = ec.ig;
4122 temp.EmitAssign (ec, expr);
4124 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4127 protected override void EmitTryBody (EmitContext ec)
4129 Statement.Emit (ec);
4132 protected override void EmitFinallyBody (EmitContext ec)
4135 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4138 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4140 expr.MutateHoistedGenericType (storey);
4141 temp.MutateHoistedGenericType (storey);
4142 Statement.MutateHoistedGenericType (storey);
4145 protected override void CloneTo (CloneContext clonectx, Statement t)
4147 Lock target = (Lock) t;
4149 target.expr = expr.Clone (clonectx);
4150 target.Statement = Statement.Clone (clonectx);
4154 public class Unchecked : Statement {
4157 public Unchecked (Block b)
4163 public override bool Resolve (BlockContext ec)
4165 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4166 return Block.Resolve (ec);
4169 protected override void DoEmit (EmitContext ec)
4171 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4175 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4177 Block.MutateHoistedGenericType (storey);
4180 protected override void CloneTo (CloneContext clonectx, Statement t)
4182 Unchecked target = (Unchecked) t;
4184 target.Block = clonectx.LookupBlock (Block);
4188 public class Checked : Statement {
4191 public Checked (Block b)
4194 b.Unchecked = false;
4197 public override bool Resolve (BlockContext ec)
4199 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4200 return Block.Resolve (ec);
4203 protected override void DoEmit (EmitContext ec)
4205 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4209 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4211 Block.MutateHoistedGenericType (storey);
4214 protected override void CloneTo (CloneContext clonectx, Statement t)
4216 Checked target = (Checked) t;
4218 target.Block = clonectx.LookupBlock (Block);
4222 public class Unsafe : Statement {
4225 public Unsafe (Block b)
4228 Block.Unsafe = true;
4229 loc = b.StartLocation;
4232 public override bool Resolve (BlockContext ec)
4234 if (ec.CurrentIterator != null)
4235 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4237 using (ec.Set (ResolveContext.Options.UnsafeScope))
4238 return Block.Resolve (ec);
4241 protected override void DoEmit (EmitContext ec)
4246 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4248 Block.MutateHoistedGenericType (storey);
4251 protected override void CloneTo (CloneContext clonectx, Statement t)
4253 Unsafe target = (Unsafe) t;
4255 target.Block = clonectx.LookupBlock (Block);
4262 public class Fixed : Statement {
4264 ArrayList declarators;
4265 Statement statement;
4270 abstract class Emitter
4272 protected LocalInfo vi;
4273 protected Expression converted;
4275 protected Emitter (Expression expr, LocalInfo li)
4281 public abstract void Emit (EmitContext ec);
4282 public abstract void EmitExit (EmitContext ec);
4285 class ExpressionEmitter : Emitter {
4286 public ExpressionEmitter (Expression converted, LocalInfo li) :
4287 base (converted, li)
4291 public override void Emit (EmitContext ec) {
4293 // Store pointer in pinned location
4295 converted.Emit (ec);
4299 public override void EmitExit (EmitContext ec)
4301 ec.ig.Emit (OpCodes.Ldc_I4_0);
4302 ec.ig.Emit (OpCodes.Conv_U);
4307 class StringEmitter : Emitter
4309 LocalInfo pinned_string;
4311 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4314 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4315 pinned_string.Pinned = true;
4318 public StringEmitter Resolve (ResolveContext rc)
4320 pinned_string.Resolve (rc);
4322 if (TypeManager.int_get_offset_to_string_data == null) {
4323 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4324 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4330 public override void Emit (EmitContext ec)
4332 pinned_string.ResolveVariable (ec);
4334 converted.Emit (ec);
4335 pinned_string.EmitAssign (ec);
4337 // TODO: Should use Binary::Add
4338 pinned_string.Emit (ec);
4339 ec.ig.Emit (OpCodes.Conv_I);
4341 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4342 //pe.InstanceExpression = pinned_string;
4343 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4345 ec.ig.Emit (OpCodes.Add);
4349 public override void EmitExit (EmitContext ec)
4351 ec.ig.Emit (OpCodes.Ldnull);
4352 pinned_string.EmitAssign (ec);
4356 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4359 declarators = decls;
4364 public Statement Statement {
4365 get { return statement; }
4368 public override bool Resolve (BlockContext ec)
4371 Expression.UnsafeError (loc);
4375 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4376 if (texpr == null) {
4377 if (type is VarExpr)
4378 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4383 expr_type = texpr.Type;
4385 data = new Emitter [declarators.Count];
4387 if (!expr_type.IsPointer){
4388 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4393 foreach (Pair p in declarators){
4394 LocalInfo vi = (LocalInfo) p.First;
4395 Expression e = (Expression) p.Second;
4397 vi.VariableInfo.SetAssigned (ec);
4398 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4401 // The rules for the possible declarators are pretty wise,
4402 // but the production on the grammar is more concise.
4404 // So we have to enforce these rules here.
4406 // We do not resolve before doing the case 1 test,
4407 // because the grammar is explicit in that the token &
4408 // is present, so we need to test for this particular case.
4412 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4416 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4426 if (e.Type.IsArray){
4427 Type array_type = TypeManager.GetElementType (e.Type);
4430 // Provided that array_type is unmanaged,
4432 if (!TypeManager.VerifyUnManaged (array_type, loc))
4436 // and T* is implicitly convertible to the
4437 // pointer type given in the fixed statement.
4439 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4441 Expression converted = Convert.ImplicitConversionRequired (
4442 ec, array_ptr, vi.VariableType, loc);
4443 if (converted == null)
4447 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4449 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4450 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4451 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4452 new NullPointer (loc),
4455 converted = converted.Resolve (ec);
4457 data [i] = new ExpressionEmitter (converted, vi);
4466 if (e.Type == TypeManager.string_type){
4467 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4472 // Case 4: fixed buffer
4473 if (e is FixedBufferPtr) {
4474 data [i++] = new ExpressionEmitter (e, vi);
4479 // Case 1: & object.
4481 Unary u = e as Unary;
4482 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4483 IVariableReference vr = u.Expr as IVariableReference;
4484 if (vr == null || !vr.IsFixed) {
4485 data [i] = new ExpressionEmitter (e, vi);
4489 if (data [i++] == null)
4490 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4492 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4495 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4496 bool ok = statement.Resolve (ec);
4497 bool flow_unreachable = ec.EndFlowBranching ();
4498 has_ret = flow_unreachable;
4503 protected override void DoEmit (EmitContext ec)
4505 for (int i = 0; i < data.Length; i++) {
4509 statement.Emit (ec);
4515 // Clear the pinned variable
4517 for (int i = 0; i < data.Length; i++) {
4518 data [i].EmitExit (ec);
4522 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4524 // Fixed statement cannot be used inside anonymous methods or lambdas
4525 throw new NotSupportedException ();
4528 protected override void CloneTo (CloneContext clonectx, Statement t)
4530 Fixed target = (Fixed) t;
4532 target.type = type.Clone (clonectx);
4533 target.declarators = new ArrayList (declarators.Count);
4534 foreach (Pair p in declarators) {
4535 LocalInfo vi = (LocalInfo) p.First;
4536 Expression e = (Expression) p.Second;
4538 target.declarators.Add (
4539 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4542 target.statement = statement.Clone (clonectx);
4546 public class Catch : Statement {
4547 public readonly string Name;
4549 public Block VarBlock;
4551 Expression type_expr;
4554 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4559 VarBlock = var_block;
4563 public Type CatchType {
4569 public bool IsGeneral {
4571 return type_expr == null;
4575 protected override void DoEmit (EmitContext ec)
4577 ILGenerator ig = ec.ig;
4579 if (CatchType != null)
4580 ig.BeginCatchBlock (CatchType);
4582 ig.BeginCatchBlock (TypeManager.object_type);
4584 if (VarBlock != null)
4588 // TODO: Move to resolve
4589 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4590 lvr.Resolve (new ResolveContext (ec.MemberContext));
4593 // Only to make verifier happy
4594 if (TypeManager.IsGenericParameter (lvr.Type))
4595 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4599 if (lvr.IsHoisted) {
4600 LocalTemporary lt = new LocalTemporary (lvr.Type);
4604 // Variable is at the top of the stack
4605 source = EmptyExpression.Null;
4608 lvr.EmitAssign (ec, source, false, false);
4610 ig.Emit (OpCodes.Pop);
4615 public override bool Resolve (BlockContext ec)
4617 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4618 if (type_expr != null) {
4619 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4625 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4626 Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4632 if (!Block.Resolve (ec))
4635 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4636 // emit the "unused variable" warnings.
4637 if (VarBlock != null)
4638 return VarBlock.Resolve (ec);
4644 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4647 type = storey.MutateType (type);
4648 if (VarBlock != null)
4649 VarBlock.MutateHoistedGenericType (storey);
4650 Block.MutateHoistedGenericType (storey);
4653 protected override void CloneTo (CloneContext clonectx, Statement t)
4655 Catch target = (Catch) t;
4657 if (type_expr != null)
4658 target.type_expr = type_expr.Clone (clonectx);
4659 if (VarBlock != null)
4660 target.VarBlock = clonectx.LookupBlock (VarBlock);
4661 target.Block = clonectx.LookupBlock (Block);
4665 public class TryFinally : ExceptionStatement {
4669 public TryFinally (Statement stmt, Block fini, Location l)
4676 public override bool Resolve (BlockContext ec)
4680 ec.StartFlowBranching (this);
4682 if (!stmt.Resolve (ec))
4686 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4687 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4688 if (!fini.Resolve (ec))
4692 ec.EndFlowBranching ();
4694 ok &= base.Resolve (ec);
4699 protected override void EmitPreTryBody (EmitContext ec)
4703 protected override void EmitTryBody (EmitContext ec)
4708 protected override void EmitFinallyBody (EmitContext ec)
4713 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4715 stmt.MutateHoistedGenericType (storey);
4716 fini.MutateHoistedGenericType (storey);
4719 protected override void CloneTo (CloneContext clonectx, Statement t)
4721 TryFinally target = (TryFinally) t;
4723 target.stmt = (Statement) stmt.Clone (clonectx);
4725 target.fini = clonectx.LookupBlock (fini);
4729 public class TryCatch : Statement {
4731 public ArrayList Specific;
4732 public Catch General;
4733 bool inside_try_finally, code_follows;
4735 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4738 this.Specific = catch_clauses;
4739 this.General = null;
4740 this.inside_try_finally = inside_try_finally;
4742 for (int i = 0; i < catch_clauses.Count; ++i) {
4743 Catch c = (Catch) catch_clauses [i];
4745 if (i != catch_clauses.Count - 1)
4746 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4748 catch_clauses.RemoveAt (i);
4756 public override bool Resolve (BlockContext ec)
4760 ec.StartFlowBranching (this);
4762 if (!Block.Resolve (ec))
4765 Type[] prev_catches = new Type [Specific.Count];
4767 foreach (Catch c in Specific){
4768 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4770 if (c.Name != null) {
4771 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4773 throw new Exception ();
4775 vi.VariableInfo = null;
4778 if (!c.Resolve (ec)) {
4783 Type resolved_type = c.CatchType;
4784 for (int ii = 0; ii < last_index; ++ii) {
4785 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4786 Report.Error (160, c.loc,
4787 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4788 TypeManager.CSharpName (prev_catches [ii]));
4793 prev_catches [last_index++] = resolved_type;
4796 if (General != null) {
4797 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4798 foreach (Catch c in Specific){
4799 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4800 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'");
4805 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4807 if (!General.Resolve (ec))
4811 ec.EndFlowBranching ();
4813 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4814 // So, ensure there's some IL code after this statement
4815 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4816 ec.NeedReturnLabel ();
4821 public void SomeCodeFollows ()
4823 code_follows = true;
4826 protected override void DoEmit (EmitContext ec)
4828 ILGenerator ig = ec.ig;
4830 if (!inside_try_finally)
4831 ig.BeginExceptionBlock ();
4835 foreach (Catch c in Specific)
4838 if (General != null)
4841 if (!inside_try_finally)
4842 ig.EndExceptionBlock ();
4845 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4847 Block.MutateHoistedGenericType (storey);
4849 if (General != null)
4850 General.MutateHoistedGenericType (storey);
4851 if (Specific != null) {
4852 foreach (Catch c in Specific)
4853 c.MutateHoistedGenericType (storey);
4857 protected override void CloneTo (CloneContext clonectx, Statement t)
4859 TryCatch target = (TryCatch) t;
4861 target.Block = clonectx.LookupBlock (Block);
4862 if (General != null)
4863 target.General = (Catch) General.Clone (clonectx);
4864 if (Specific != null){
4865 target.Specific = new ArrayList ();
4866 foreach (Catch c in Specific)
4867 target.Specific.Add (c.Clone (clonectx));
4872 // FIXME: Why is it almost exact copy of Using ??
4873 public class UsingTemporary : ExceptionStatement {
4874 TemporaryVariable local_copy;
4875 public Statement Statement;
4879 public UsingTemporary (Expression expr, Statement stmt, Location l)
4886 public override bool Resolve (BlockContext ec)
4888 expr = expr.Resolve (ec);
4892 expr_type = expr.Type;
4894 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4895 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4896 Using.Error_IsNotConvertibleToIDisposable (expr);
4901 local_copy = new TemporaryVariable (expr_type, loc);
4902 local_copy.Resolve (ec);
4904 ec.StartFlowBranching (this);
4906 bool ok = Statement.Resolve (ec);
4908 ec.EndFlowBranching ();
4910 ok &= base.Resolve (ec);
4912 if (TypeManager.void_dispose_void == null) {
4913 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4914 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4920 protected override void EmitPreTryBody (EmitContext ec)
4922 local_copy.EmitAssign (ec, expr);
4925 protected override void EmitTryBody (EmitContext ec)
4927 Statement.Emit (ec);
4930 protected override void EmitFinallyBody (EmitContext ec)
4932 ILGenerator ig = ec.ig;
4933 if (!TypeManager.IsStruct (expr_type)) {
4934 Label skip = ig.DefineLabel ();
4935 local_copy.Emit (ec);
4936 ig.Emit (OpCodes.Brfalse, skip);
4937 local_copy.Emit (ec);
4938 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4939 ig.MarkLabel (skip);
4943 Expression ml = Expression.MemberLookup (
4944 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4945 "Dispose", Location.Null);
4947 if (!(ml is MethodGroupExpr)) {
4948 local_copy.Emit (ec);
4949 ig.Emit (OpCodes.Box, expr_type);
4950 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4954 MethodInfo mi = null;
4956 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4957 if (TypeManager.GetParameterData (mk).Count == 0) {
4964 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4968 local_copy.AddressOf (ec, AddressOp.Load);
4969 ig.Emit (OpCodes.Call, mi);
4972 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4974 expr_type = storey.MutateType (expr_type);
4975 local_copy.MutateHoistedGenericType (storey);
4976 Statement.MutateHoistedGenericType (storey);
4979 protected override void CloneTo (CloneContext clonectx, Statement t)
4981 UsingTemporary target = (UsingTemporary) t;
4983 target.expr = expr.Clone (clonectx);
4984 target.Statement = Statement.Clone (clonectx);
4988 public class Using : ExceptionStatement {
4990 public Statement EmbeddedStatement {
4991 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4997 ExpressionStatement assign;
4999 public Using (Expression var, Expression init, Statement stmt, Location l)
5007 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5009 Report.SymbolRelatedToPreviousError (expr.Type);
5010 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5011 expr.GetSignatureForError ());
5014 protected override void EmitPreTryBody (EmitContext ec)
5016 assign.EmitStatement (ec);
5019 protected override void EmitTryBody (EmitContext ec)
5024 protected override void EmitFinallyBody (EmitContext ec)
5026 ILGenerator ig = ec.ig;
5027 Label skip = ig.DefineLabel ();
5029 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5030 if (emit_null_check) {
5032 ig.Emit (OpCodes.Brfalse, skip);
5035 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5037 if (emit_null_check)
5038 ig.MarkLabel (skip);
5041 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5043 assign.MutateHoistedGenericType (storey);
5044 var.MutateHoistedGenericType (storey);
5045 stmt.MutateHoistedGenericType (storey);
5048 public override bool Resolve (BlockContext ec)
5050 if (!ResolveVariable (ec))
5053 ec.StartFlowBranching (this);
5055 bool ok = stmt.Resolve (ec);
5057 ec.EndFlowBranching ();
5059 ok &= base.Resolve (ec);
5061 if (TypeManager.void_dispose_void == null) {
5062 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5063 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5069 bool ResolveVariable (BlockContext ec)
5071 assign = new SimpleAssign (var, init, loc);
5072 assign = assign.ResolveStatement (ec);
5076 if (assign.Type == TypeManager.idisposable_type ||
5077 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5081 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5083 Error_IsNotConvertibleToIDisposable (var);
5087 throw new NotImplementedException ("covariance?");
5090 protected override void CloneTo (CloneContext clonectx, Statement t)
5092 Using target = (Using) t;
5094 target.var = var.Clone (clonectx);
5095 target.init = init.Clone (clonectx);
5096 target.stmt = stmt.Clone (clonectx);
5101 /// Implementation of the foreach C# statement
5103 public class Foreach : Statement {
5105 sealed class ArrayForeach : Statement
5107 class ArrayCounter : TemporaryVariable
5109 StatementExpression increment;
5111 public ArrayCounter (Location loc)
5112 : base (TypeManager.int32_type, loc)
5116 public void ResolveIncrement (BlockContext ec)
5118 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5119 increment.Resolve (ec);
5122 public void EmitIncrement (EmitContext ec)
5124 increment.Emit (ec);
5128 readonly Foreach for_each;
5129 readonly Statement statement;
5132 TemporaryVariable[] lengths;
5133 Expression [] length_exprs;
5134 ArrayCounter[] counter;
5136 TemporaryVariable copy;
5139 public ArrayForeach (Foreach @foreach, int rank)
5141 for_each = @foreach;
5142 statement = for_each.statement;
5145 counter = new ArrayCounter [rank];
5146 length_exprs = new Expression [rank];
5149 // Only use temporary length variables when dealing with
5150 // multi-dimensional arrays
5153 lengths = new TemporaryVariable [rank];
5156 protected override void CloneTo (CloneContext clonectx, Statement target)
5158 throw new NotImplementedException ();
5161 public override bool Resolve (BlockContext ec)
5163 copy = new TemporaryVariable (for_each.expr.Type, loc);
5166 int rank = length_exprs.Length;
5167 Arguments list = new Arguments (rank);
5168 for (int i = 0; i < rank; i++) {
5169 counter [i] = new ArrayCounter (loc);
5170 counter [i].ResolveIncrement (ec);
5173 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5175 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5176 lengths [i].Resolve (ec);
5178 Arguments args = new Arguments (1);
5179 args.Add (new Argument (new IntConstant (i, loc)));
5180 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5183 list.Add (new Argument (counter [i]));
5186 access = new ElementAccess (copy, list).Resolve (ec);
5190 Expression var_type = for_each.type;
5191 VarExpr ve = var_type as VarExpr;
5193 // Infer implicitly typed local variable from foreach array type
5194 var_type = new TypeExpression (access.Type, ve.Location);
5197 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5198 if (var_type == null)
5201 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5207 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5208 ec.CurrentBranching.CreateSibling ();
5210 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5211 if (for_each.variable == null)
5214 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5215 if (!statement.Resolve (ec))
5217 ec.EndFlowBranching ();
5219 // There's no direct control flow from the end of the embedded statement to the end of the loop
5220 ec.CurrentBranching.CurrentUsageVector.Goto ();
5222 ec.EndFlowBranching ();
5227 protected override void DoEmit (EmitContext ec)
5229 ILGenerator ig = ec.ig;
5231 copy.EmitAssign (ec, for_each.expr);
5233 int rank = length_exprs.Length;
5234 Label[] test = new Label [rank];
5235 Label[] loop = new Label [rank];
5237 for (int i = 0; i < rank; i++) {
5238 test [i] = ig.DefineLabel ();
5239 loop [i] = ig.DefineLabel ();
5241 if (lengths != null)
5242 lengths [i].EmitAssign (ec, length_exprs [i]);
5245 IntConstant zero = new IntConstant (0, loc);
5246 for (int i = 0; i < rank; i++) {
5247 counter [i].EmitAssign (ec, zero);
5249 ig.Emit (OpCodes.Br, test [i]);
5250 ig.MarkLabel (loop [i]);
5253 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5255 statement.Emit (ec);
5257 ig.MarkLabel (ec.LoopBegin);
5259 for (int i = rank - 1; i >= 0; i--){
5260 counter [i].EmitIncrement (ec);
5262 ig.MarkLabel (test [i]);
5263 counter [i].Emit (ec);
5265 if (lengths != null)
5266 lengths [i].Emit (ec);
5268 length_exprs [i].Emit (ec);
5270 ig.Emit (OpCodes.Blt, loop [i]);
5273 ig.MarkLabel (ec.LoopEnd);
5276 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5278 for_each.expr.MutateHoistedGenericType (storey);
5280 copy.MutateHoistedGenericType (storey);
5281 conv.MutateHoistedGenericType (storey);
5282 statement.MutateHoistedGenericType (storey);
5284 for (int i = 0; i < counter.Length; i++) {
5285 counter [i].MutateHoistedGenericType (storey);
5286 if (lengths != null)
5287 lengths [i].MutateHoistedGenericType (storey);
5292 sealed class CollectionForeach : Statement
5294 class CollectionForeachStatement : Statement
5297 Expression variable, current, conv;
5298 Statement statement;
5301 public CollectionForeachStatement (Type type, Expression variable,
5302 Expression current, Statement statement,
5306 this.variable = variable;
5307 this.current = current;
5308 this.statement = statement;
5312 protected override void CloneTo (CloneContext clonectx, Statement target)
5314 throw new NotImplementedException ();
5317 public override bool Resolve (BlockContext ec)
5319 current = current.Resolve (ec);
5320 if (current == null)
5323 conv = Convert.ExplicitConversion (ec, current, type, loc);
5327 assign = new SimpleAssign (variable, conv, loc);
5328 if (assign.Resolve (ec) == null)
5331 if (!statement.Resolve (ec))
5337 protected override void DoEmit (EmitContext ec)
5339 assign.EmitStatement (ec);
5340 statement.Emit (ec);
5343 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5345 assign.MutateHoistedGenericType (storey);
5346 statement.MutateHoistedGenericType (storey);
5350 Expression variable, expr;
5351 Statement statement;
5353 TemporaryVariable enumerator;
5358 MethodGroupExpr get_enumerator;
5359 PropertyExpr get_current;
5360 MethodInfo move_next;
5361 Expression var_type;
5362 Type enumerator_type;
5363 bool enumerator_found;
5365 public CollectionForeach (Expression var_type, Expression var,
5366 Expression expr, Statement stmt, Location l)
5368 this.var_type = var_type;
5369 this.variable = var;
5375 protected override void CloneTo (CloneContext clonectx, Statement target)
5377 throw new NotImplementedException ();
5380 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5382 Type return_type = mi.ReturnType;
5385 // Ok, we can access it, now make sure that we can do something
5386 // with this `GetEnumerator'
5389 if (return_type == TypeManager.ienumerator_type ||
5390 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5392 // If it is not an interface, lets try to find the methods ourselves.
5393 // For example, if we have:
5394 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5395 // We can avoid the iface call. This is a runtime perf boost.
5396 // even bigger if we have a ValueType, because we avoid the cost
5399 // We have to make sure that both methods exist for us to take
5400 // this path. If one of the methods does not exist, we will just
5401 // use the interface. Sadly, this complex if statement is the only
5402 // way I could do this without a goto
5405 if (TypeManager.bool_movenext_void == null) {
5406 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5407 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5410 if (TypeManager.ienumerator_getcurrent == null) {
5411 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5412 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5416 // Prefer a generic enumerator over a non-generic one.
5418 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5419 enumerator_type = return_type;
5420 if (!FetchGetCurrent (ec, return_type))
5421 get_current = new PropertyExpr (
5422 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5423 if (!FetchMoveNext (return_type))
5424 move_next = TypeManager.bool_movenext_void;
5428 if (return_type.IsInterface ||
5429 !FetchMoveNext (return_type) ||
5430 !FetchGetCurrent (ec, return_type)) {
5431 enumerator_type = return_type;
5432 move_next = TypeManager.bool_movenext_void;
5433 get_current = new PropertyExpr (
5434 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5439 // Ok, so they dont return an IEnumerable, we will have to
5440 // find if they support the GetEnumerator pattern.
5443 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5444 Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5445 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5450 enumerator_type = return_type;
5456 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5458 bool FetchMoveNext (Type t)
5460 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5462 BindingFlags.Public | BindingFlags.Instance,
5465 if (move_next_list == null)
5468 foreach (MemberInfo m in move_next_list){
5469 MethodInfo mi = (MethodInfo) m;
5471 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5472 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5482 // Retrieves a `public T get_Current ()' method from the Type `t'
5484 bool FetchGetCurrent (ResolveContext ec, Type t)
5486 PropertyExpr pe = Expression.MemberLookup (
5487 ec.CurrentType, t, "Current", MemberTypes.Property,
5488 Expression.AllBindingFlags, loc) as PropertyExpr;
5496 void Error_Enumerator ()
5498 if (enumerator_found) {
5502 Report.Error (1579, loc,
5503 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5504 TypeManager.CSharpName (expr.Type));
5507 bool IsOverride (MethodInfo m)
5509 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5511 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5513 if (m is MethodBuilder)
5516 MethodInfo base_method = m.GetBaseDefinition ();
5517 return base_method != m;
5520 bool TryType (ResolveContext ec, Type t)
5522 MethodGroupExpr mg = Expression.MemberLookup (
5523 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5524 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5528 MethodInfo result = null;
5529 MethodInfo tmp_move_next = null;
5530 PropertyExpr tmp_get_cur = null;
5531 Type tmp_enumerator_type = enumerator_type;
5532 foreach (MethodInfo mi in mg.Methods) {
5533 if (TypeManager.GetParameterData (mi).Count != 0)
5536 // Check whether GetEnumerator is public
5537 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5540 if (IsOverride (mi))
5543 enumerator_found = true;
5545 if (!GetEnumeratorFilter (ec, mi))
5548 if (result != null) {
5549 if (TypeManager.IsGenericType (result.ReturnType)) {
5550 if (!TypeManager.IsGenericType (mi.ReturnType))
5553 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5554 Report.SymbolRelatedToPreviousError (t);
5555 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5556 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5557 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5561 // Always prefer generics enumerators
5562 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5563 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5564 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5567 Report.SymbolRelatedToPreviousError (result);
5568 Report.SymbolRelatedToPreviousError (mi);
5569 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5570 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5575 tmp_move_next = move_next;
5576 tmp_get_cur = get_current;
5577 tmp_enumerator_type = enumerator_type;
5578 if (mi.DeclaringType == t)
5582 if (result != null) {
5583 move_next = tmp_move_next;
5584 get_current = tmp_get_cur;
5585 enumerator_type = tmp_enumerator_type;
5586 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5587 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5589 if (t != expr.Type) {
5590 expr = Convert.ExplicitConversion (
5593 throw new InternalErrorException ();
5596 get_enumerator.InstanceExpression = expr;
5597 get_enumerator.IsBase = t != expr.Type;
5605 bool ProbeCollectionType (ResolveContext ec, Type t)
5607 int errors = Report.Errors;
5608 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5609 if (TryType (ec, tt))
5614 if (Report.Errors > errors)
5618 // Now try to find the method in the interfaces
5620 Type [] ifaces = TypeManager.GetInterfaces (t);
5621 foreach (Type i in ifaces){
5622 if (TryType (ec, i))
5629 public override bool Resolve (BlockContext ec)
5631 enumerator_type = TypeManager.ienumerator_type;
5633 if (!ProbeCollectionType (ec, expr.Type)) {
5634 Error_Enumerator ();
5638 VarExpr ve = var_type as VarExpr;
5640 // Infer implicitly typed local variable from foreach enumerable type
5641 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5644 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5645 if (var_type == null)
5648 enumerator = new TemporaryVariable (enumerator_type, loc);
5649 enumerator.Resolve (ec);
5651 init = new Invocation (get_enumerator, null);
5652 init = init.Resolve (ec);
5656 Expression move_next_expr;
5658 MemberInfo[] mi = new MemberInfo[] { move_next };
5659 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5660 mg.InstanceExpression = enumerator;
5662 move_next_expr = new Invocation (mg, null);
5665 get_current.InstanceExpression = enumerator;
5667 Statement block = new CollectionForeachStatement (
5668 var_type.Type, variable, get_current, statement, loc);
5670 loop = new While (move_next_expr, block, loc);
5673 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5674 if (implements_idisposable || !enumerator_type.IsSealed) {
5675 wrapper = new DisposableWrapper (this, implements_idisposable);
5677 wrapper = new NonDisposableWrapper (this);
5680 return wrapper.Resolve (ec);
5683 protected override void DoEmit (EmitContext ec)
5688 class NonDisposableWrapper : Statement {
5689 CollectionForeach parent;
5691 internal NonDisposableWrapper (CollectionForeach parent)
5693 this.parent = parent;
5696 protected override void CloneTo (CloneContext clonectx, Statement target)
5698 throw new NotSupportedException ();
5701 public override bool Resolve (BlockContext ec)
5703 return parent.ResolveLoop (ec);
5706 protected override void DoEmit (EmitContext ec)
5708 parent.EmitLoopInit (ec);
5709 parent.EmitLoopBody (ec);
5712 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5714 throw new NotSupportedException ();
5718 sealed class DisposableWrapper : ExceptionStatement
5720 CollectionForeach parent;
5721 bool implements_idisposable;
5723 internal DisposableWrapper (CollectionForeach parent, bool implements)
5725 this.parent = parent;
5726 this.implements_idisposable = implements;
5729 protected override void CloneTo (CloneContext clonectx, Statement target)
5731 throw new NotSupportedException ();
5734 public override bool Resolve (BlockContext ec)
5738 ec.StartFlowBranching (this);
5740 if (!parent.ResolveLoop (ec))
5743 ec.EndFlowBranching ();
5745 ok &= base.Resolve (ec);
5747 if (TypeManager.void_dispose_void == null) {
5748 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5749 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5754 protected override void EmitPreTryBody (EmitContext ec)
5756 parent.EmitLoopInit (ec);
5759 protected override void EmitTryBody (EmitContext ec)
5761 parent.EmitLoopBody (ec);
5764 protected override void EmitFinallyBody (EmitContext ec)
5766 Expression instance = parent.enumerator;
5767 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5768 ILGenerator ig = ec.ig;
5770 parent.enumerator.Emit (ec);
5772 Label call_dispose = ig.DefineLabel ();
5774 if (!implements_idisposable) {
5775 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5776 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5782 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5784 // using 'endfinally' to empty the evaluation stack
5785 ig.Emit (OpCodes.Endfinally);
5786 ig.MarkLabel (call_dispose);
5789 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5792 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5794 throw new NotSupportedException ();
5798 bool ResolveLoop (BlockContext ec)
5800 return loop.Resolve (ec);
5803 void EmitLoopInit (EmitContext ec)
5805 enumerator.EmitAssign (ec, init);
5808 void EmitLoopBody (EmitContext ec)
5813 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5815 enumerator_type = storey.MutateType (enumerator_type);
5816 init.MutateHoistedGenericType (storey);
5817 loop.MutateHoistedGenericType (storey);
5822 Expression variable;
5824 Statement statement;
5826 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5827 Statement stmt, Location l)
5830 this.variable = var;
5836 public Statement Statement {
5837 get { return statement; }
5840 public override bool Resolve (BlockContext ec)
5842 expr = expr.Resolve (ec);
5847 Report.Error (186, loc, "Use of null is not valid in this context");
5851 if (expr.Type == TypeManager.string_type) {
5852 statement = new ArrayForeach (this, 1);
5853 } else if (expr.Type.IsArray) {
5854 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5856 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5857 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5858 expr.ExprClassName);
5862 statement = new CollectionForeach (type, variable, expr, statement, loc);
5865 return statement.Resolve (ec);
5868 protected override void DoEmit (EmitContext ec)
5870 ILGenerator ig = ec.ig;
5872 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5873 ec.LoopBegin = ig.DefineLabel ();
5874 ec.LoopEnd = ig.DefineLabel ();
5876 statement.Emit (ec);
5878 ec.LoopBegin = old_begin;
5879 ec.LoopEnd = old_end;
5882 protected override void CloneTo (CloneContext clonectx, Statement t)
5884 Foreach target = (Foreach) t;
5886 target.type = type.Clone (clonectx);
5887 target.variable = variable.Clone (clonectx);
5888 target.expr = expr.Clone (clonectx);
5889 target.statement = statement.Clone (clonectx);
5892 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5894 statement.MutateHoistedGenericType (storey);