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 // (C) 2001, 2002, 2003 Ximian, Inc.
10 // (C) 2003, 2004 Novell, Inc.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections;
19 using System.Collections.Specialized;
21 namespace Mono.CSharp {
23 public abstract class Statement {
27 /// Resolves the statement, true means that all sub-statements
30 public virtual bool Resolve (EmitContext ec)
36 /// We already know that the statement is unreachable, but we still
37 /// need to resolve it to catch errors.
39 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
42 // This conflicts with csc's way of doing this, but IMHO it's
43 // the right thing to do.
45 // If something is unreachable, we still check whether it's
46 // correct. This means that you cannot use unassigned variables
47 // in unreachable code, for instance.
51 Report.Warning (162, 2, loc, "Unreachable code detected");
53 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
54 bool ok = Resolve (ec);
55 ec.KillFlowBranching ();
61 /// Return value indicates whether all code paths emitted return.
63 protected abstract void DoEmit (EmitContext ec);
66 /// Utility wrapper routine for Error, just to beautify the code
68 public void Error (int error, string format, params object[] args)
70 Error (error, String.Format (format, args));
73 public void Error (int error, string s)
76 Report.Error (error, loc, s);
78 Report.Error (error, s);
82 /// Return value indicates whether all code paths emitted return.
84 public virtual void Emit (EmitContext ec)
91 // This routine must be overrided in derived classes and make copies
92 // of all the data that might be modified if resolved
94 protected virtual void CloneTo (CloneContext clonectx, Statement target)
96 throw new InternalErrorException ("{0} does not implement Statement.CloneTo", this.GetType ());
99 public Statement Clone (CloneContext clonectx)
101 Statement s = (Statement) this.MemberwiseClone ();
102 CloneTo (clonectx, s);
106 public Statement PerformClone ()
108 CloneContext clonectx = new CloneContext ();
110 return Clone (clonectx);
116 // This class is used during the Statement.Clone operation
117 // to remap objects that have been cloned.
119 // Since blocks are cloned by Block.Clone, we need a way for
120 // expressions that must reference the block to be cloned
121 // pointing to the new cloned block.
123 public class CloneContext {
124 Hashtable block_map = new Hashtable ();
125 Hashtable variable_map;
127 public void AddBlockMap (Block from, Block to)
129 if (block_map.Contains (from))
131 block_map [from] = to;
134 public Block LookupBlock (Block from)
136 Block result = (Block) block_map [from];
139 result = (Block) from.Clone (this);
140 block_map [from] = result;
147 /// Remaps block to cloned copy if one exists.
149 public Block RemapBlockCopy (Block from)
151 Block mapped_to = (Block)block_map[from];
152 if (mapped_to == null)
158 public void AddVariableMap (LocalInfo from, LocalInfo to)
160 if (variable_map == null)
161 variable_map = new Hashtable ();
163 if (variable_map.Contains (from))
165 variable_map [from] = to;
168 public LocalInfo LookupVariable (LocalInfo from)
170 LocalInfo result = (LocalInfo) variable_map [from];
173 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
179 public sealed class EmptyStatement : Statement {
181 private EmptyStatement () {}
183 public static readonly EmptyStatement Value = new EmptyStatement ();
185 public override bool Resolve (EmitContext ec)
190 public override bool ResolveUnreachable (EmitContext ec, bool warn)
195 protected override void DoEmit (EmitContext ec)
199 protected override void CloneTo (CloneContext clonectx, Statement target)
205 public class If : Statement {
207 public Statement TrueStatement;
208 public Statement FalseStatement;
212 public If (Expression expr, Statement trueStatement, Location l)
215 TrueStatement = trueStatement;
219 public If (Expression expr,
220 Statement trueStatement,
221 Statement falseStatement,
225 TrueStatement = trueStatement;
226 FalseStatement = falseStatement;
230 public override bool Resolve (EmitContext ec)
234 Report.Debug (1, "START IF BLOCK", loc);
236 expr = Expression.ResolveBoolean (ec, expr, loc);
242 Assign ass = expr as Assign;
243 if (ass != null && ass.Source is Constant) {
244 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
248 // Dead code elimination
250 if (expr is BoolConstant){
251 bool take = ((BoolConstant) expr).Value;
254 if (!TrueStatement.Resolve (ec))
257 if ((FalseStatement != null) &&
258 !FalseStatement.ResolveUnreachable (ec, true))
260 FalseStatement = null;
262 if (!TrueStatement.ResolveUnreachable (ec, true))
264 TrueStatement = null;
266 if ((FalseStatement != null) &&
267 !FalseStatement.Resolve (ec))
274 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
276 ok &= TrueStatement.Resolve (ec);
278 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
280 ec.CurrentBranching.CreateSibling ();
282 if (FalseStatement != null)
283 ok &= FalseStatement.Resolve (ec);
285 ec.EndFlowBranching ();
287 Report.Debug (1, "END IF BLOCK", loc);
292 protected override void DoEmit (EmitContext ec)
294 ILGenerator ig = ec.ig;
295 Label false_target = ig.DefineLabel ();
299 // If we're a boolean expression, Resolve() already
300 // eliminated dead code for us.
302 if (expr is BoolConstant){
303 bool take = ((BoolConstant) expr).Value;
306 TrueStatement.Emit (ec);
307 else if (FalseStatement != null)
308 FalseStatement.Emit (ec);
313 expr.EmitBranchable (ec, false_target, false);
315 TrueStatement.Emit (ec);
317 if (FalseStatement != null){
318 bool branch_emitted = false;
320 end = ig.DefineLabel ();
322 ig.Emit (OpCodes.Br, end);
323 branch_emitted = true;
326 ig.MarkLabel (false_target);
327 FalseStatement.Emit (ec);
332 ig.MarkLabel (false_target);
336 protected override void CloneTo (CloneContext clonectx, Statement t)
340 target.expr = expr.Clone (clonectx);
341 target.TrueStatement = TrueStatement.Clone (clonectx);
342 if (FalseStatement != null)
343 target.FalseStatement = FalseStatement.Clone (clonectx);
347 public class Do : Statement {
348 public Expression expr;
349 public Statement EmbeddedStatement;
352 public Do (Statement statement, Expression boolExpr, Location l)
355 EmbeddedStatement = statement;
359 public override bool Resolve (EmitContext ec)
363 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
365 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
367 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
368 if (!EmbeddedStatement.Resolve (ec))
370 ec.EndFlowBranching ();
372 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
373 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
375 expr = Expression.ResolveBoolean (ec, expr, loc);
378 else if (expr is BoolConstant){
379 bool res = ((BoolConstant) expr).Value;
385 ec.CurrentBranching.CurrentUsageVector.Goto ();
387 ec.EndFlowBranching ();
392 protected override void DoEmit (EmitContext ec)
394 ILGenerator ig = ec.ig;
395 Label loop = ig.DefineLabel ();
396 Label old_begin = ec.LoopBegin;
397 Label old_end = ec.LoopEnd;
399 ec.LoopBegin = ig.DefineLabel ();
400 ec.LoopEnd = ig.DefineLabel ();
403 EmbeddedStatement.Emit (ec);
404 ig.MarkLabel (ec.LoopBegin);
407 // Dead code elimination
409 if (expr is BoolConstant){
410 bool res = ((BoolConstant) expr).Value;
413 ec.ig.Emit (OpCodes.Br, loop);
415 expr.EmitBranchable (ec, loop, true);
417 ig.MarkLabel (ec.LoopEnd);
419 ec.LoopBegin = old_begin;
420 ec.LoopEnd = old_end;
423 protected override void CloneTo (CloneContext clonectx, Statement t)
427 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
428 target.expr = expr.Clone (clonectx);
432 public class While : Statement {
433 public Expression expr;
434 public Statement Statement;
435 bool infinite, empty;
437 public While (Expression boolExpr, Statement statement, Location l)
439 this.expr = boolExpr;
440 Statement = statement;
444 public override bool Resolve (EmitContext ec)
448 expr = Expression.ResolveBoolean (ec, expr, loc);
453 // Inform whether we are infinite or not
455 if (expr is BoolConstant){
456 BoolConstant bc = (BoolConstant) expr;
458 if (bc.Value == false){
459 if (!Statement.ResolveUnreachable (ec, true))
467 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
469 ec.CurrentBranching.CreateSibling ();
471 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
472 if (!Statement.Resolve (ec))
474 ec.EndFlowBranching ();
476 // There's no direct control flow from the end of the embedded statement to the end of the loop
477 ec.CurrentBranching.CurrentUsageVector.Goto ();
479 ec.EndFlowBranching ();
484 protected override void DoEmit (EmitContext ec)
489 ILGenerator ig = ec.ig;
490 Label old_begin = ec.LoopBegin;
491 Label old_end = ec.LoopEnd;
493 ec.LoopBegin = ig.DefineLabel ();
494 ec.LoopEnd = ig.DefineLabel ();
497 // Inform whether we are infinite or not
499 if (expr is BoolConstant){
500 ig.MarkLabel (ec.LoopBegin);
502 ig.Emit (OpCodes.Br, ec.LoopBegin);
505 // Inform that we are infinite (ie, `we return'), only
506 // if we do not `break' inside the code.
508 ig.MarkLabel (ec.LoopEnd);
510 Label while_loop = ig.DefineLabel ();
512 ig.Emit (OpCodes.Br, ec.LoopBegin);
513 ig.MarkLabel (while_loop);
517 ig.MarkLabel (ec.LoopBegin);
519 expr.EmitBranchable (ec, while_loop, true);
521 ig.MarkLabel (ec.LoopEnd);
524 ec.LoopBegin = old_begin;
525 ec.LoopEnd = old_end;
528 protected override void CloneTo (CloneContext clonectx, Statement t)
530 While target = (While) t;
532 target.expr = expr.Clone (clonectx);
533 target.Statement = Statement.Clone (clonectx);
537 public class For : Statement {
539 Statement InitStatement;
541 public Statement Statement;
542 bool infinite, empty;
544 public For (Statement initStatement,
550 InitStatement = initStatement;
552 Increment = increment;
553 Statement = statement;
557 public override bool Resolve (EmitContext ec)
561 if (InitStatement != null){
562 if (!InitStatement.Resolve (ec))
567 Test = Expression.ResolveBoolean (ec, Test, loc);
570 else if (Test is BoolConstant){
571 BoolConstant bc = (BoolConstant) Test;
573 if (bc.Value == false){
574 if (!Statement.ResolveUnreachable (ec, true))
576 if ((Increment != null) &&
577 !Increment.ResolveUnreachable (ec, false))
587 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
589 ec.CurrentBranching.CreateSibling ();
591 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
593 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
594 if (!Statement.Resolve (ec))
596 ec.EndFlowBranching ();
598 if (Increment != null){
599 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
600 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
603 if (!Increment.Resolve (ec))
608 // There's no direct control flow from the end of the embedded statement to the end of the loop
609 ec.CurrentBranching.CurrentUsageVector.Goto ();
611 ec.EndFlowBranching ();
616 protected override void DoEmit (EmitContext ec)
621 ILGenerator ig = ec.ig;
622 Label old_begin = ec.LoopBegin;
623 Label old_end = ec.LoopEnd;
624 Label loop = ig.DefineLabel ();
625 Label test = ig.DefineLabel ();
627 if (InitStatement != null && InitStatement != EmptyStatement.Value)
628 InitStatement.Emit (ec);
630 ec.LoopBegin = ig.DefineLabel ();
631 ec.LoopEnd = ig.DefineLabel ();
633 ig.Emit (OpCodes.Br, test);
637 ig.MarkLabel (ec.LoopBegin);
638 if (Increment != EmptyStatement.Value)
643 // If test is null, there is no test, and we are just
648 // The Resolve code already catches the case for
649 // Test == BoolConstant (false) so we know that
652 if (Test is BoolConstant)
653 ig.Emit (OpCodes.Br, loop);
655 Test.EmitBranchable (ec, loop, true);
658 ig.Emit (OpCodes.Br, loop);
659 ig.MarkLabel (ec.LoopEnd);
661 ec.LoopBegin = old_begin;
662 ec.LoopEnd = old_end;
665 protected override void CloneTo (CloneContext clonectx, Statement t)
667 For target = (For) t;
669 if (InitStatement != null)
670 target.InitStatement = InitStatement.Clone (clonectx);
672 target.Test = Test.Clone (clonectx);
673 if (Increment != null)
674 target.Increment = Increment.Clone (clonectx);
675 target.Statement = Statement.Clone (clonectx);
679 public class StatementExpression : Statement {
680 ExpressionStatement expr;
682 public StatementExpression (ExpressionStatement expr)
688 public override bool Resolve (EmitContext ec)
691 expr = expr.ResolveStatement (ec);
695 protected override void DoEmit (EmitContext ec)
697 expr.EmitStatement (ec);
700 public override string ToString ()
702 return "StatementExpression (" + expr + ")";
705 protected override void CloneTo (CloneContext clonectx, Statement t)
707 StatementExpression target = (StatementExpression) t;
709 target.expr = (ExpressionStatement) expr.Clone (clonectx);
714 /// Implements the return statement
716 public class Return : Statement {
720 public Return (Expression expr, Location l)
726 bool DoResolve (EmitContext ec)
729 if (ec.ReturnType == TypeManager.void_type)
732 Error (126, "An object of a type convertible to `{0}' is required " +
733 "for the return statement",
734 TypeManager.CSharpName (ec.ReturnType));
738 AnonymousContainer am = ec.CurrentAnonymousMethod;
739 if ((am != null) && am.IsIterator && ec.InIterator) {
740 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
741 "statement to return a value, or yield break to end the iteration");
744 if (am == null && ec.ReturnType == TypeManager.void_type) {
745 MemberCore mc = ec.ResolveContext as MemberCore;
746 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
747 mc.GetSignatureForError ());
750 Expr = Expr.Resolve (ec);
754 if (Expr.Type != ec.ReturnType) {
755 if (ec.InferReturnType) {
756 ec.ReturnType = Expr.Type;
758 Expr = Convert.ImplicitConversionRequired (
759 ec, Expr, ec.ReturnType, loc);
763 Report.Error (1662, loc,
764 "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",
765 am.ContainerType, am.GetSignatureForError ());
775 public override bool Resolve (EmitContext ec)
780 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
782 ec.NeedReturnLabel ();
783 ec.CurrentBranching.CurrentUsageVector.Goto ();
787 protected override void DoEmit (EmitContext ec)
793 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
797 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
799 ec.ig.Emit (OpCodes.Ret);
802 protected override void CloneTo (CloneContext clonectx, Statement t)
804 Return target = (Return) t;
805 // It's null for simple return;
807 target.Expr = Expr.Clone (clonectx);
811 public class Goto : Statement {
813 LabeledStatement label;
816 public override bool Resolve (EmitContext ec)
818 int errors = Report.Errors;
819 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
820 ec.CurrentBranching.CurrentUsageVector.Goto ();
821 return errors == Report.Errors;
824 public Goto (string label, Location l)
830 public string Target {
831 get { return target; }
834 public void SetResolvedTarget (LabeledStatement label)
837 label.AddReference ();
840 protected override void DoEmit (EmitContext ec)
843 throw new InternalErrorException ("goto emitted before target resolved");
844 Label l = label.LabelTarget (ec);
845 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
849 public class LabeledStatement : Statement {
856 FlowBranching.UsageVector vectors;
858 public LabeledStatement (string name, Location l)
864 public Label LabelTarget (EmitContext ec)
869 label = ec.ig.DefineLabel ();
879 public bool IsDefined {
880 get { return defined; }
883 public bool HasBeenReferenced {
884 get { return referenced; }
887 public FlowBranching.UsageVector JumpOrigins {
888 get { return vectors; }
891 public void AddUsageVector (FlowBranching.UsageVector vector)
893 vector = vector.Clone ();
894 vector.Next = vectors;
898 public override bool Resolve (EmitContext ec)
900 // this flow-branching will be terminated when the surrounding block ends
901 ec.StartFlowBranching (this);
905 protected override void DoEmit (EmitContext ec)
907 if (ig != null && ig != ec.ig)
908 throw new InternalErrorException ("cannot happen");
910 ec.ig.MarkLabel (label);
913 public void AddReference ()
921 /// `goto default' statement
923 public class GotoDefault : Statement {
925 public GotoDefault (Location l)
930 public override bool Resolve (EmitContext ec)
932 ec.CurrentBranching.CurrentUsageVector.Goto ();
936 protected override void DoEmit (EmitContext ec)
938 if (ec.Switch == null){
939 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
943 if (!ec.Switch.GotDefault){
944 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
947 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
952 /// `goto case' statement
954 public class GotoCase : Statement {
958 public GotoCase (Expression e, Location l)
964 public override bool Resolve (EmitContext ec)
966 if (ec.Switch == null){
967 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
971 expr = expr.Resolve (ec);
975 Constant c = expr as Constant;
977 Error (150, "A constant value is expected");
981 Type type = ec.Switch.SwitchType;
982 if (!Convert.ImplicitStandardConversionExists (c, type))
983 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
984 "convertible to type `{0}'", TypeManager.CSharpName (type));
987 object val = c.GetValue ();
988 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
989 val = TypeManager.ChangeType (val, type, out fail);
992 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
993 c.GetSignatureForError (), TypeManager.CSharpName (type));
998 val = SwitchLabel.NullStringCase;
1000 sl = (SwitchLabel) ec.Switch.Elements [val];
1003 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1004 (c.GetValue () == null ? "null" : val.ToString ()));
1008 ec.CurrentBranching.CurrentUsageVector.Goto ();
1012 protected override void DoEmit (EmitContext ec)
1014 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1017 protected override void CloneTo (CloneContext clonectx, Statement t)
1019 GotoCase target = (GotoCase) t;
1021 target.expr = expr.Clone (clonectx);
1022 target.sl = sl.Clone (clonectx);
1026 public class Throw : Statement {
1029 public Throw (Expression expr, Location l)
1035 public override bool Resolve (EmitContext ec)
1037 ec.CurrentBranching.CurrentUsageVector.Goto ();
1040 expr = expr.Resolve (ec);
1044 ExprClass eclass = expr.eclass;
1046 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1047 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1048 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1054 if ((t != TypeManager.exception_type) &&
1055 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1056 !(expr is NullLiteral)) {
1058 "The type caught or thrown must be derived " +
1059 "from System.Exception");
1066 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
1071 Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1077 protected override void DoEmit (EmitContext ec)
1080 ec.ig.Emit (OpCodes.Rethrow);
1084 ec.ig.Emit (OpCodes.Throw);
1088 protected override void CloneTo (CloneContext clonectx, Statement t)
1090 Throw target = (Throw) t;
1092 target.expr = expr.Clone (clonectx);
1096 public class Break : Statement {
1098 public Break (Location l)
1103 bool unwind_protect;
1105 public override bool Resolve (EmitContext ec)
1107 int errors = Report.Errors;
1108 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1109 ec.CurrentBranching.CurrentUsageVector.Goto ();
1110 return errors == Report.Errors;
1113 protected override void DoEmit (EmitContext ec)
1115 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1118 protected override void CloneTo (CloneContext clonectx, Statement t)
1124 public class Continue : Statement {
1126 public Continue (Location l)
1131 bool unwind_protect;
1133 public override bool Resolve (EmitContext ec)
1135 int errors = Report.Errors;
1136 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1137 ec.CurrentBranching.CurrentUsageVector.Goto ();
1138 return errors == Report.Errors;
1141 protected override void DoEmit (EmitContext ec)
1143 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1146 protected override void CloneTo (CloneContext clonectx, Statement t)
1152 public abstract class Variable
1154 public abstract Type Type {
1158 public abstract bool HasInstance {
1162 public abstract bool NeedsTemporary {
1166 public abstract void EmitInstance (EmitContext ec);
1168 public abstract void Emit (EmitContext ec);
1170 public abstract void EmitAssign (EmitContext ec);
1172 public abstract void EmitAddressOf (EmitContext ec);
1175 public interface IKnownVariable {
1176 Block Block { get; }
1177 Location Location { get; }
1181 // The information about a user-perceived local variable
1183 public class LocalInfo : IKnownVariable {
1184 public Expression Type;
1186 public Type VariableType;
1187 public readonly string Name;
1188 public readonly Location Location;
1189 public readonly Block Block;
1191 public VariableInfo VariableInfo;
1194 public Variable Variable {
1206 CompilerGenerated = 64,
1210 public enum ReadOnlyContext: byte {
1217 ReadOnlyContext ro_context;
1218 LocalBuilder builder;
1220 public LocalInfo (Expression type, string name, Block block, Location l)
1228 public LocalInfo (DeclSpace ds, Block block, Location l)
1230 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1235 public void ResolveVariable (EmitContext ec)
1237 Block theblock = Block;
1238 if (theblock.ScopeInfo != null)
1239 var = theblock.ScopeInfo.GetCapturedVariable (this);
1244 // This is needed to compile on both .NET 1.x and .NET 2.x
1245 // the later introduced `DeclareLocal (Type t, bool pinned)'
1247 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1249 builder = ec.ig.DeclareLocal (VariableType);
1251 var = new LocalVariable (this, builder);
1255 public void EmitSymbolInfo (EmitContext ec, string name)
1257 if (builder != null)
1258 ec.DefineLocalVariable (name, builder);
1261 public bool IsThisAssigned (EmitContext ec)
1263 if (VariableInfo == null)
1264 throw new Exception ();
1266 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1269 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1272 public bool IsAssigned (EmitContext ec)
1274 if (VariableInfo == null)
1275 throw new Exception ();
1277 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1280 public bool Resolve (EmitContext ec)
1282 if (VariableType == null) {
1283 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1287 VariableType = texpr.Type;
1290 if (TypeManager.IsGenericParameter (VariableType))
1293 if (VariableType == TypeManager.void_type) {
1294 Expression.Error_VoidInvalidInTheContext (Location);
1298 if (VariableType.IsAbstract && VariableType.IsSealed) {
1299 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1303 if (VariableType.IsPointer && !ec.InUnsafe)
1304 Expression.UnsafeError (Location);
1309 public bool IsCaptured {
1310 get { return (flags & Flags.Captured) != 0; }
1311 set { flags |= Flags.Captured; }
1314 public bool IsConstant {
1315 get { return (flags & Flags.IsConstant) != 0; }
1316 set { flags |= Flags.IsConstant; }
1319 public bool AddressTaken {
1320 get { return (flags & Flags.AddressTaken) != 0; }
1321 set { flags |= Flags.AddressTaken; }
1324 public bool CompilerGenerated {
1325 get { return (flags & Flags.CompilerGenerated) != 0; }
1326 set { flags |= Flags.CompilerGenerated; }
1329 public override string ToString ()
1331 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1332 Name, Type, VariableInfo, Location);
1336 get { return (flags & Flags.Used) != 0; }
1337 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1340 public bool ReadOnly {
1341 get { return (flags & Flags.ReadOnly) != 0; }
1344 public void SetReadOnlyContext (ReadOnlyContext context)
1346 flags |= Flags.ReadOnly;
1347 ro_context = context;
1350 public string GetReadOnlyContext ()
1353 throw new InternalErrorException ("Variable is not readonly");
1355 switch (ro_context) {
1356 case ReadOnlyContext.Fixed:
1357 return "fixed variable";
1358 case ReadOnlyContext.Foreach:
1359 return "foreach iteration variable";
1360 case ReadOnlyContext.Using:
1361 return "using variable";
1363 throw new NotImplementedException ();
1367 // Whether the variable is pinned, if Pinned the variable has been
1368 // allocated in a pinned slot with DeclareLocal.
1370 public bool Pinned {
1371 get { return (flags & Flags.Pinned) != 0; }
1372 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1375 public bool IsThis {
1376 get { return (flags & Flags.IsThis) != 0; }
1377 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1380 Block IKnownVariable.Block {
1381 get { return Block; }
1384 Location IKnownVariable.Location {
1385 get { return Location; }
1388 protected class LocalVariable : Variable
1390 public readonly LocalInfo LocalInfo;
1391 LocalBuilder builder;
1393 public LocalVariable (LocalInfo local, LocalBuilder builder)
1395 this.LocalInfo = local;
1396 this.builder = builder;
1399 public override Type Type {
1400 get { return LocalInfo.VariableType; }
1403 public override bool HasInstance {
1404 get { return false; }
1407 public override bool NeedsTemporary {
1408 get { return false; }
1411 public override void EmitInstance (EmitContext ec)
1416 public override void Emit (EmitContext ec)
1418 ec.ig.Emit (OpCodes.Ldloc, builder);
1421 public override void EmitAssign (EmitContext ec)
1423 ec.ig.Emit (OpCodes.Stloc, builder);
1426 public override void EmitAddressOf (EmitContext ec)
1428 ec.ig.Emit (OpCodes.Ldloca, builder);
1432 public LocalInfo Clone (CloneContext clonectx)
1435 // Variables in anonymous block are not resolved yet
1437 if (VariableType == null)
1438 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1441 // Variables in method block are resolved
1443 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1444 li.VariableType = VariableType;
1450 /// Block represents a C# block.
1454 /// This class is used in a number of places: either to represent
1455 /// explicit blocks that the programmer places or implicit blocks.
1457 /// Implicit blocks are used as labels or to introduce variable
1460 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1461 /// they contain extra information that is not necessary on normal blocks.
1463 public class Block : Statement {
1464 public Block Parent;
1465 public readonly Location StartLocation;
1466 public Location EndLocation = Location.Null;
1468 public ExplicitBlock Explicit;
1469 public ToplevelBlock Toplevel;
1472 public enum Flags : byte {
1475 VariablesInitialized = 4,
1479 HasVarargs = 64, // Used in ToplevelBlock
1482 protected Flags flags;
1484 public bool Unchecked {
1485 get { return (flags & Flags.Unchecked) != 0; }
1486 set { flags |= Flags.Unchecked; }
1489 public bool Unsafe {
1490 get { return (flags & Flags.Unsafe) != 0; }
1491 set { flags |= Flags.Unsafe; }
1495 // The statements in this block
1497 protected ArrayList statements;
1501 // An array of Blocks. We keep track of children just
1502 // to generate the local variable declarations.
1504 // Statements and child statements are handled through the
1510 // Labels. (label, block) pairs.
1515 // Keeps track of (name, type) pairs
1517 IDictionary variables;
1520 // Keeps track of constants
1521 Hashtable constants;
1524 // Temporary variables.
1526 ArrayList temporary_variables;
1529 // If this is a switch section, the enclosing switch block.
1533 ExpressionStatement scope_init;
1535 ArrayList anonymous_children;
1537 protected static int id;
1541 int assignable_slots;
1542 protected ScopeInfo scope_info;
1543 bool unreachable_shown;
1546 public Block (Block parent)
1547 : this (parent, (Flags) 0, Location.Null, Location.Null)
1550 public Block (Block parent, Flags flags)
1551 : this (parent, flags, Location.Null, Location.Null)
1554 public Block (Block parent, Location start, Location end)
1555 : this (parent, (Flags) 0, start, end)
1558 public Block (Block parent, Flags flags, Location start, Location end)
1560 if (parent != null) {
1561 parent.AddChild (this);
1563 // the appropriate constructors will fixup these fields
1564 Toplevel = parent.Toplevel;
1565 Explicit = parent.Explicit;
1568 this.Parent = parent;
1570 this.StartLocation = start;
1571 this.EndLocation = end;
1574 statements = new ArrayList ();
1577 public Block CreateSwitchBlock (Location start)
1579 // FIXME: should this be implicit?
1580 Block new_block = new ExplicitBlock (this, start, start);
1581 new_block.switch_block = this;
1586 get { return this_id; }
1589 public IDictionary Variables {
1591 if (variables == null)
1592 variables = new ListDictionary ();
1597 void AddChild (Block b)
1599 if (children == null)
1600 children = new ArrayList ();
1605 public void SetEndLocation (Location loc)
1610 protected static void Error_158 (string name, Location loc)
1612 Report.Error (158, loc, "The label `{0}' shadows another label " +
1613 "by the same name in a contained scope", name);
1617 /// Adds a label to the current block.
1621 /// false if the name already exists in this block. true
1625 public bool AddLabel (LabeledStatement target)
1627 if (switch_block != null)
1628 return switch_block.AddLabel (target);
1630 string name = target.Name;
1633 while (cur != null) {
1634 LabeledStatement s = cur.DoLookupLabel (name);
1636 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1637 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1641 if (this == Explicit)
1647 while (cur != null) {
1648 if (cur.DoLookupLabel (name) != null) {
1649 Error_158 (name, target.loc);
1653 if (children != null) {
1654 foreach (Block b in children) {
1655 LabeledStatement s = b.DoLookupLabel (name);
1659 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1660 Error_158 (name, target.loc);
1668 Toplevel.CheckError158 (name, target.loc);
1671 labels = new Hashtable ();
1673 labels.Add (name, target);
1677 public LabeledStatement LookupLabel (string name)
1679 LabeledStatement s = DoLookupLabel (name);
1683 if (children == null)
1686 foreach (Block child in children) {
1687 if (Explicit != child.Explicit)
1690 s = child.LookupLabel (name);
1698 LabeledStatement DoLookupLabel (string name)
1700 if (switch_block != null)
1701 return switch_block.LookupLabel (name);
1704 if (labels.Contains (name))
1705 return ((LabeledStatement) labels [name]);
1710 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1713 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1714 while (kvi == null) {
1715 b = b.Explicit.Parent;
1718 kvi = b.Explicit.GetKnownVariable (name);
1724 // Is kvi.Block nested inside 'b'
1725 if (b.Explicit != kvi.Block.Explicit) {
1727 // If a variable by the same name it defined in a nested block of this
1728 // block, we violate the invariant meaning in a block.
1731 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1732 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1737 // It's ok if the definition is in a nested subblock of b, but not
1738 // nested inside this block -- a definition in a sibling block
1739 // should not affect us.
1745 // Block 'b' and kvi.Block are the same textual block.
1746 // However, different variables are extant.
1748 // Check if the variable is in scope in both blocks. We use
1749 // an indirect check that depends on AddVariable doing its
1750 // part in maintaining the invariant-meaning-in-block property.
1752 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1756 // Even though we detected the error when the name is used, we
1757 // treat it as if the variable declaration was in error.
1759 Report.SymbolRelatedToPreviousError (loc, name);
1760 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1764 public LocalInfo AddVariable (Expression type, string name, Location l)
1766 LocalInfo vi = GetLocalInfo (name);
1768 Report.SymbolRelatedToPreviousError (vi.Location, name);
1769 if (Explicit == vi.Block.Explicit)
1770 Error_AlreadyDeclared (l, name, null);
1772 Error_AlreadyDeclared (l, name, "parent");
1776 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1778 Report.SymbolRelatedToPreviousError (pi.Location, name);
1779 Error_AlreadyDeclared (loc, name,
1780 pi.Block == Toplevel ? "method argument" : "parent or current");
1784 if (Toplevel.GenericMethod != null) {
1785 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1786 if (tp.Name == name) {
1787 Report.SymbolRelatedToPreviousError (tp);
1788 Error_AlreadyDeclaredTypeParameter (loc, name);
1794 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1796 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1797 Error_AlreadyDeclared (l, name, "child");
1801 vi = new LocalInfo (type, name, this, l);
1804 if ((flags & Flags.VariablesInitialized) != 0)
1805 throw new InternalErrorException ("block has already been resolved");
1810 protected virtual void AddVariable (LocalInfo li)
1812 Variables.Add (li.Name, li);
1813 Explicit.AddKnownVariable (li.Name, li);
1816 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1818 if (reason == null) {
1819 Error_AlreadyDeclared (loc, var);
1823 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1824 "in this scope because it would give a different meaning " +
1825 "to `{0}', which is already used in a `{1}' scope " +
1826 "to denote something else", var, reason);
1829 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1831 Report.Error (128, loc,
1832 "A local variable named `{0}' is already defined in this scope", name);
1835 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1837 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1840 public bool AddConstant (Expression type, string name, Expression value, Location l)
1842 if (AddVariable (type, name, l) == null)
1845 if (constants == null)
1846 constants = new Hashtable ();
1848 constants.Add (name, value);
1850 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1855 static int next_temp_id = 0;
1857 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1859 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1861 if (temporary_variables == null)
1862 temporary_variables = new ArrayList ();
1864 int id = ++next_temp_id;
1865 string name = "$s_" + id.ToString ();
1867 LocalInfo li = new LocalInfo (te, name, this, loc);
1868 li.CompilerGenerated = true;
1869 temporary_variables.Add (li);
1873 public LocalInfo GetLocalInfo (string name)
1875 for (Block b = this; b != null; b = b.Parent) {
1876 if (b.variables != null) {
1877 LocalInfo ret = b.variables [name] as LocalInfo;
1885 public Expression GetVariableType (string name)
1887 LocalInfo vi = GetLocalInfo (name);
1888 return vi == null ? null : vi.Type;
1891 public Expression GetConstantExpression (string name)
1893 for (Block b = this; b != null; b = b.Parent) {
1894 if (b.constants != null) {
1895 Expression ret = b.constants [name] as Expression;
1903 public void AddStatement (Statement s)
1906 flags |= Flags.BlockUsed;
1910 get { return (flags & Flags.BlockUsed) != 0; }
1915 flags |= Flags.BlockUsed;
1918 public bool HasRet {
1919 get { return (flags & Flags.HasRet) != 0; }
1922 public bool IsDestructor {
1923 get { return (flags & Flags.IsDestructor) != 0; }
1926 public void SetDestructor ()
1928 flags |= Flags.IsDestructor;
1931 public int AssignableSlots {
1933 if ((flags & Flags.VariablesInitialized) == 0)
1934 throw new Exception ("Variables have not been initialized yet");
1935 return assignable_slots;
1939 public ScopeInfo ScopeInfo {
1940 get { return scope_info; }
1943 public ScopeInfo CreateScopeInfo ()
1945 if (scope_info == null)
1946 scope_info = ScopeInfo.CreateScope (this);
1951 public ArrayList AnonymousChildren {
1952 get { return anonymous_children; }
1955 public void AddAnonymousChild (ToplevelBlock b)
1957 if (anonymous_children == null)
1958 anonymous_children = new ArrayList ();
1960 anonymous_children.Add (b);
1963 void DoResolveConstants (EmitContext ec)
1965 if (constants == null)
1968 if (variables == null)
1969 throw new InternalErrorException ("cannot happen");
1971 foreach (DictionaryEntry de in variables) {
1972 string name = (string) de.Key;
1973 LocalInfo vi = (LocalInfo) de.Value;
1974 Type variable_type = vi.VariableType;
1976 if (variable_type == null)
1979 Expression cv = (Expression) constants [name];
1983 // Don't let 'const int Foo = Foo;' succeed.
1984 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1985 // which in turn causes the 'must be constant' error to be triggered.
1986 constants.Remove (name);
1988 if (!Const.IsConstantTypeValid (variable_type)) {
1989 Const.Error_InvalidConstantType (variable_type, loc);
1993 ec.CurrentBlock = this;
1995 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1996 e = cv.Resolve (ec);
2001 Constant ce = e as Constant;
2003 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2007 e = ce.ConvertImplicitly (variable_type);
2009 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2010 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2012 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2016 constants.Add (name, e);
2017 vi.IsConstant = true;
2021 protected void ResolveMeta (EmitContext ec, int offset)
2023 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2025 // If some parent block was unsafe, we remain unsafe even if this block
2026 // isn't explicitly marked as such.
2027 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2028 flags |= Flags.VariablesInitialized;
2030 if (variables != null) {
2031 foreach (LocalInfo li in variables.Values) {
2032 if (!li.Resolve (ec))
2034 li.VariableInfo = new VariableInfo (li, offset);
2035 offset += li.VariableInfo.Length;
2038 assignable_slots = offset;
2040 DoResolveConstants (ec);
2042 if (children == null)
2044 foreach (Block b in children)
2045 b.ResolveMeta (ec, offset);
2050 // Emits the local variable declarations for a block
2052 public virtual void EmitMeta (EmitContext ec)
2054 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2055 if (ScopeInfo != null) {
2056 scope_init = ScopeInfo.GetScopeInitializer (ec);
2057 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2061 if (variables != null){
2062 foreach (LocalInfo vi in variables.Values)
2063 vi.ResolveVariable (ec);
2066 if (temporary_variables != null) {
2067 foreach (LocalInfo vi in temporary_variables)
2068 vi.ResolveVariable (ec);
2071 if (children != null){
2072 foreach (Block b in children)
2077 void UsageWarning (FlowBranching.UsageVector vector)
2081 if ((variables != null) && (Report.WarningLevel >= 3)) {
2082 foreach (DictionaryEntry de in variables){
2083 LocalInfo vi = (LocalInfo) de.Value;
2088 name = (string) de.Key;
2090 // vi.VariableInfo can be null for 'catch' variables
2091 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2092 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2094 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2100 private void CheckPossibleMistakenEmptyStatement (Statement s)
2104 // Some statements are wrapped by a Block. Since
2105 // others' internal could be changed, here I treat
2106 // them as possibly wrapped by Block equally.
2107 Block b = s as Block;
2108 if (b != null && b.statements.Count == 1)
2109 s = (Statement) b.statements [0];
2112 body = ((Lock) s).Statement;
2114 body = ((For) s).Statement;
2115 else if (s is Foreach)
2116 body = ((Foreach) s).Statement;
2117 else if (s is While)
2118 body = ((While) s).Statement;
2119 else if (s is Using)
2120 body = ((Using) s).Statement;
2121 else if (s is Fixed)
2122 body = ((Fixed) s).Statement;
2126 if (body == null || body is EmptyStatement)
2127 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2130 public override bool Resolve (EmitContext ec)
2132 Block prev_block = ec.CurrentBlock;
2135 int errors = Report.Errors;
2137 ec.CurrentBlock = this;
2138 ec.StartFlowBranching (this);
2140 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2143 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2144 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2145 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2146 // responsible for handling the situation.
2148 int statement_count = statements.Count;
2149 for (int ix = 0; ix < statement_count; ix++){
2150 Statement s = (Statement) statements [ix];
2151 // Check possible empty statement (CS0642)
2152 if (Report.WarningLevel >= 3 &&
2153 ix + 1 < statement_count &&
2154 statements [ix + 1] is Block)
2155 CheckPossibleMistakenEmptyStatement (s);
2158 // Warn if we detect unreachable code.
2161 if (s is EmptyStatement)
2165 ((Block) s).unreachable = true;
2167 if (!unreachable_shown && !(s is LabeledStatement)) {
2168 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2169 unreachable_shown = true;
2174 // Note that we're not using ResolveUnreachable() for unreachable
2175 // statements here. ResolveUnreachable() creates a temporary
2176 // flow branching and kills it afterwards. This leads to problems
2177 // if you have two unreachable statements where the first one
2178 // assigns a variable and the second one tries to access it.
2181 if (!s.Resolve (ec)) {
2182 if (ec.IsInProbingMode)
2186 statements [ix] = EmptyStatement.Value;
2190 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2191 statements [ix] = EmptyStatement.Value;
2193 num_statements = ix + 1;
2195 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2196 if (unreachable && s is LabeledStatement)
2197 throw new InternalErrorException ("should not happen");
2200 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2201 ec.CurrentBranching, statement_count, num_statements);
2206 while (ec.CurrentBranching is FlowBranchingLabeled)
2207 ec.EndFlowBranching ();
2209 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2211 ec.CurrentBlock = prev_block;
2213 // If we're a non-static `struct' constructor which doesn't have an
2214 // initializer, then we must initialize all of the struct's fields.
2215 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2218 if ((labels != null) && (Report.WarningLevel >= 2)) {
2219 foreach (LabeledStatement label in labels.Values)
2220 if (!label.HasBeenReferenced)
2221 Report.Warning (164, 2, label.loc,
2222 "This label has not been referenced");
2225 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2227 if (vector.IsUnreachable)
2228 flags |= Flags.HasRet;
2230 if (ok && (errors == Report.Errors)) {
2231 UsageWarning (vector);
2237 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2239 unreachable_shown = true;
2243 Report.Warning (162, 2, loc, "Unreachable code detected");
2245 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2246 bool ok = Resolve (ec);
2247 ec.KillFlowBranching ();
2252 protected override void DoEmit (EmitContext ec)
2254 for (int ix = 0; ix < num_statements; ix++){
2255 Statement s = (Statement) statements [ix];
2260 public override void Emit (EmitContext ec)
2262 Block prev_block = ec.CurrentBlock;
2264 ec.CurrentBlock = this;
2266 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2267 bool is_lexical_block = this == Explicit && Parent != null;
2269 if (emit_debug_info) {
2270 if (is_lexical_block)
2273 ec.Mark (StartLocation, true);
2274 if (scope_init != null)
2275 scope_init.EmitStatement (ec);
2277 ec.Mark (EndLocation, true);
2279 if (emit_debug_info) {
2280 if (is_lexical_block)
2283 if (variables != null) {
2284 foreach (DictionaryEntry de in variables) {
2285 string name = (string) de.Key;
2286 LocalInfo vi = (LocalInfo) de.Value;
2288 vi.EmitSymbolInfo (ec, name);
2293 ec.CurrentBlock = prev_block;
2296 public override string ToString ()
2298 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2301 protected override void CloneTo (CloneContext clonectx, Statement t)
2303 Block target = (Block) t;
2305 clonectx.AddBlockMap (this, target);
2307 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2308 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2310 target.Parent = clonectx.RemapBlockCopy (Parent);
2312 if (variables != null){
2313 target.variables = new Hashtable ();
2315 foreach (DictionaryEntry de in variables){
2316 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2317 target.variables [de.Key] = newlocal;
2318 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2322 target.statements = new ArrayList (statements.Count);
2323 foreach (Statement s in statements)
2324 target.statements.Add (s.Clone (clonectx));
2326 if (target.children != null){
2327 target.children = new ArrayList (children.Count);
2328 foreach (Block b in children){
2329 target.children.Add (clonectx.LookupBlock (b));
2334 // TODO: labels, switch_block, constants (?), anonymous_children
2339 public class ExplicitBlock : Block {
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;
2351 Hashtable known_variables;
2354 // Marks a variable with name @name as being used in this or a child block.
2355 // If a variable name has been used in a child block, it's illegal to
2356 // declare a variable with the same name in the current block.
2358 internal void AddKnownVariable (string name, IKnownVariable info)
2360 if (known_variables == null)
2361 known_variables = new Hashtable ();
2363 known_variables [name] = info;
2366 Parent.Explicit.AddKnownVariable (name, info);
2369 internal IKnownVariable GetKnownVariable (string name)
2371 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2374 protected override void CloneTo (CloneContext clonectx, Statement t)
2376 ExplicitBlock target = (ExplicitBlock) t;
2377 target.known_variables = null;
2378 base.CloneTo (clonectx, t);
2382 public class ToplevelParameterInfo : IKnownVariable {
2383 public readonly ToplevelBlock Block;
2384 public readonly int Index;
2385 public VariableInfo VariableInfo;
2387 Block IKnownVariable.Block {
2388 get { return Block; }
2390 public Parameter Parameter {
2391 get { return Block.Parameters [Index]; }
2393 public Location Location {
2394 get { return Parameter.Location; }
2397 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2405 // A toplevel block contains extra information, the split is done
2406 // only to separate information that would otherwise bloat the more
2407 // lightweight Block.
2409 // In particular, this was introduced when the support for Anonymous
2410 // Methods was implemented.
2412 public class ToplevelBlock : ExplicitBlock {
2413 GenericMethod generic;
2414 FlowBranchingToplevel top_level_branching;
2415 AnonymousContainer anonymous_container;
2416 RootScopeInfo root_scope;
2417 Parameters parameters;
2418 ToplevelParameterInfo[] parameter_info;
2420 public bool HasVarargs {
2421 get { return (flags & Flags.HasVarargs) != 0; }
2422 set { flags |= Flags.HasVarargs; }
2425 public bool IsIterator {
2426 get { return (flags & Flags.IsIterator) != 0; }
2430 // The parameters for the block.
2432 public Parameters Parameters {
2433 get { return parameters; }
2436 public bool CompleteContexts (EmitContext ec)
2438 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2440 if (root_scope != null)
2441 root_scope.LinkScopes ();
2443 if (Parent == null && root_scope != null) {
2444 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2446 if (root_scope.DefineType () == null)
2448 if (!root_scope.ResolveType ())
2450 if (!root_scope.ResolveMembers ())
2452 if (!root_scope.DefineMembers ())
2459 public GenericMethod GenericMethod {
2460 get { return generic; }
2463 public ToplevelBlock Container {
2464 get { return Parent == null ? null : Parent.Toplevel; }
2467 public AnonymousContainer AnonymousContainer {
2468 get { return anonymous_container; }
2469 set { anonymous_container = value; }
2472 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2473 this (parent, (Flags) 0, parameters, start)
2477 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2478 this (parent, parameters, start)
2480 this.generic = generic;
2483 public ToplevelBlock (Parameters parameters, Location start) :
2484 this (null, (Flags) 0, parameters, start)
2488 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2489 this (null, flags, parameters, start)
2493 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2494 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2495 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2496 base (null, flags, start, Location.Null)
2498 this.Toplevel = this;
2500 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2501 this.Parent = parent;
2503 parent.AddAnonymousChild (this);
2505 if (this.parameters.Count != 0)
2506 ProcessParameters ();
2509 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2513 protected override void CloneTo (CloneContext clonectx, Statement t)
2515 ToplevelBlock target = (ToplevelBlock) t;
2516 base.CloneTo (clonectx, t);
2518 if (parameters.Count != 0)
2519 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2520 for (int i = 0; i < parameters.Count; ++i)
2521 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2524 public bool CheckError158 (string name, Location loc)
2526 if (AnonymousChildren != null) {
2527 foreach (ToplevelBlock child in AnonymousChildren) {
2528 if (!child.CheckError158 (name, loc))
2533 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2534 if (!c.DoCheckError158 (name, loc))
2541 public virtual Expression GetTransparentIdentifier (string name)
2546 void ProcessParameters ()
2548 int n = parameters.Count;
2549 parameter_info = new ToplevelParameterInfo [n];
2550 for (int i = 0; i < n; ++i) {
2551 parameter_info [i] = new ToplevelParameterInfo (this, i);
2553 Parameter p = parameters [i];
2557 string name = p.Name;
2558 LocalInfo vi = GetLocalInfo (name);
2560 Report.SymbolRelatedToPreviousError (vi.Location, name);
2561 Error_AlreadyDeclared (loc, name, "parent or current");
2565 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2567 Report.SymbolRelatedToPreviousError (pi.Location, name);
2568 Error_AlreadyDeclared (loc, name, "parent or current");
2572 AddKnownVariable (name, parameter_info [i]);
2575 // mark this block as "used" so that we create local declarations in a sub-block
2576 // FIXME: This appears to uncover a lot of bugs
2580 bool DoCheckError158 (string name, Location loc)
2582 LabeledStatement s = LookupLabel (name);
2584 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2585 Error_158 (name, loc);
2592 public RootScopeInfo CreateRootScope (TypeContainer host)
2594 if (root_scope != null)
2597 if (Container == null)
2598 root_scope = new RootScopeInfo (
2599 this, host, generic, StartLocation);
2601 if (scope_info != null)
2602 throw new InternalErrorException ();
2604 scope_info = root_scope;
2608 public void CreateIteratorHost (RootScopeInfo root)
2610 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2612 if (Parent != null || root_scope != null)
2613 throw new InternalErrorException ();
2615 scope_info = root_scope = root;
2618 public RootScopeInfo RootScope {
2620 if (root_scope != null)
2622 else if (Container != null)
2623 return Container.RootScope;
2629 public FlowBranchingToplevel TopLevelBranching {
2630 get { return top_level_branching; }
2634 // This is used if anonymous methods are used inside an iterator
2635 // (see 2test-22.cs for an example).
2637 // The AnonymousMethod is created while parsing - at a time when we don't
2638 // know yet that we're inside an iterator, so it's `Container' is initially
2639 // null. Later on, when resolving the iterator, we need to move the
2640 // anonymous method into that iterator.
2642 public void ReParent (ToplevelBlock new_parent)
2644 if ((flags & Flags.VariablesInitialized) != 0)
2645 throw new InternalErrorException ("block has already been resolved");
2647 Parent = new_parent;
2651 // Returns a `ParameterReference' for the given name, or null if there
2652 // is no such parameter
2654 public ParameterReference GetParameterReference (string name, Location loc)
2656 ToplevelParameterInfo p = GetParameterInfo (name);
2657 return p == null ? null : new ParameterReference (this, p, loc);
2660 public ToplevelParameterInfo GetParameterInfo (string name)
2663 for (ToplevelBlock t = this; t != null; t = t.Container) {
2664 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2666 return t.parameter_info [idx];
2672 // Whether the parameter named `name' is local to this block,
2673 // or false, if the parameter belongs to an encompassing block.
2675 public bool IsLocalParameter (string name)
2677 return Parameters.GetParameterByName (name) != null;
2681 // Whether the `name' is a parameter reference
2683 public bool IsParameterReference (string name)
2685 for (ToplevelBlock t = this; t != null; t = t.Container) {
2686 if (t.IsLocalParameter (name))
2692 LocalInfo this_variable = null;
2695 // Returns the "this" instance variable of this block.
2696 // See AddThisVariable() for more information.
2698 public LocalInfo ThisVariable {
2699 get { return this_variable; }
2704 // This is used by non-static `struct' constructors which do not have an
2705 // initializer - in this case, the constructor must initialize all of the
2706 // struct's fields. To do this, we add a "this" variable and use the flow
2707 // analysis code to ensure that it's been fully initialized before control
2708 // leaves the constructor.
2710 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2712 if (this_variable == null) {
2713 this_variable = new LocalInfo (ds, this, l);
2714 this_variable.Used = true;
2715 this_variable.IsThis = true;
2717 Variables.Add ("this", this_variable);
2720 return this_variable;
2723 public bool IsThisAssigned (EmitContext ec)
2725 return this_variable == null || this_variable.IsThisAssigned (ec);
2728 public bool ResolveMeta (EmitContext ec, Parameters ip)
2730 int errors = Report.Errors;
2731 int orig_count = parameters.Count;
2733 if (top_level_branching != null)
2739 // Assert: orig_count != parameter.Count => orig_count == 0
2740 if (orig_count != 0 && orig_count != parameters.Count)
2741 throw new InternalErrorException ("parameter information mismatch");
2743 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2745 for (int i = 0; i < orig_count; ++i) {
2746 Parameter.Modifier mod = parameters.ParameterModifier (i);
2748 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2751 VariableInfo vi = new VariableInfo (ip, i, offset);
2752 parameter_info [i].VariableInfo = vi;
2753 offset += vi.Length;
2756 ResolveMeta (ec, offset);
2758 top_level_branching = ec.StartFlowBranching (this);
2760 return Report.Errors == errors;
2764 // Check whether all `out' parameters have been assigned.
2766 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2768 if (vector.IsUnreachable)
2771 int n = parameter_info == null ? 0 : parameter_info.Length;
2773 for (int i = 0; i < n; i++) {
2774 VariableInfo var = parameter_info [i].VariableInfo;
2779 if (vector.IsAssigned (var, false))
2782 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2787 public override void EmitMeta (EmitContext ec)
2790 parameters.ResolveVariable (this);
2793 public void MakeIterator (Iterator iterator)
2795 flags |= Flags.IsIterator;
2797 Block block = new ExplicitBlock (this, StartLocation, EndLocation);
2798 foreach (Statement stmt in statements)
2799 block.AddStatement (stmt);
2800 statements.Clear ();
2801 statements.Add (new MoveNextStatement (iterator, block));
2804 protected class MoveNextStatement : Statement {
2808 public MoveNextStatement (Iterator iterator, Block block)
2810 this.iterator = iterator;
2812 this.loc = iterator.Location;
2815 public override bool Resolve (EmitContext ec)
2817 return block.Resolve (ec);
2820 protected override void DoEmit (EmitContext ec)
2822 iterator.EmitMoveNext (ec, block);
2826 public override string ToString ()
2828 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2829 root_scope, anonymous_container != null ?
2830 anonymous_container.Scope : null);
2834 public class SwitchLabel {
2841 Label il_label_code;
2842 bool il_label_code_set;
2844 public static readonly object NullStringCase = new object ();
2847 // if expr == null, then it is the default case.
2849 public SwitchLabel (Expression expr, Location l)
2855 public Expression Label {
2861 public object Converted {
2867 public Label GetILLabel (EmitContext ec)
2870 il_label = ec.ig.DefineLabel ();
2871 il_label_set = true;
2876 public Label GetILLabelCode (EmitContext ec)
2878 if (!il_label_code_set){
2879 il_label_code = ec.ig.DefineLabel ();
2880 il_label_code_set = true;
2882 return il_label_code;
2886 // Resolves the expression, reduces it to a literal if possible
2887 // and then converts it to the requested type.
2889 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2891 Expression e = label.Resolve (ec);
2896 Constant c = e as Constant;
2898 Report.Error (150, loc, "A constant value is expected");
2902 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2903 converted = NullStringCase;
2907 if (allow_nullable && c.GetValue () == null) {
2908 converted = NullStringCase;
2912 c = c.ImplicitConversionRequired (required_type, loc);
2916 converted = c.GetValue ();
2920 public void Erorr_AlreadyOccurs (Type switchType, SwitchLabel collisionWith)
2923 if (converted == null)
2925 else if (converted == NullStringCase)
2927 else if (TypeManager.IsEnumType (switchType))
2928 label = TypeManager.CSharpEnumValue (switchType, converted);
2930 label = converted.ToString ();
2932 Report.SymbolRelatedToPreviousError (collisionWith.loc, null);
2933 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2936 public SwitchLabel Clone (CloneContext clonectx)
2938 return new SwitchLabel (label.Clone (clonectx), loc);
2942 public class SwitchSection {
2943 // An array of SwitchLabels.
2944 public readonly ArrayList Labels;
2945 public readonly Block Block;
2947 public SwitchSection (ArrayList labels, Block block)
2953 public SwitchSection Clone (CloneContext clonectx)
2955 ArrayList cloned_labels = new ArrayList ();
2957 foreach (SwitchLabel sl in cloned_labels)
2958 cloned_labels.Add (sl.Clone (clonectx));
2960 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
2964 public class Switch : Statement {
2965 public ArrayList Sections;
2966 public Expression Expr;
2969 /// Maps constants whose type type SwitchType to their SwitchLabels.
2971 public IDictionary Elements;
2974 /// The governing switch type
2976 public Type SwitchType;
2981 Label default_target;
2983 Expression new_expr;
2985 SwitchSection constant_section;
2986 SwitchSection default_section;
2990 // Nullable Types support for GMCS.
2992 Nullable.Unwrap unwrap;
2994 protected bool HaveUnwrap {
2995 get { return unwrap != null; }
2998 protected bool HaveUnwrap {
2999 get { return false; }
3004 // The types allowed to be implicitly cast from
3005 // on the governing type
3007 static Type [] allowed_types;
3009 public Switch (Expression e, ArrayList sects, Location l)
3016 public bool GotDefault {
3018 return default_section != null;
3022 public Label DefaultTarget {
3024 return default_target;
3029 // Determines the governing type for a switch. The returned
3030 // expression might be the expression from the switch, or an
3031 // expression that includes any potential conversions to the
3032 // integral types or to string.
3034 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3036 Type t = TypeManager.DropGenericTypeArguments (expr.Type);
3038 if (t == TypeManager.byte_type ||
3039 t == TypeManager.sbyte_type ||
3040 t == TypeManager.ushort_type ||
3041 t == TypeManager.short_type ||
3042 t == TypeManager.uint32_type ||
3043 t == TypeManager.int32_type ||
3044 t == TypeManager.uint64_type ||
3045 t == TypeManager.int64_type ||
3046 t == TypeManager.char_type ||
3047 t == TypeManager.string_type ||
3048 t == TypeManager.bool_type ||
3049 t.IsSubclassOf (TypeManager.enum_type))
3052 if (allowed_types == null){
3053 allowed_types = new Type [] {
3054 TypeManager.sbyte_type,
3055 TypeManager.byte_type,
3056 TypeManager.short_type,
3057 TypeManager.ushort_type,
3058 TypeManager.int32_type,
3059 TypeManager.uint32_type,
3060 TypeManager.int64_type,
3061 TypeManager.uint64_type,
3062 TypeManager.char_type,
3063 TypeManager.string_type,
3064 TypeManager.bool_type
3069 // Try to find a *user* defined implicit conversion.
3071 // If there is no implicit conversion, or if there are multiple
3072 // conversions, we have to report an error
3074 Expression converted = null;
3075 foreach (Type tt in allowed_types){
3078 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3083 // Ignore over-worked ImplicitUserConversions that do
3084 // an implicit conversion in addition to the user conversion.
3086 if (!(e is UserCast))
3089 if (converted != null){
3090 Report.ExtraInformation (
3092 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3093 TypeManager.CSharpName (expr.Type)));
3103 // Performs the basic sanity checks on the switch statement
3104 // (looks for duplicate keys and non-constant expressions).
3106 // It also returns a hashtable with the keys that we will later
3107 // use to compute the switch tables
3109 bool CheckSwitch (EmitContext ec)
3112 Elements = Sections.Count > 10 ?
3113 (IDictionary)new Hashtable () :
3114 (IDictionary)new ListDictionary ();
3116 foreach (SwitchSection ss in Sections){
3117 foreach (SwitchLabel sl in ss.Labels){
3118 if (sl.Label == null){
3119 if (default_section != null){
3120 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3123 default_section = ss;
3127 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3132 object key = sl.Converted;
3134 Elements.Add (key, sl);
3135 } catch (ArgumentException) {
3136 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3144 void EmitObjectInteger (ILGenerator ig, object k)
3147 IntConstant.EmitInt (ig, (int) k);
3148 else if (k is Constant) {
3149 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3152 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3155 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3157 IntConstant.EmitInt (ig, (int) (long) k);
3158 ig.Emit (OpCodes.Conv_I8);
3161 LongConstant.EmitLong (ig, (long) k);
3163 else if (k is ulong)
3165 ulong ul = (ulong) k;
3168 IntConstant.EmitInt (ig, unchecked ((int) ul));
3169 ig.Emit (OpCodes.Conv_U8);
3173 LongConstant.EmitLong (ig, unchecked ((long) ul));
3177 IntConstant.EmitInt (ig, (int) ((char) k));
3178 else if (k is sbyte)
3179 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3181 IntConstant.EmitInt (ig, (int) ((byte) k));
3182 else if (k is short)
3183 IntConstant.EmitInt (ig, (int) ((short) k));
3184 else if (k is ushort)
3185 IntConstant.EmitInt (ig, (int) ((ushort) k));
3187 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3189 throw new Exception ("Unhandled case");
3192 // structure used to hold blocks of keys while calculating table switch
3193 class KeyBlock : IComparable
3195 public KeyBlock (long _nFirst)
3197 nFirst = nLast = _nFirst;
3201 public ArrayList rgKeys = null;
3202 // how many items are in the bucket
3203 public int Size = 1;
3206 get { return (int) (nLast - nFirst + 1); }
3208 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
3210 return kbLast.nLast - kbFirst.nFirst + 1;
3212 public int CompareTo (object obj)
3214 KeyBlock kb = (KeyBlock) obj;
3215 int nLength = Length;
3216 int nLengthOther = kb.Length;
3217 if (nLengthOther == nLength)
3218 return (int) (kb.nFirst - nFirst);
3219 return nLength - nLengthOther;
3224 /// This method emits code for a lookup-based switch statement (non-string)
3225 /// Basically it groups the cases into blocks that are at least half full,
3226 /// and then spits out individual lookup opcodes for each block.
3227 /// It emits the longest blocks first, and short blocks are just
3228 /// handled with direct compares.
3230 /// <param name="ec"></param>
3231 /// <param name="val"></param>
3232 /// <returns></returns>
3233 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3235 int cElements = Elements.Count;
3236 object [] rgKeys = new object [cElements];
3237 Elements.Keys.CopyTo (rgKeys, 0);
3238 Array.Sort (rgKeys);
3240 // initialize the block list with one element per key
3241 ArrayList rgKeyBlocks = new ArrayList ();
3242 foreach (object key in rgKeys)
3243 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3246 // iteratively merge the blocks while they are at least half full
3247 // there's probably a really cool way to do this with a tree...
3248 while (rgKeyBlocks.Count > 1)
3250 ArrayList rgKeyBlocksNew = new ArrayList ();
3251 kbCurr = (KeyBlock) rgKeyBlocks [0];
3252 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3254 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3255 if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
3258 kbCurr.nLast = kb.nLast;
3259 kbCurr.Size += kb.Size;
3263 // start a new block
3264 rgKeyBlocksNew.Add (kbCurr);
3268 rgKeyBlocksNew.Add (kbCurr);
3269 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3271 rgKeyBlocks = rgKeyBlocksNew;
3274 // initialize the key lists
3275 foreach (KeyBlock kb in rgKeyBlocks)
3276 kb.rgKeys = new ArrayList ();
3278 // fill the key lists
3280 if (rgKeyBlocks.Count > 0) {
3281 kbCurr = (KeyBlock) rgKeyBlocks [0];
3282 foreach (object key in rgKeys)
3284 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
3285 System.Convert.ToInt64 (key) > kbCurr.nLast;
3287 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3288 kbCurr.rgKeys.Add (key);
3292 // sort the blocks so we can tackle the largest ones first
3293 rgKeyBlocks.Sort ();
3295 // okay now we can start...
3296 ILGenerator ig = ec.ig;
3297 Label lblEnd = ig.DefineLabel (); // at the end ;-)
3298 Label lblDefault = ig.DefineLabel ();
3300 Type typeKeys = null;
3301 if (rgKeys.Length > 0)
3302 typeKeys = rgKeys [0].GetType (); // used for conversions
3306 if (TypeManager.IsEnumType (SwitchType))
3307 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3309 compare_type = SwitchType;
3311 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3313 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3314 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3317 foreach (object key in kb.rgKeys)
3319 ig.Emit (OpCodes.Ldloc, val);
3320 EmitObjectInteger (ig, key);
3321 SwitchLabel sl = (SwitchLabel) Elements [key];
3322 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3327 // TODO: if all the keys in the block are the same and there are
3328 // no gaps/defaults then just use a range-check.
3329 if (compare_type == TypeManager.int64_type ||
3330 compare_type == TypeManager.uint64_type)
3332 // TODO: optimize constant/I4 cases
3334 // check block range (could be > 2^31)
3335 ig.Emit (OpCodes.Ldloc, val);
3336 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3337 ig.Emit (OpCodes.Blt, lblDefault);
3338 ig.Emit (OpCodes.Ldloc, val);
3339 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
3340 ig.Emit (OpCodes.Bgt, lblDefault);
3343 ig.Emit (OpCodes.Ldloc, val);
3346 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3347 ig.Emit (OpCodes.Sub);
3349 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3354 ig.Emit (OpCodes.Ldloc, val);
3355 int nFirst = (int) kb.nFirst;
3358 IntConstant.EmitInt (ig, nFirst);
3359 ig.Emit (OpCodes.Sub);
3361 else if (nFirst < 0)
3363 IntConstant.EmitInt (ig, -nFirst);
3364 ig.Emit (OpCodes.Add);
3368 // first, build the list of labels for the switch
3370 int cJumps = kb.Length;
3371 Label [] rgLabels = new Label [cJumps];
3372 for (int iJump = 0; iJump < cJumps; iJump++)
3374 object key = kb.rgKeys [iKey];
3375 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
3377 SwitchLabel sl = (SwitchLabel) Elements [key];
3378 rgLabels [iJump] = sl.GetILLabel (ec);
3382 rgLabels [iJump] = lblDefault;
3384 // emit the switch opcode
3385 ig.Emit (OpCodes.Switch, rgLabels);
3388 // mark the default for this block
3390 ig.MarkLabel (lblDefault);
3393 // TODO: find the default case and emit it here,
3394 // to prevent having to do the following jump.
3395 // make sure to mark other labels in the default section
3397 // the last default just goes to the end
3398 ig.Emit (OpCodes.Br, lblDefault);
3400 // now emit the code for the sections
3401 bool fFoundDefault = false;
3402 bool fFoundNull = false;
3403 foreach (SwitchSection ss in Sections)
3405 foreach (SwitchLabel sl in ss.Labels)
3406 if (sl.Converted == SwitchLabel.NullStringCase)
3410 foreach (SwitchSection ss in Sections)
3412 foreach (SwitchLabel sl in ss.Labels)
3414 ig.MarkLabel (sl.GetILLabel (ec));
3415 ig.MarkLabel (sl.GetILLabelCode (ec));
3416 if (sl.Converted == SwitchLabel.NullStringCase)
3417 ig.MarkLabel (null_target);
3418 else if (sl.Label == null) {
3419 ig.MarkLabel (lblDefault);
3420 fFoundDefault = true;
3422 ig.MarkLabel (null_target);
3428 if (!fFoundDefault) {
3429 ig.MarkLabel (lblDefault);
3430 if (HaveUnwrap && !fFoundNull) {
3431 ig.MarkLabel (null_target);
3435 ig.MarkLabel (lblEnd);
3438 // This simple emit switch works, but does not take advantage of the
3440 // TODO: remove non-string logic from here
3441 // TODO: binary search strings?
3443 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3445 ILGenerator ig = ec.ig;
3446 Label end_of_switch = ig.DefineLabel ();
3447 Label next_test = ig.DefineLabel ();
3448 bool first_test = true;
3449 bool pending_goto_end = false;
3450 bool null_marked = false;
3452 int section_count = Sections.Count;
3454 // TODO: implement switch optimization for string by using Hashtable
3455 //if (SwitchType == TypeManager.string_type && section_count > 7)
3456 // Console.WriteLine ("Switch optimization possible " + loc);
3458 ig.Emit (OpCodes.Ldloc, val);
3460 if (Elements.Contains (SwitchLabel.NullStringCase)){
3461 ig.Emit (OpCodes.Brfalse, null_target);
3463 ig.Emit (OpCodes.Brfalse, default_target);
3465 ig.Emit (OpCodes.Ldloc, val);
3466 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3467 ig.Emit (OpCodes.Stloc, val);
3469 for (int section = 0; section < section_count; section++){
3470 SwitchSection ss = (SwitchSection) Sections [section];
3472 if (ss == default_section)
3475 Label sec_begin = ig.DefineLabel ();
3477 ig.Emit (OpCodes.Nop);
3479 if (pending_goto_end)
3480 ig.Emit (OpCodes.Br, end_of_switch);
3482 int label_count = ss.Labels.Count;
3484 for (int label = 0; label < label_count; label++){
3485 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3486 ig.MarkLabel (sl.GetILLabel (ec));
3489 ig.MarkLabel (next_test);
3490 next_test = ig.DefineLabel ();
3493 // If we are the default target
3495 if (sl.Label != null){
3496 object lit = sl.Converted;
3498 if (lit == SwitchLabel.NullStringCase){
3500 if (label + 1 == label_count)
3501 ig.Emit (OpCodes.Br, next_test);
3505 ig.Emit (OpCodes.Ldloc, val);
3506 ig.Emit (OpCodes.Ldstr, (string)lit);
3507 if (label_count == 1)
3508 ig.Emit (OpCodes.Bne_Un, next_test);
3510 if (label+1 == label_count)
3511 ig.Emit (OpCodes.Bne_Un, next_test);
3513 ig.Emit (OpCodes.Beq, sec_begin);
3518 ig.MarkLabel (null_target);
3521 ig.MarkLabel (sec_begin);
3522 foreach (SwitchLabel sl in ss.Labels)
3523 ig.MarkLabel (sl.GetILLabelCode (ec));
3526 pending_goto_end = !ss.Block.HasRet;
3529 ig.MarkLabel (next_test);
3530 ig.MarkLabel (default_target);
3532 ig.MarkLabel (null_target);
3533 if (default_section != null)
3534 default_section.Block.Emit (ec);
3535 ig.MarkLabel (end_of_switch);
3538 SwitchSection FindSection (SwitchLabel label)
3540 foreach (SwitchSection ss in Sections){
3541 foreach (SwitchLabel sl in ss.Labels){
3550 public override bool Resolve (EmitContext ec)
3552 Expr = Expr.Resolve (ec);
3556 new_expr = SwitchGoverningType (ec, Expr);
3559 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3560 unwrap = Nullable.Unwrap.Create (Expr, ec);
3564 new_expr = SwitchGoverningType (ec, unwrap);
3568 if (new_expr == null){
3569 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3574 SwitchType = new_expr.Type;
3576 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3577 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3581 if (!CheckSwitch (ec))
3585 Elements.Remove (SwitchLabel.NullStringCase);
3587 Switch old_switch = ec.Switch;
3589 ec.Switch.SwitchType = SwitchType;
3591 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3592 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3594 is_constant = new_expr is Constant;
3596 object key = ((Constant) new_expr).GetValue ();
3597 SwitchLabel label = (SwitchLabel) Elements [key];
3599 constant_section = FindSection (label);
3600 if (constant_section == null)
3601 constant_section = default_section;
3605 foreach (SwitchSection ss in Sections){
3607 ec.CurrentBranching.CreateSibling (
3608 null, FlowBranching.SiblingType.SwitchSection);
3612 if (is_constant && (ss != constant_section)) {
3613 // If we're a constant switch, we're only emitting
3614 // one single section - mark all the others as
3616 ec.CurrentBranching.CurrentUsageVector.Goto ();
3617 if (!ss.Block.ResolveUnreachable (ec, true))
3620 if (!ss.Block.Resolve (ec))
3625 if (default_section == null)
3626 ec.CurrentBranching.CreateSibling (
3627 null, FlowBranching.SiblingType.SwitchSection);
3629 ec.EndFlowBranching ();
3630 ec.Switch = old_switch;
3632 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3637 protected override void DoEmit (EmitContext ec)
3639 ILGenerator ig = ec.ig;
3641 default_target = ig.DefineLabel ();
3642 null_target = ig.DefineLabel ();
3644 // Store variable for comparission purposes
3647 value = ig.DeclareLocal (SwitchType);
3649 unwrap.EmitCheck (ec);
3650 ig.Emit (OpCodes.Brfalse, null_target);
3652 ig.Emit (OpCodes.Stloc, value);
3654 } else if (!is_constant) {
3655 value = ig.DeclareLocal (SwitchType);
3657 ig.Emit (OpCodes.Stloc, value);
3662 // Setup the codegen context
3664 Label old_end = ec.LoopEnd;
3665 Switch old_switch = ec.Switch;
3667 ec.LoopEnd = ig.DefineLabel ();
3672 if (constant_section != null)
3673 constant_section.Block.Emit (ec);
3674 } else if (SwitchType == TypeManager.string_type)
3675 SimpleSwitchEmit (ec, value);
3677 TableSwitchEmit (ec, value);
3679 // Restore context state.
3680 ig.MarkLabel (ec.LoopEnd);
3683 // Restore the previous context
3685 ec.LoopEnd = old_end;
3686 ec.Switch = old_switch;
3689 protected override void CloneTo (CloneContext clonectx, Statement t)
3691 Switch target = (Switch) t;
3693 target.Expr = Expr.Clone (clonectx);
3694 target.Sections = new ArrayList ();
3695 foreach (SwitchSection ss in Sections){
3696 target.Sections.Add (ss.Clone (clonectx));
3701 public abstract class ExceptionStatement : Statement
3703 public abstract void EmitFinally (EmitContext ec);
3705 protected bool emit_finally = true;
3706 ArrayList parent_vectors;
3708 protected void DoEmitFinally (EmitContext ec)
3711 ec.ig.BeginFinallyBlock ();
3712 else if (ec.InIterator)
3713 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3717 protected void ResolveFinally (FlowBranchingException branching)
3719 emit_finally = branching.EmitFinally;
3721 branching.Parent.StealFinallyClauses (ref parent_vectors);
3725 public class Lock : ExceptionStatement {
3727 public Statement Statement;
3728 TemporaryVariable temp;
3730 public Lock (Expression expr, Statement stmt, Location l)
3737 public override bool Resolve (EmitContext ec)
3739 expr = expr.Resolve (ec);
3743 if (expr.Type.IsValueType){
3744 Report.Error (185, loc,
3745 "`{0}' is not a reference type as required by the lock statement",
3746 TypeManager.CSharpName (expr.Type));
3750 FlowBranchingException branching = ec.StartFlowBranching (this);
3751 bool ok = Statement.Resolve (ec);
3753 ResolveFinally (branching);
3755 ec.EndFlowBranching ();
3757 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3758 // So, ensure there's some IL code after the finally block.
3759 ec.NeedReturnLabel ();
3761 // Avoid creating libraries that reference the internal
3764 if (t == TypeManager.null_type)
3765 t = TypeManager.object_type;
3767 temp = new TemporaryVariable (t, loc);
3773 protected override void DoEmit (EmitContext ec)
3775 ILGenerator ig = ec.ig;
3777 temp.Store (ec, expr);
3779 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3783 ig.BeginExceptionBlock ();
3784 Statement.Emit (ec);
3789 ig.EndExceptionBlock ();
3792 public override void EmitFinally (EmitContext ec)
3795 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3798 protected override void CloneTo (CloneContext clonectx, Statement t)
3800 Lock target = (Lock) t;
3802 target.expr = expr.Clone (clonectx);
3803 target.Statement = Statement.Clone (clonectx);
3807 public class Unchecked : Statement {
3810 public Unchecked (Block b)
3816 public override bool Resolve (EmitContext ec)
3818 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3819 return Block.Resolve (ec);
3822 protected override void DoEmit (EmitContext ec)
3824 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3828 protected override void CloneTo (CloneContext clonectx, Statement t)
3830 Unchecked target = (Unchecked) t;
3832 target.Block = clonectx.LookupBlock (Block);
3836 public class Checked : Statement {
3839 public Checked (Block b)
3842 b.Unchecked = false;
3845 public override bool Resolve (EmitContext ec)
3847 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3848 return Block.Resolve (ec);
3851 protected override void DoEmit (EmitContext ec)
3853 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3857 protected override void CloneTo (CloneContext clonectx, Statement t)
3859 Checked target = (Checked) t;
3861 target.Block = clonectx.LookupBlock (Block);
3865 public class Unsafe : Statement {
3868 public Unsafe (Block b)
3871 Block.Unsafe = true;
3874 public override bool Resolve (EmitContext ec)
3876 using (ec.With (EmitContext.Flags.InUnsafe, true))
3877 return Block.Resolve (ec);
3880 protected override void DoEmit (EmitContext ec)
3882 using (ec.With (EmitContext.Flags.InUnsafe, true))
3885 protected override void CloneTo (CloneContext clonectx, Statement t)
3887 Unsafe target = (Unsafe) t;
3889 target.Block = clonectx.LookupBlock (Block);
3896 public class Fixed : Statement {
3898 ArrayList declarators;
3899 Statement statement;
3904 abstract class Emitter
3906 protected LocalInfo vi;
3907 protected Expression converted;
3909 protected Emitter (Expression expr, LocalInfo li)
3915 public abstract void Emit (EmitContext ec);
3916 public abstract void EmitExit (EmitContext ec);
3919 class ExpressionEmitter : Emitter {
3920 public ExpressionEmitter (Expression converted, LocalInfo li) :
3921 base (converted, li)
3925 public override void Emit (EmitContext ec) {
3927 // Store pointer in pinned location
3929 converted.Emit (ec);
3930 vi.Variable.EmitAssign (ec);
3933 public override void EmitExit (EmitContext ec)
3935 ec.ig.Emit (OpCodes.Ldc_I4_0);
3936 ec.ig.Emit (OpCodes.Conv_U);
3937 vi.Variable.EmitAssign (ec);
3941 class StringEmitter : Emitter {
3942 LocalBuilder pinned_string;
3945 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3951 public override void Emit (EmitContext ec)
3953 ILGenerator ig = ec.ig;
3954 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3956 converted.Emit (ec);
3957 ig.Emit (OpCodes.Stloc, pinned_string);
3959 Expression sptr = new StringPtr (pinned_string, loc);
3960 converted = Convert.ImplicitConversionRequired (
3961 ec, sptr, vi.VariableType, loc);
3963 if (converted == null)
3966 converted.Emit (ec);
3967 vi.Variable.EmitAssign (ec);
3970 public override void EmitExit (EmitContext ec)
3972 ec.ig.Emit (OpCodes.Ldnull);
3973 ec.ig.Emit (OpCodes.Stloc, pinned_string);
3977 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3980 declarators = decls;
3985 public Statement Statement {
3986 get { return statement; }
3989 public override bool Resolve (EmitContext ec)
3992 Expression.UnsafeError (loc);
3996 TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
4000 expr_type = texpr.Type;
4002 data = new Emitter [declarators.Count];
4004 if (!expr_type.IsPointer){
4005 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4010 foreach (Pair p in declarators){
4011 LocalInfo vi = (LocalInfo) p.First;
4012 Expression e = (Expression) p.Second;
4014 vi.VariableInfo.SetAssigned (ec);
4015 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4018 // The rules for the possible declarators are pretty wise,
4019 // but the production on the grammar is more concise.
4021 // So we have to enforce these rules here.
4023 // We do not resolve before doing the case 1 test,
4024 // because the grammar is explicit in that the token &
4025 // is present, so we need to test for this particular case.
4029 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4034 // Case 1: & object.
4036 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4037 Expression child = ((Unary) e).Expr;
4039 if (child is ParameterReference || child is LocalVariableReference){
4042 "No need to use fixed statement for parameters or " +
4043 "local variable declarations (address is already " +
4048 ec.InFixedInitializer = true;
4050 ec.InFixedInitializer = false;
4054 child = ((Unary) e).Expr;
4056 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4059 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4060 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4064 data [i] = new ExpressionEmitter (e, vi);
4070 ec.InFixedInitializer = true;
4072 ec.InFixedInitializer = false;
4079 if (e.Type.IsArray){
4080 Type array_type = TypeManager.GetElementType (e.Type);
4083 // Provided that array_type is unmanaged,
4085 if (!TypeManager.VerifyUnManaged (array_type, loc))
4089 // and T* is implicitly convertible to the
4090 // pointer type given in the fixed statement.
4092 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4094 Expression converted = Convert.ImplicitConversionRequired (
4095 ec, array_ptr, vi.VariableType, loc);
4096 if (converted == null)
4100 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4102 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4103 new Binary (Binary.Operator.Equality, e, new NullConstant (loc)),
4104 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4108 converted = converted.Resolve (ec);
4110 data [i] = new ExpressionEmitter (converted, vi);
4119 if (e.Type == TypeManager.string_type){
4120 data [i] = new StringEmitter (e, vi, loc);
4125 // Case 4: fixed buffer
4126 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4127 if (fixed_buffer_ptr != null) {
4128 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4133 // For other cases, flag a `this is already fixed expression'
4135 if (e is LocalVariableReference || e is ParameterReference ||
4136 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4138 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4142 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4146 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4147 bool ok = statement.Resolve (ec);
4148 bool flow_unreachable = ec.EndFlowBranching ();
4149 has_ret = flow_unreachable;
4154 protected override void DoEmit (EmitContext ec)
4156 for (int i = 0; i < data.Length; i++) {
4160 statement.Emit (ec);
4166 // Clear the pinned variable
4168 for (int i = 0; i < data.Length; i++) {
4169 data [i].EmitExit (ec);
4173 protected override void CloneTo (CloneContext clonectx, Statement t)
4175 Fixed target = (Fixed) t;
4177 target.type = type.Clone (clonectx);
4178 target.declarators = new ArrayList (declarators.Count);
4179 foreach (Pair p in declarators) {
4180 LocalInfo vi = (LocalInfo) p.First;
4181 Expression e = (Expression) p.Second;
4183 target.declarators.Add (
4184 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4187 target.statement = statement.Clone (clonectx);
4191 public class Catch : Statement {
4192 public readonly string Name;
4194 public Block VarBlock;
4196 Expression type_expr;
4199 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4204 VarBlock = var_block;
4208 public Type CatchType {
4214 public bool IsGeneral {
4216 return type_expr == null;
4220 protected override void DoEmit(EmitContext ec)
4222 ILGenerator ig = ec.ig;
4224 if (CatchType != null)
4225 ig.BeginCatchBlock (CatchType);
4227 ig.BeginCatchBlock (TypeManager.object_type);
4229 if (VarBlock != null)
4233 LocalInfo vi = Block.GetLocalInfo (Name);
4235 throw new Exception ("Variable does not exist in this block");
4237 if (vi.Variable.NeedsTemporary) {
4238 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4239 ig.Emit (OpCodes.Stloc, e);
4241 vi.Variable.EmitInstance (ec);
4242 ig.Emit (OpCodes.Ldloc, e);
4243 vi.Variable.EmitAssign (ec);
4245 vi.Variable.EmitAssign (ec);
4247 ig.Emit (OpCodes.Pop);
4252 public override bool Resolve (EmitContext ec)
4254 using (ec.With (EmitContext.Flags.InCatch, true)) {
4255 if (type_expr != null) {
4256 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4262 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4263 Error (155, "The type caught or thrown must be derived from System.Exception");
4269 if (!Block.Resolve (ec))
4272 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4273 // emit the "unused variable" warnings.
4274 if (VarBlock != null)
4275 return VarBlock.Resolve (ec);
4281 protected override void CloneTo (CloneContext clonectx, Statement t)
4283 Catch target = (Catch) t;
4285 if (type_expr != null)
4286 target.type_expr = type_expr.Clone (clonectx);
4287 if (VarBlock != null)
4288 target.VarBlock = clonectx.LookupBlock (VarBlock);
4289 target.Block = clonectx.LookupBlock (Block);
4293 public class Try : ExceptionStatement {
4294 public Block Fini, Block;
4295 public ArrayList Specific;
4296 public Catch General;
4298 bool need_exc_block;
4301 // specific, general and fini might all be null.
4303 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4305 if (specific == null && general == null){
4306 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4310 this.Specific = specific;
4311 this.General = general;
4316 public override bool Resolve (EmitContext ec)
4320 FlowBranchingException branching = ec.StartFlowBranching (this);
4322 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4324 if (!Block.Resolve (ec))
4327 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4329 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4331 Type[] prevCatches = new Type [Specific.Count];
4333 foreach (Catch c in Specific){
4334 ec.CurrentBranching.CreateSibling (
4335 c.Block, FlowBranching.SiblingType.Catch);
4337 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4339 if (c.Name != null) {
4340 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4342 throw new Exception ();
4344 vi.VariableInfo = null;
4347 if (!c.Resolve (ec))
4350 Type resolvedType = c.CatchType;
4351 for (int ii = 0; ii < last_index; ++ii) {
4352 if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
4353 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches [ii].FullName);
4358 prevCatches [last_index++] = resolvedType;
4359 need_exc_block = true;
4362 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4364 if (General != null){
4365 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4366 foreach (Catch c in Specific){
4367 if (c.CatchType == TypeManager.exception_type) {
4368 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'");
4373 ec.CurrentBranching.CreateSibling (
4374 General.Block, FlowBranching.SiblingType.Catch);
4376 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4378 if (!General.Resolve (ec))
4381 need_exc_block = true;
4384 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4388 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4390 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4391 using (ec.With (EmitContext.Flags.InFinally, true)) {
4392 if (!Fini.Resolve (ec))
4397 need_exc_block = true;
4400 if (ec.InIterator) {
4401 ResolveFinally (branching);
4402 need_exc_block |= emit_finally;
4404 emit_finally = Fini != null;
4406 ec.EndFlowBranching ();
4408 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4409 // So, ensure there's some IL code after the finally block.
4410 ec.NeedReturnLabel ();
4412 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4414 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4419 protected override void DoEmit (EmitContext ec)
4421 ILGenerator ig = ec.ig;
4424 ig.BeginExceptionBlock ();
4427 foreach (Catch c in Specific)
4430 if (General != null)
4435 ig.EndExceptionBlock ();
4438 public override void EmitFinally (EmitContext ec)
4444 public bool HasCatch
4447 return General != null || Specific.Count > 0;
4451 protected override void CloneTo (CloneContext clonectx, Statement t)
4453 Try target = (Try) t;
4455 target.Block = clonectx.LookupBlock (Block);
4457 target.Fini = clonectx.LookupBlock (Fini);
4458 if (General != null)
4459 target.General = (Catch) General.Clone (clonectx);
4460 if (Specific != null){
4461 target.Specific = new ArrayList ();
4462 foreach (Catch c in Specific)
4463 target.Specific.Add (c.Clone (clonectx));
4468 public class Using : ExceptionStatement {
4469 object expression_or_block;
4470 public Statement Statement;
4474 Expression [] resolved_vars;
4475 Expression [] converted_vars;
4476 Expression [] assign;
4477 TemporaryVariable local_copy;
4479 public Using (object expression_or_block, Statement stmt, Location l)
4481 this.expression_or_block = expression_or_block;
4487 // Resolves for the case of using using a local variable declaration.
4489 bool ResolveLocalVariableDecls (EmitContext ec)
4491 resolved_vars = new Expression[var_list.Count];
4492 assign = new Expression [var_list.Count];
4493 converted_vars = new Expression[var_list.Count];
4495 for (int i = 0; i < assign.Length; ++i) {
4496 DictionaryEntry e = (DictionaryEntry) var_list [i];
4497 Expression var = (Expression) e.Key;
4498 Expression new_expr = (Expression) e.Value;
4500 Expression a = new Assign (var, new_expr, loc);
4505 resolved_vars [i] = var;
4508 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4509 converted_vars [i] = var;
4513 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4515 Error_IsNotConvertibleToIDisposable (var);
4519 converted_vars [i] = a;
4525 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4527 Report.SymbolRelatedToPreviousError (expr.Type);
4528 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4529 expr.GetSignatureForError ());
4532 bool ResolveExpression (EmitContext ec)
4534 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4535 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4536 Error_IsNotConvertibleToIDisposable (expr);
4541 local_copy = new TemporaryVariable (expr_type, loc);
4542 local_copy.Resolve (ec);
4548 // Emits the code for the case of using using a local variable declaration.
4550 void EmitLocalVariableDecls (EmitContext ec)
4552 ILGenerator ig = ec.ig;
4555 for (i = 0; i < assign.Length; i++) {
4556 ExpressionStatement es = assign [i] as ExpressionStatement;
4559 es.EmitStatement (ec);
4561 assign [i].Emit (ec);
4562 ig.Emit (OpCodes.Pop);
4566 ig.BeginExceptionBlock ();
4568 Statement.Emit (ec);
4570 var_list.Reverse ();
4575 void EmitLocalVariableDeclFinally (EmitContext ec)
4577 ILGenerator ig = ec.ig;
4579 int i = assign.Length;
4580 for (int ii = 0; ii < var_list.Count; ++ii){
4581 Expression var = resolved_vars [--i];
4582 Label skip = ig.DefineLabel ();
4585 ig.BeginFinallyBlock ();
4587 if (!var.Type.IsValueType) {
4589 ig.Emit (OpCodes.Brfalse, skip);
4590 converted_vars [i].Emit (ec);
4591 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4593 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4595 if (!(ml is MethodGroupExpr)) {
4597 ig.Emit (OpCodes.Box, var.Type);
4598 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4600 MethodInfo mi = null;
4602 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4603 if (TypeManager.GetParameterData (mk).Count == 0) {
4610 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4614 IMemoryLocation mloc = (IMemoryLocation) var;
4616 mloc.AddressOf (ec, AddressOp.Load);
4617 ig.Emit (OpCodes.Call, mi);
4621 ig.MarkLabel (skip);
4624 ig.EndExceptionBlock ();
4626 ig.BeginFinallyBlock ();
4631 void EmitExpression (EmitContext ec)
4634 // Make a copy of the expression and operate on that.
4636 ILGenerator ig = ec.ig;
4638 local_copy.Store (ec, expr);
4641 ig.BeginExceptionBlock ();
4643 Statement.Emit (ec);
4647 ig.EndExceptionBlock ();
4650 void EmitExpressionFinally (EmitContext ec)
4652 ILGenerator ig = ec.ig;
4653 if (!expr_type.IsValueType) {
4654 Label skip = ig.DefineLabel ();
4655 local_copy.Emit (ec);
4656 ig.Emit (OpCodes.Brfalse, skip);
4657 local_copy.Emit (ec);
4658 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4659 ig.MarkLabel (skip);
4661 Expression ml = Expression.MemberLookup (
4662 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4663 "Dispose", Location.Null);
4665 if (!(ml is MethodGroupExpr)) {
4666 local_copy.Emit (ec);
4667 ig.Emit (OpCodes.Box, expr_type);
4668 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4670 MethodInfo mi = null;
4672 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4673 if (TypeManager.GetParameterData (mk).Count == 0) {
4680 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4684 local_copy.AddressOf (ec, AddressOp.Load);
4685 ig.Emit (OpCodes.Call, mi);
4690 public override bool Resolve (EmitContext ec)
4692 if (expression_or_block is DictionaryEntry){
4693 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4694 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4696 if (!ResolveLocalVariableDecls (ec))
4699 } else if (expression_or_block is Expression){
4700 expr = (Expression) expression_or_block;
4702 expr = expr.Resolve (ec);
4706 expr_type = expr.Type;
4708 if (!ResolveExpression (ec))
4712 FlowBranchingException branching = ec.StartFlowBranching (this);
4714 bool ok = Statement.Resolve (ec);
4716 ResolveFinally (branching);
4718 ec.EndFlowBranching ();
4720 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4721 // So, ensure there's some IL code after the finally block.
4722 ec.NeedReturnLabel ();
4727 protected override void DoEmit (EmitContext ec)
4729 if (expression_or_block is DictionaryEntry)
4730 EmitLocalVariableDecls (ec);
4731 else if (expression_or_block is Expression)
4732 EmitExpression (ec);
4735 public override void EmitFinally (EmitContext ec)
4737 if (expression_or_block is DictionaryEntry)
4738 EmitLocalVariableDeclFinally (ec);
4739 else if (expression_or_block is Expression)
4740 EmitExpressionFinally (ec);
4743 protected override void CloneTo (CloneContext clonectx, Statement t)
4745 Using target = (Using) t;
4747 if (expression_or_block is Expression)
4748 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4750 target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
4752 target.Statement = Statement.Clone (clonectx);
4757 /// Implementation of the foreach C# statement
4759 public class Foreach : Statement {
4761 Expression variable;
4763 Statement statement;
4765 CollectionForeach collection;
4767 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4768 Statement stmt, Location l)
4771 this.variable = var;
4777 public Statement Statement {
4778 get { return statement; }
4781 public override bool Resolve (EmitContext ec)
4783 expr = expr.Resolve (ec);
4787 if (expr.Type == TypeManager.null_type) {
4788 Report.Error (186, loc, "Use of null is not valid in this context");
4792 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4793 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4794 expr.ExprClassName);
4799 // We need an instance variable. Not sure this is the best
4800 // way of doing this.
4802 // FIXME: When we implement propertyaccess, will those turn
4803 // out to return values in ExprClass? I think they should.
4805 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4806 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4807 collection.Error_Enumerator ();
4811 if (expr.Type.IsArray) {
4812 array = new ArrayForeach (type, variable, expr, statement, loc);
4813 return array.Resolve (ec);
4816 collection = new CollectionForeach (type, variable, expr, statement, loc);
4817 return collection.Resolve (ec);
4820 protected override void DoEmit (EmitContext ec)
4822 ILGenerator ig = ec.ig;
4824 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4825 ec.LoopBegin = ig.DefineLabel ();
4826 ec.LoopEnd = ig.DefineLabel ();
4828 if (collection != null)
4829 collection.Emit (ec);
4833 ec.LoopBegin = old_begin;
4834 ec.LoopEnd = old_end;
4837 protected class ArrayCounter : TemporaryVariable
4839 public ArrayCounter (Location loc)
4840 : base (TypeManager.int32_type, loc)
4843 public void Initialize (EmitContext ec)
4846 ec.ig.Emit (OpCodes.Ldc_I4_0);
4850 public void Increment (EmitContext ec)
4854 ec.ig.Emit (OpCodes.Ldc_I4_1);
4855 ec.ig.Emit (OpCodes.Add);
4860 protected class ArrayForeach : Statement
4862 Expression variable, expr, conv;
4863 Statement statement;
4865 Expression var_type;
4866 TemporaryVariable[] lengths;
4867 ArrayCounter[] counter;
4870 TemporaryVariable copy;
4873 public ArrayForeach (Expression var_type, Expression var,
4874 Expression expr, Statement stmt, Location l)
4876 this.var_type = var_type;
4877 this.variable = var;
4883 public override bool Resolve (EmitContext ec)
4885 array_type = expr.Type;
4886 rank = array_type.GetArrayRank ();
4888 copy = new TemporaryVariable (array_type, loc);
4891 counter = new ArrayCounter [rank];
4892 lengths = new TemporaryVariable [rank];
4894 ArrayList list = new ArrayList ();
4895 for (int i = 0; i < rank; i++) {
4896 counter [i] = new ArrayCounter (loc);
4897 counter [i].Resolve (ec);
4899 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4900 lengths [i].Resolve (ec);
4902 list.Add (counter [i]);
4905 access = new ElementAccess (copy, list).Resolve (ec);
4909 VarExpr ve = var_type as VarExpr;
4911 // Infer implicitly typed local variable from foreach array type
4912 var_type = new TypeExpression (access.Type, ve.Location);
4915 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4916 if (var_type == null)
4919 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4925 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4926 ec.CurrentBranching.CreateSibling ();
4928 variable = variable.ResolveLValue (ec, conv, loc);
4929 if (variable == null)
4932 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4933 if (!statement.Resolve (ec))
4935 ec.EndFlowBranching ();
4937 // There's no direct control flow from the end of the embedded statement to the end of the loop
4938 ec.CurrentBranching.CurrentUsageVector.Goto ();
4940 ec.EndFlowBranching ();
4945 protected override void DoEmit (EmitContext ec)
4947 ILGenerator ig = ec.ig;
4949 copy.Store (ec, expr);
4951 Label[] test = new Label [rank];
4952 Label[] loop = new Label [rank];
4954 for (int i = 0; i < rank; i++) {
4955 test [i] = ig.DefineLabel ();
4956 loop [i] = ig.DefineLabel ();
4958 lengths [i].EmitThis (ec);
4959 ((ArrayAccess) access).EmitGetLength (ec, i);
4960 lengths [i].EmitStore (ec);
4963 for (int i = 0; i < rank; i++) {
4964 counter [i].Initialize (ec);
4966 ig.Emit (OpCodes.Br, test [i]);
4967 ig.MarkLabel (loop [i]);
4970 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4972 statement.Emit (ec);
4974 ig.MarkLabel (ec.LoopBegin);
4976 for (int i = rank - 1; i >= 0; i--){
4977 counter [i].Increment (ec);
4979 ig.MarkLabel (test [i]);
4980 counter [i].Emit (ec);
4981 lengths [i].Emit (ec);
4982 ig.Emit (OpCodes.Blt, loop [i]);
4985 ig.MarkLabel (ec.LoopEnd);
4989 protected class CollectionForeach : ExceptionStatement
4991 Expression variable, expr;
4992 Statement statement;
4994 TemporaryVariable enumerator;
4998 MethodGroupExpr get_enumerator;
4999 PropertyExpr get_current;
5000 MethodInfo move_next;
5001 Expression var_type;
5002 Type enumerator_type;
5004 bool enumerator_found;
5006 public CollectionForeach (Expression var_type, Expression var,
5007 Expression expr, Statement stmt, Location l)
5009 this.var_type = var_type;
5010 this.variable = var;
5016 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5018 Type return_type = mi.ReturnType;
5020 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5022 // Apply the same optimization as MS: skip the GetEnumerator
5023 // returning an IEnumerator, and use the one returning a
5024 // CharEnumerator instead. This allows us to avoid the
5025 // try-finally block and the boxing.
5030 // Ok, we can access it, now make sure that we can do something
5031 // with this `GetEnumerator'
5034 if (return_type == TypeManager.ienumerator_type ||
5035 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5036 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5038 // If it is not an interface, lets try to find the methods ourselves.
5039 // For example, if we have:
5040 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5041 // We can avoid the iface call. This is a runtime perf boost.
5042 // even bigger if we have a ValueType, because we avoid the cost
5045 // We have to make sure that both methods exist for us to take
5046 // this path. If one of the methods does not exist, we will just
5047 // use the interface. Sadly, this complex if statement is the only
5048 // way I could do this without a goto
5053 // Prefer a generic enumerator over a non-generic one.
5055 if (return_type.IsInterface && return_type.IsGenericType) {
5056 enumerator_type = return_type;
5057 if (!FetchGetCurrent (ec, return_type))
5058 get_current = new PropertyExpr (
5059 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5060 if (!FetchMoveNext (return_type))
5061 move_next = TypeManager.bool_movenext_void;
5066 if (return_type.IsInterface ||
5067 !FetchMoveNext (return_type) ||
5068 !FetchGetCurrent (ec, return_type)) {
5069 enumerator_type = return_type;
5070 move_next = TypeManager.bool_movenext_void;
5071 get_current = new PropertyExpr (
5072 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5077 // Ok, so they dont return an IEnumerable, we will have to
5078 // find if they support the GetEnumerator pattern.
5081 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5082 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",
5083 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5088 enumerator_type = return_type;
5089 is_disposable = !enumerator_type.IsSealed ||
5090 TypeManager.ImplementsInterface (
5091 enumerator_type, TypeManager.idisposable_type);
5097 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5099 bool FetchMoveNext (Type t)
5101 MemberList move_next_list;
5103 move_next_list = TypeContainer.FindMembers (
5104 t, MemberTypes.Method,
5105 BindingFlags.Public | BindingFlags.Instance,
5106 Type.FilterName, "MoveNext");
5107 if (move_next_list.Count == 0)
5110 foreach (MemberInfo m in move_next_list){
5111 MethodInfo mi = (MethodInfo) m;
5113 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5114 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5124 // Retrieves a `public T get_Current ()' method from the Type `t'
5126 bool FetchGetCurrent (EmitContext ec, Type t)
5128 PropertyExpr pe = Expression.MemberLookup (
5129 ec.ContainerType, t, "Current", MemberTypes.Property,
5130 Expression.AllBindingFlags, loc) as PropertyExpr;
5139 // Retrieves a `public void Dispose ()' method from the Type `t'
5141 static MethodInfo FetchMethodDispose (Type t)
5143 MemberList dispose_list;
5145 dispose_list = TypeContainer.FindMembers (
5146 t, MemberTypes.Method,
5147 BindingFlags.Public | BindingFlags.Instance,
5148 Type.FilterName, "Dispose");
5149 if (dispose_list.Count == 0)
5152 foreach (MemberInfo m in dispose_list){
5153 MethodInfo mi = (MethodInfo) m;
5155 if (TypeManager.GetParameterData (mi).Count == 0){
5156 if (mi.ReturnType == TypeManager.void_type)
5163 public void Error_Enumerator ()
5165 if (enumerator_found) {
5169 Report.Error (1579, loc,
5170 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5171 TypeManager.CSharpName (expr.Type));
5174 bool IsOverride (MethodInfo m)
5176 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5178 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5180 if (m is MethodBuilder)
5183 MethodInfo base_method = m.GetBaseDefinition ();
5184 return base_method != m;
5187 bool TryType (EmitContext ec, Type t)
5189 MethodGroupExpr mg = Expression.MemberLookup (
5190 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5191 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5195 MethodInfo result = null;
5196 MethodInfo tmp_move_next = null;
5197 PropertyExpr tmp_get_cur = null;
5198 Type tmp_enumerator_type = enumerator_type;
5199 foreach (MethodInfo mi in mg.Methods) {
5200 if (TypeManager.GetParameterData (mi).Count != 0)
5203 // Check whether GetEnumerator is public
5204 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5207 if (IsOverride (mi))
5210 enumerator_found = true;
5212 if (!GetEnumeratorFilter (ec, mi))
5215 if (result != null) {
5216 if (TypeManager.IsGenericType (result.ReturnType)) {
5217 if (!TypeManager.IsGenericType (mi.ReturnType))
5220 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5221 Report.SymbolRelatedToPreviousError (t);
5222 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5223 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5224 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5228 // Always prefer generics enumerators
5229 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5230 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5231 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5234 Report.SymbolRelatedToPreviousError (result);
5235 Report.SymbolRelatedToPreviousError (mi);
5236 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5237 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5242 tmp_move_next = move_next;
5243 tmp_get_cur = get_current;
5244 tmp_enumerator_type = enumerator_type;
5245 if (mi.DeclaringType == t)
5249 if (result != null) {
5250 move_next = tmp_move_next;
5251 get_current = tmp_get_cur;
5252 enumerator_type = tmp_enumerator_type;
5253 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5254 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5256 if (t != expr.Type) {
5257 expr = Convert.ExplicitConversion (
5260 throw new InternalErrorException ();
5263 get_enumerator.InstanceExpression = expr;
5264 get_enumerator.IsBase = t != expr.Type;
5272 bool ProbeCollectionType (EmitContext ec, Type t)
5274 int errors = Report.Errors;
5275 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5276 if (TryType (ec, tt))
5281 if (Report.Errors > errors)
5285 // Now try to find the method in the interfaces
5287 Type [] ifaces = TypeManager.GetInterfaces (t);
5288 foreach (Type i in ifaces){
5289 if (TryType (ec, i))
5296 public override bool Resolve (EmitContext ec)
5298 enumerator_type = TypeManager.ienumerator_type;
5299 is_disposable = true;
5301 if (!ProbeCollectionType (ec, expr.Type)) {
5302 Error_Enumerator ();
5306 VarExpr ve = var_type as VarExpr;
5308 // Infer implicitly typed local variable from foreach enumerable type
5309 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5312 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5313 if (var_type == null)
5316 enumerator = new TemporaryVariable (enumerator_type, loc);
5317 enumerator.Resolve (ec);
5319 init = new Invocation (get_enumerator, null);
5320 init = init.Resolve (ec);
5324 Expression move_next_expr;
5326 MemberInfo[] mi = new MemberInfo[] { move_next };
5327 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5328 mg.InstanceExpression = enumerator;
5330 move_next_expr = new Invocation (mg, null);
5333 get_current.InstanceExpression = enumerator;
5335 Statement block = new CollectionForeachStatement (
5336 var_type.Type, variable, get_current, statement, loc);
5338 loop = new While (move_next_expr, block, loc);
5342 FlowBranchingException branching = null;
5344 branching = ec.StartFlowBranching (this);
5346 if (!loop.Resolve (ec))
5349 if (is_disposable) {
5350 ResolveFinally (branching);
5351 ec.EndFlowBranching ();
5353 emit_finally = true;
5358 protected override void DoEmit (EmitContext ec)
5360 ILGenerator ig = ec.ig;
5362 enumerator.Store (ec, init);
5365 // Protect the code in a try/finalize block, so that
5366 // if the beast implement IDisposable, we get rid of it
5368 if (is_disposable && emit_finally)
5369 ig.BeginExceptionBlock ();
5374 // Now the finally block
5376 if (is_disposable) {
5379 ig.EndExceptionBlock ();
5384 public override void EmitFinally (EmitContext ec)
5386 ILGenerator ig = ec.ig;
5388 if (enumerator_type.IsValueType) {
5389 MethodInfo mi = FetchMethodDispose (enumerator_type);
5391 enumerator.EmitLoadAddress (ec);
5392 ig.Emit (OpCodes.Call, mi);
5394 enumerator.Emit (ec);
5395 ig.Emit (OpCodes.Box, enumerator_type);
5396 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5399 Label call_dispose = ig.DefineLabel ();
5401 enumerator.Emit (ec);
5402 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5403 ig.Emit (OpCodes.Dup);
5404 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5405 ig.Emit (OpCodes.Pop);
5407 Label end_finally = ig.DefineLabel ();
5408 ig.Emit (OpCodes.Br, end_finally);
5410 ig.MarkLabel (call_dispose);
5411 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5412 ig.MarkLabel (end_finally);
5417 protected class CollectionForeachStatement : Statement
5420 Expression variable, current, conv;
5421 Statement statement;
5424 public CollectionForeachStatement (Type type, Expression variable,
5425 Expression current, Statement statement,
5429 this.variable = variable;
5430 this.current = current;
5431 this.statement = statement;
5435 public override bool Resolve (EmitContext ec)
5437 current = current.Resolve (ec);
5438 if (current == null)
5441 conv = Convert.ExplicitConversion (ec, current, type, loc);
5445 assign = new Assign (variable, conv, loc);
5446 if (assign.Resolve (ec) == null)
5449 if (!statement.Resolve (ec))
5455 protected override void DoEmit (EmitContext ec)
5457 assign.EmitStatement (ec);
5458 statement.Emit (ec);
5462 protected override void CloneTo (CloneContext clonectx, Statement t)
5464 Foreach target = (Foreach) t;
5466 target.type = type.Clone (clonectx);
5467 target.variable = variable.Clone (clonectx);
5468 target.expr = expr.Clone (clonectx);
5469 target.statement = statement.Clone (clonectx);