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 `D' 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)
1434 // Only this kind is created by the parser.
1435 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1440 /// Block represents a C# block.
1444 /// This class is used in a number of places: either to represent
1445 /// explicit blocks that the programmer places or implicit blocks.
1447 /// Implicit blocks are used as labels or to introduce variable
1450 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1451 /// they contain extra information that is not necessary on normal blocks.
1453 public class Block : Statement {
1454 public Block Parent;
1455 public readonly Location StartLocation;
1456 public Location EndLocation = Location.Null;
1458 public ExplicitBlock Explicit;
1459 public ToplevelBlock Toplevel;
1462 public enum Flags : byte {
1465 VariablesInitialized = 4,
1469 HasVarargs = 64, // Used in ToplevelBlock
1472 protected Flags flags;
1474 public bool Unchecked {
1475 get { return (flags & Flags.Unchecked) != 0; }
1476 set { flags |= Flags.Unchecked; }
1479 public bool Unsafe {
1480 get { return (flags & Flags.Unsafe) != 0; }
1481 set { flags |= Flags.Unsafe; }
1485 // The statements in this block
1487 protected ArrayList statements;
1491 // An array of Blocks. We keep track of children just
1492 // to generate the local variable declarations.
1494 // Statements and child statements are handled through the
1500 // Labels. (label, block) pairs.
1505 // Keeps track of (name, type) pairs
1507 IDictionary variables;
1510 // Keeps track of constants
1511 Hashtable constants;
1514 // Temporary variables.
1516 ArrayList temporary_variables;
1519 // If this is a switch section, the enclosing switch block.
1523 ExpressionStatement scope_init;
1525 ArrayList anonymous_children;
1527 protected static int id;
1531 int assignable_slots;
1532 protected ScopeInfo scope_info;
1533 bool unreachable_shown;
1536 public Block (Block parent)
1537 : this (parent, (Flags) 0, Location.Null, Location.Null)
1540 public Block (Block parent, Flags flags)
1541 : this (parent, flags, Location.Null, Location.Null)
1544 public Block (Block parent, Location start, Location end)
1545 : this (parent, (Flags) 0, start, end)
1548 public Block (Block parent, Flags flags, Location start, Location end)
1550 if (parent != null) {
1551 parent.AddChild (this);
1553 // the appropriate constructors will fixup these fields
1554 Toplevel = parent.Toplevel;
1555 Explicit = parent.Explicit;
1558 this.Parent = parent;
1560 this.StartLocation = start;
1561 this.EndLocation = end;
1564 statements = new ArrayList ();
1567 public Block CreateSwitchBlock (Location start)
1569 // FIXME: should this be implicit?
1570 Block new_block = new ExplicitBlock (this, start, start);
1571 new_block.switch_block = this;
1576 get { return this_id; }
1579 public IDictionary Variables {
1581 if (variables == null)
1582 variables = new ListDictionary ();
1587 void AddChild (Block b)
1589 if (children == null)
1590 children = new ArrayList ();
1595 public void SetEndLocation (Location loc)
1600 protected static void Error_158 (string name, Location loc)
1602 Report.Error (158, loc, "The label `{0}' shadows another label " +
1603 "by the same name in a contained scope", name);
1607 /// Adds a label to the current block.
1611 /// false if the name already exists in this block. true
1615 public bool AddLabel (LabeledStatement target)
1617 if (switch_block != null)
1618 return switch_block.AddLabel (target);
1620 string name = target.Name;
1623 while (cur != null) {
1624 LabeledStatement s = cur.DoLookupLabel (name);
1626 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1627 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1631 if (this == Explicit)
1637 while (cur != null) {
1638 if (cur.DoLookupLabel (name) != null) {
1639 Error_158 (name, target.loc);
1643 if (children != null) {
1644 foreach (Block b in children) {
1645 LabeledStatement s = b.DoLookupLabel (name);
1649 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1650 Error_158 (name, target.loc);
1658 Toplevel.CheckError158 (name, target.loc);
1661 labels = new Hashtable ();
1663 labels.Add (name, target);
1667 public LabeledStatement LookupLabel (string name)
1669 LabeledStatement s = DoLookupLabel (name);
1673 if (children == null)
1676 foreach (Block child in children) {
1677 if (Explicit != child.Explicit)
1680 s = child.LookupLabel (name);
1688 LabeledStatement DoLookupLabel (string name)
1690 if (switch_block != null)
1691 return switch_block.LookupLabel (name);
1694 if (labels.Contains (name))
1695 return ((LabeledStatement) labels [name]);
1700 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1703 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1704 while (kvi == null) {
1705 b = b.Explicit.Parent;
1708 kvi = b.Explicit.GetKnownVariable (name);
1714 // Is kvi.Block nested inside 'b'
1715 if (b.Explicit != kvi.Block.Explicit) {
1717 // If a variable by the same name it defined in a nested block of this
1718 // block, we violate the invariant meaning in a block.
1721 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1722 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1727 // It's ok if the definition is in a nested subblock of b, but not
1728 // nested inside this block -- a definition in a sibling block
1729 // should not affect us.
1735 // Block 'b' and kvi.Block are the same textual block.
1736 // However, different variables are extant.
1738 // Check if the variable is in scope in both blocks. We use
1739 // an indirect check that depends on AddVariable doing its
1740 // part in maintaining the invariant-meaning-in-block property.
1742 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1746 // Even though we detected the error when the name is used, we
1747 // treat it as if the variable declaration was in error.
1749 Report.SymbolRelatedToPreviousError (loc, name);
1750 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1754 public LocalInfo AddVariable (Expression type, string name, Location l)
1756 LocalInfo vi = GetLocalInfo (name);
1758 Report.SymbolRelatedToPreviousError (vi.Location, name);
1759 if (Explicit == vi.Block.Explicit)
1760 Report.Error (128, l,
1761 "A local variable named `{0}' is already defined in this scope", name);
1763 Error_AlreadyDeclared (l, name, "parent");
1767 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1769 Report.SymbolRelatedToPreviousError (pi.Location, name);
1770 Error_AlreadyDeclared (loc, name,
1771 pi.Block == Toplevel ? "method argument" : "parent or current");
1774 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1776 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1777 Error_AlreadyDeclared (l, name, "child");
1781 vi = new LocalInfo (type, name, this, l);
1782 Variables.Add (name, vi);
1783 Explicit.AddKnownVariable (name, vi);
1785 if ((flags & Flags.VariablesInitialized) != 0)
1786 throw new InternalErrorException ("block has already been resolved");
1791 protected static void Error_AlreadyDeclared (Location loc, string var, string reason)
1793 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1794 "in this scope because it would give a different meaning " +
1795 "to `{0}', which is already used in a `{1}' scope " +
1796 "to denote something else", var, reason);
1799 public bool AddConstant (Expression type, string name, Expression value, Location l)
1801 if (AddVariable (type, name, l) == null)
1804 if (constants == null)
1805 constants = new Hashtable ();
1807 constants.Add (name, value);
1809 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1814 static int next_temp_id = 0;
1816 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1818 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1820 if (temporary_variables == null)
1821 temporary_variables = new ArrayList ();
1823 int id = ++next_temp_id;
1824 string name = "$s_" + id.ToString ();
1826 LocalInfo li = new LocalInfo (te, name, this, loc);
1827 li.CompilerGenerated = true;
1828 temporary_variables.Add (li);
1832 public LocalInfo GetLocalInfo (string name)
1834 for (Block b = this; b != null; b = b.Parent) {
1835 if (b.variables != null) {
1836 LocalInfo ret = b.variables [name] as LocalInfo;
1844 public Expression GetVariableType (string name)
1846 LocalInfo vi = GetLocalInfo (name);
1847 return vi == null ? null : vi.Type;
1850 public Expression GetConstantExpression (string name)
1852 for (Block b = this; b != null; b = b.Parent) {
1853 if (b.constants != null) {
1854 Expression ret = b.constants [name] as Expression;
1862 public void AddStatement (Statement s)
1865 flags |= Flags.BlockUsed;
1869 get { return (flags & Flags.BlockUsed) != 0; }
1874 flags |= Flags.BlockUsed;
1877 public bool HasRet {
1878 get { return (flags & Flags.HasRet) != 0; }
1881 public bool IsDestructor {
1882 get { return (flags & Flags.IsDestructor) != 0; }
1885 public void SetDestructor ()
1887 flags |= Flags.IsDestructor;
1890 public int AssignableSlots {
1892 if ((flags & Flags.VariablesInitialized) == 0)
1893 throw new Exception ("Variables have not been initialized yet");
1894 return assignable_slots;
1898 public ScopeInfo ScopeInfo {
1899 get { return scope_info; }
1902 public ScopeInfo CreateScopeInfo ()
1904 if (scope_info == null)
1905 scope_info = ScopeInfo.CreateScope (this);
1910 public ArrayList AnonymousChildren {
1911 get { return anonymous_children; }
1914 public void AddAnonymousChild (ToplevelBlock b)
1916 if (anonymous_children == null)
1917 anonymous_children = new ArrayList ();
1919 anonymous_children.Add (b);
1922 void DoResolveConstants (EmitContext ec)
1924 if (constants == null)
1927 if (variables == null)
1928 throw new InternalErrorException ("cannot happen");
1930 foreach (DictionaryEntry de in variables) {
1931 string name = (string) de.Key;
1932 LocalInfo vi = (LocalInfo) de.Value;
1933 Type variable_type = vi.VariableType;
1935 if (variable_type == null)
1938 Expression cv = (Expression) constants [name];
1942 // Don't let 'const int Foo = Foo;' succeed.
1943 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1944 // which in turn causes the 'must be constant' error to be triggered.
1945 constants.Remove (name);
1947 if (!Const.IsConstantTypeValid (variable_type)) {
1948 Const.Error_InvalidConstantType (variable_type, loc);
1952 ec.CurrentBlock = this;
1954 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1955 e = cv.Resolve (ec);
1960 Constant ce = e as Constant;
1962 Const.Error_ExpressionMustBeConstant (vi.Location, name);
1966 e = ce.ConvertImplicitly (variable_type);
1968 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
1969 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
1971 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
1975 constants.Add (name, e);
1976 vi.IsConstant = true;
1980 protected void ResolveMeta (EmitContext ec, int offset)
1982 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1984 // If some parent block was unsafe, we remain unsafe even if this block
1985 // isn't explicitly marked as such.
1986 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
1987 flags |= Flags.VariablesInitialized;
1989 if (variables != null) {
1990 foreach (LocalInfo li in variables.Values) {
1991 if (!li.Resolve (ec))
1993 li.VariableInfo = new VariableInfo (li, offset);
1994 offset += li.VariableInfo.Length;
1997 assignable_slots = offset;
1999 DoResolveConstants (ec);
2001 if (children == null)
2003 foreach (Block b in children)
2004 b.ResolveMeta (ec, offset);
2009 // Emits the local variable declarations for a block
2011 public virtual void EmitMeta (EmitContext ec)
2013 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2014 if (ScopeInfo != null) {
2015 scope_init = ScopeInfo.GetScopeInitializer (ec);
2016 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2020 if (variables != null){
2021 foreach (LocalInfo vi in variables.Values)
2022 vi.ResolveVariable (ec);
2025 if (temporary_variables != null) {
2026 foreach (LocalInfo vi in temporary_variables)
2027 vi.ResolveVariable (ec);
2030 if (children != null){
2031 foreach (Block b in children)
2036 void UsageWarning (FlowBranching.UsageVector vector)
2040 if ((variables != null) && (Report.WarningLevel >= 3)) {
2041 foreach (DictionaryEntry de in variables){
2042 LocalInfo vi = (LocalInfo) de.Value;
2047 name = (string) de.Key;
2049 // vi.VariableInfo can be null for 'catch' variables
2050 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2051 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2053 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2059 private void CheckPossibleMistakenEmptyStatement (Statement s)
2063 // Some statements are wrapped by a Block. Since
2064 // others' internal could be changed, here I treat
2065 // them as possibly wrapped by Block equally.
2066 Block b = s as Block;
2067 if (b != null && b.statements.Count == 1)
2068 s = (Statement) b.statements [0];
2071 body = ((Lock) s).Statement;
2073 body = ((For) s).Statement;
2074 else if (s is Foreach)
2075 body = ((Foreach) s).Statement;
2076 else if (s is While)
2077 body = ((While) s).Statement;
2078 else if (s is Using)
2079 body = ((Using) s).Statement;
2080 else if (s is Fixed)
2081 body = ((Fixed) s).Statement;
2085 if (body == null || body is EmptyStatement)
2086 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2089 public override bool Resolve (EmitContext ec)
2091 Block prev_block = ec.CurrentBlock;
2094 int errors = Report.Errors;
2096 ec.CurrentBlock = this;
2097 ec.StartFlowBranching (this);
2099 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2102 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2103 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2104 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2105 // responsible for handling the situation.
2107 int statement_count = statements.Count;
2108 for (int ix = 0; ix < statement_count; ix++){
2109 Statement s = (Statement) statements [ix];
2110 // Check possible empty statement (CS0642)
2111 if (Report.WarningLevel >= 3 &&
2112 ix + 1 < statement_count &&
2113 statements [ix + 1] is Block)
2114 CheckPossibleMistakenEmptyStatement (s);
2117 // Warn if we detect unreachable code.
2120 if (s is EmptyStatement)
2124 ((Block) s).unreachable = true;
2126 if (!unreachable_shown && !(s is LabeledStatement)) {
2127 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2128 unreachable_shown = true;
2133 // Note that we're not using ResolveUnreachable() for unreachable
2134 // statements here. ResolveUnreachable() creates a temporary
2135 // flow branching and kills it afterwards. This leads to problems
2136 // if you have two unreachable statements where the first one
2137 // assigns a variable and the second one tries to access it.
2140 if (!s.Resolve (ec)) {
2141 if (ec.IsInProbingMode)
2145 statements [ix] = EmptyStatement.Value;
2149 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2150 statements [ix] = EmptyStatement.Value;
2152 num_statements = ix + 1;
2154 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2155 if (unreachable && s is LabeledStatement)
2156 throw new InternalErrorException ("should not happen");
2159 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2160 ec.CurrentBranching, statement_count, num_statements);
2165 while (ec.CurrentBranching is FlowBranchingLabeled)
2166 ec.EndFlowBranching ();
2168 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2170 ec.CurrentBlock = prev_block;
2172 // If we're a non-static `struct' constructor which doesn't have an
2173 // initializer, then we must initialize all of the struct's fields.
2174 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2177 if ((labels != null) && (Report.WarningLevel >= 2)) {
2178 foreach (LabeledStatement label in labels.Values)
2179 if (!label.HasBeenReferenced)
2180 Report.Warning (164, 2, label.loc,
2181 "This label has not been referenced");
2184 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2186 if (vector.IsUnreachable)
2187 flags |= Flags.HasRet;
2189 if (ok && (errors == Report.Errors)) {
2190 UsageWarning (vector);
2196 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2198 unreachable_shown = true;
2202 Report.Warning (162, 2, loc, "Unreachable code detected");
2204 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2205 bool ok = Resolve (ec);
2206 ec.KillFlowBranching ();
2211 protected override void DoEmit (EmitContext ec)
2213 for (int ix = 0; ix < num_statements; ix++){
2214 Statement s = (Statement) statements [ix];
2219 public override void Emit (EmitContext ec)
2221 Block prev_block = ec.CurrentBlock;
2223 ec.CurrentBlock = this;
2225 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2226 bool is_lexical_block = this == Explicit && Parent != null;
2228 if (emit_debug_info) {
2229 if (is_lexical_block)
2232 ec.Mark (StartLocation, true);
2233 if (scope_init != null)
2234 scope_init.EmitStatement (ec);
2236 ec.Mark (EndLocation, true);
2238 if (emit_debug_info) {
2239 if (is_lexical_block)
2242 if (variables != null) {
2243 foreach (DictionaryEntry de in variables) {
2244 string name = (string) de.Key;
2245 LocalInfo vi = (LocalInfo) de.Value;
2247 vi.EmitSymbolInfo (ec, name);
2252 ec.CurrentBlock = prev_block;
2255 public override string ToString ()
2257 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2260 protected override void CloneTo (CloneContext clonectx, Statement t)
2262 Block target = (Block) t;
2264 clonectx.AddBlockMap (this, target);
2266 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2267 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2269 target.Parent = clonectx.RemapBlockCopy (Parent);
2271 if (variables != null){
2272 target.variables = new Hashtable ();
2274 foreach (DictionaryEntry de in variables){
2275 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2276 target.variables [de.Key] = newlocal;
2277 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2281 target.statements = new ArrayList (statements.Count);
2282 foreach (Statement s in statements)
2283 target.statements.Add (s.Clone (clonectx));
2285 if (target.children != null){
2286 target.children = new ArrayList (children.Count);
2287 foreach (Block b in children){
2288 target.children.Add (clonectx.LookupBlock (b));
2293 // TODO: labels, switch_block, constants (?), anonymous_children
2298 public class ExplicitBlock : Block {
2299 public ExplicitBlock (Block parent, Location start, Location end)
2300 : this (parent, (Flags) 0, start, end)
2304 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2305 : base (parent, flags, start, end)
2307 this.Explicit = this;
2310 Hashtable known_variables;
2313 // Marks a variable with name @name as being used in this or a child block.
2314 // If a variable name has been used in a child block, it's illegal to
2315 // declare a variable with the same name in the current block.
2317 internal void AddKnownVariable (string name, IKnownVariable info)
2319 if (known_variables == null)
2320 known_variables = new Hashtable ();
2322 known_variables [name] = info;
2325 Parent.Explicit.AddKnownVariable (name, info);
2328 internal IKnownVariable GetKnownVariable (string name)
2330 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2333 protected override void CloneTo (CloneContext clonectx, Statement t)
2335 ExplicitBlock target = (ExplicitBlock) t;
2336 target.known_variables = null;
2337 base.CloneTo (clonectx, t);
2341 public class ToplevelParameterInfo : IKnownVariable {
2342 public readonly ToplevelBlock Block;
2343 public readonly int Index;
2344 public VariableInfo VariableInfo;
2346 Block IKnownVariable.Block {
2347 get { return Block; }
2349 public Parameter Parameter {
2350 get { return Block.Parameters [Index]; }
2352 public Location Location {
2353 get { return Parameter.Location; }
2356 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2364 // A toplevel block contains extra information, the split is done
2365 // only to separate information that would otherwise bloat the more
2366 // lightweight Block.
2368 // In particular, this was introduced when the support for Anonymous
2369 // Methods was implemented.
2371 public class ToplevelBlock : ExplicitBlock {
2372 GenericMethod generic;
2373 FlowBranchingToplevel top_level_branching;
2374 AnonymousContainer anonymous_container;
2375 RootScopeInfo root_scope;
2376 Parameters parameters;
2377 ToplevelParameterInfo[] parameter_info;
2379 public bool HasVarargs {
2380 get { return (flags & Flags.HasVarargs) != 0; }
2381 set { flags |= Flags.HasVarargs; }
2384 public bool IsIterator {
2385 get { return (flags & Flags.IsIterator) != 0; }
2389 // The parameters for the block.
2391 public Parameters Parameters {
2392 get { return parameters; }
2395 public bool CompleteContexts (EmitContext ec)
2397 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2399 if (root_scope != null)
2400 root_scope.LinkScopes ();
2402 if (Parent == null && root_scope != null) {
2403 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2405 if (root_scope.DefineType () == null)
2407 if (!root_scope.ResolveType ())
2409 if (!root_scope.ResolveMembers ())
2411 if (!root_scope.DefineMembers ())
2418 public GenericMethod GenericMethod {
2419 get { return generic; }
2422 public ToplevelBlock Container {
2423 get { return Parent == null ? null : Parent.Toplevel; }
2426 public AnonymousContainer AnonymousContainer {
2427 get { return anonymous_container; }
2428 set { anonymous_container = value; }
2431 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2432 this (parent, (Flags) 0, parameters, start)
2436 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2437 this (parent, parameters, start)
2439 this.generic = generic;
2442 public ToplevelBlock (Parameters parameters, Location start) :
2443 this (null, (Flags) 0, parameters, start)
2447 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2448 this (null, flags, parameters, start)
2452 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2453 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2454 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2455 base (null, flags, start, Location.Null)
2457 this.Toplevel = this;
2459 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2460 this.Parent = parent;
2462 parent.AddAnonymousChild (this);
2464 if (this.parameters.Count != 0)
2465 ProcessParameters ();
2468 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2472 protected override void CloneTo (CloneContext clonectx, Statement t)
2474 ToplevelBlock target = (ToplevelBlock) t;
2475 base.CloneTo (clonectx, t);
2477 if (parameters.Count != 0)
2478 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2479 for (int i = 0; i < parameters.Count; ++i)
2480 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2483 public bool CheckError158 (string name, Location loc)
2485 if (AnonymousChildren != null) {
2486 foreach (ToplevelBlock child in AnonymousChildren) {
2487 if (!child.CheckError158 (name, loc))
2492 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2493 if (!c.DoCheckError158 (name, loc))
2500 void ProcessParameters ()
2502 int n = parameters.Count;
2503 parameter_info = new ToplevelParameterInfo [n];
2504 for (int i = 0; i < n; ++i) {
2505 parameter_info [i] = new ToplevelParameterInfo (this, i);
2507 Parameter p = parameters [i];
2511 string name = p.Name;
2512 LocalInfo vi = GetLocalInfo (name);
2514 Report.SymbolRelatedToPreviousError (vi.Location, name);
2515 Error_AlreadyDeclared (loc, name, "parent or current");
2519 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2521 Report.SymbolRelatedToPreviousError (pi.Location, name);
2522 Error_AlreadyDeclared (loc, name, "parent or current");
2526 AddKnownVariable (name, parameter_info [i]);
2529 // mark this block as "used" so that we create local declarations in a sub-block
2530 // FIXME: This appears to uncover a lot of bugs
2534 bool DoCheckError158 (string name, Location loc)
2536 LabeledStatement s = LookupLabel (name);
2538 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2539 Error_158 (name, loc);
2546 public RootScopeInfo CreateRootScope (TypeContainer host)
2548 if (root_scope != null)
2551 if (Container == null)
2552 root_scope = new RootScopeInfo (
2553 this, host, generic, StartLocation);
2555 if (scope_info != null)
2556 throw new InternalErrorException ();
2558 scope_info = root_scope;
2562 public void CreateIteratorHost (RootScopeInfo root)
2564 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2566 if (Parent != null || root_scope != null)
2567 throw new InternalErrorException ();
2569 scope_info = root_scope = root;
2572 public RootScopeInfo RootScope {
2574 if (root_scope != null)
2576 else if (Container != null)
2577 return Container.RootScope;
2583 public FlowBranchingToplevel TopLevelBranching {
2584 get { return top_level_branching; }
2588 // This is used if anonymous methods are used inside an iterator
2589 // (see 2test-22.cs for an example).
2591 // The AnonymousMethod is created while parsing - at a time when we don't
2592 // know yet that we're inside an iterator, so it's `Container' is initially
2593 // null. Later on, when resolving the iterator, we need to move the
2594 // anonymous method into that iterator.
2596 public void ReParent (ToplevelBlock new_parent)
2598 if ((flags & Flags.VariablesInitialized) != 0)
2599 throw new InternalErrorException ("block has already been resolved");
2601 Parent = new_parent;
2605 // Returns a `ParameterReference' for the given name, or null if there
2606 // is no such parameter
2608 public ParameterReference GetParameterReference (string name, Location loc)
2610 ToplevelParameterInfo p = GetParameterInfo (name);
2611 return p == null ? null : new ParameterReference (this, p, loc);
2614 public ToplevelParameterInfo GetParameterInfo (string name)
2617 for (ToplevelBlock t = this; t != null; t = t.Container) {
2618 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2620 return t.parameter_info [idx];
2626 // Whether the parameter named `name' is local to this block,
2627 // or false, if the parameter belongs to an encompassing block.
2629 public bool IsLocalParameter (string name)
2631 return Parameters.GetParameterByName (name) != null;
2635 // Whether the `name' is a parameter reference
2637 public bool IsParameterReference (string name)
2639 for (ToplevelBlock t = this; t != null; t = t.Container) {
2640 if (t.IsLocalParameter (name))
2646 LocalInfo this_variable = null;
2649 // Returns the "this" instance variable of this block.
2650 // See AddThisVariable() for more information.
2652 public LocalInfo ThisVariable {
2653 get { return this_variable; }
2658 // This is used by non-static `struct' constructors which do not have an
2659 // initializer - in this case, the constructor must initialize all of the
2660 // struct's fields. To do this, we add a "this" variable and use the flow
2661 // analysis code to ensure that it's been fully initialized before control
2662 // leaves the constructor.
2664 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2666 if (this_variable == null) {
2667 this_variable = new LocalInfo (ds, this, l);
2668 this_variable.Used = true;
2669 this_variable.IsThis = true;
2671 Variables.Add ("this", this_variable);
2674 return this_variable;
2677 public bool IsThisAssigned (EmitContext ec)
2679 return this_variable == null || this_variable.IsThisAssigned (ec);
2682 public bool ResolveMeta (EmitContext ec, Parameters ip)
2684 int errors = Report.Errors;
2685 int orig_count = parameters.Count;
2687 if (top_level_branching != null)
2693 // Assert: orig_count != parameter.Count => orig_count == 0
2694 if (orig_count != 0 && orig_count != parameters.Count)
2695 throw new InternalErrorException ("parameter information mismatch");
2697 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2699 for (int i = 0; i < orig_count; ++i) {
2700 Parameter.Modifier mod = parameters.ParameterModifier (i);
2702 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2705 VariableInfo vi = new VariableInfo (ip, i, offset);
2706 parameter_info [i].VariableInfo = vi;
2707 offset += vi.Length;
2710 ResolveMeta (ec, offset);
2712 top_level_branching = ec.StartFlowBranching (this);
2714 return Report.Errors == errors;
2718 // Check whether all `out' parameters have been assigned.
2720 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2722 if (vector.IsUnreachable)
2725 int n = parameter_info == null ? 0 : parameter_info.Length;
2727 for (int i = 0; i < n; i++) {
2728 VariableInfo var = parameter_info [i].VariableInfo;
2733 if (vector.IsAssigned (var, false))
2736 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2741 public override void EmitMeta (EmitContext ec)
2744 parameters.ResolveVariable (this);
2747 public void MakeIterator (Iterator iterator)
2749 flags |= Flags.IsIterator;
2751 Block block = new ExplicitBlock (this, StartLocation, EndLocation);
2752 foreach (Statement stmt in statements)
2753 block.AddStatement (stmt);
2754 statements.Clear ();
2755 statements.Add (new MoveNextStatement (iterator, block));
2758 protected class MoveNextStatement : Statement {
2762 public MoveNextStatement (Iterator iterator, Block block)
2764 this.iterator = iterator;
2766 this.loc = iterator.Location;
2769 public override bool Resolve (EmitContext ec)
2771 return block.Resolve (ec);
2774 protected override void DoEmit (EmitContext ec)
2776 iterator.EmitMoveNext (ec, block);
2780 public override string ToString ()
2782 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2783 root_scope, anonymous_container != null ?
2784 anonymous_container.Scope : null);
2788 public class SwitchLabel {
2795 Label il_label_code;
2796 bool il_label_code_set;
2798 public static readonly object NullStringCase = new object ();
2801 // if expr == null, then it is the default case.
2803 public SwitchLabel (Expression expr, Location l)
2809 public Expression Label {
2815 public object Converted {
2821 public Label GetILLabel (EmitContext ec)
2824 il_label = ec.ig.DefineLabel ();
2825 il_label_set = true;
2830 public Label GetILLabelCode (EmitContext ec)
2832 if (!il_label_code_set){
2833 il_label_code = ec.ig.DefineLabel ();
2834 il_label_code_set = true;
2836 return il_label_code;
2840 // Resolves the expression, reduces it to a literal if possible
2841 // and then converts it to the requested type.
2843 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2845 Expression e = label.Resolve (ec);
2850 Constant c = e as Constant;
2852 Report.Error (150, loc, "A constant value is expected");
2856 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2857 converted = NullStringCase;
2861 if (allow_nullable && c.GetValue () == null) {
2862 converted = NullStringCase;
2866 c = c.ImplicitConversionRequired (required_type, loc);
2870 converted = c.GetValue ();
2874 public void Erorr_AlreadyOccurs (Type switchType, SwitchLabel collisionWith)
2877 if (converted == null)
2879 else if (converted == NullStringCase)
2881 else if (TypeManager.IsEnumType (switchType))
2882 label = TypeManager.CSharpEnumValue (switchType, converted);
2884 label = converted.ToString ();
2886 Report.SymbolRelatedToPreviousError (collisionWith.loc, null);
2887 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2890 public SwitchLabel Clone (CloneContext clonectx)
2892 return new SwitchLabel (label.Clone (clonectx), loc);
2896 public class SwitchSection {
2897 // An array of SwitchLabels.
2898 public readonly ArrayList Labels;
2899 public readonly Block Block;
2901 public SwitchSection (ArrayList labels, Block block)
2907 public SwitchSection Clone (CloneContext clonectx)
2909 ArrayList cloned_labels = new ArrayList ();
2911 foreach (SwitchLabel sl in cloned_labels)
2912 cloned_labels.Add (sl.Clone (clonectx));
2914 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
2918 public class Switch : Statement {
2919 public ArrayList Sections;
2920 public Expression Expr;
2923 /// Maps constants whose type type SwitchType to their SwitchLabels.
2925 public IDictionary Elements;
2928 /// The governing switch type
2930 public Type SwitchType;
2935 Label default_target;
2937 Expression new_expr;
2939 SwitchSection constant_section;
2940 SwitchSection default_section;
2944 // Nullable Types support for GMCS.
2946 Nullable.Unwrap unwrap;
2948 protected bool HaveUnwrap {
2949 get { return unwrap != null; }
2952 protected bool HaveUnwrap {
2953 get { return false; }
2958 // The types allowed to be implicitly cast from
2959 // on the governing type
2961 static Type [] allowed_types;
2963 public Switch (Expression e, ArrayList sects, Location l)
2970 public bool GotDefault {
2972 return default_section != null;
2976 public Label DefaultTarget {
2978 return default_target;
2983 // Determines the governing type for a switch. The returned
2984 // expression might be the expression from the switch, or an
2985 // expression that includes any potential conversions to the
2986 // integral types or to string.
2988 Expression SwitchGoverningType (EmitContext ec, Expression expr)
2990 Type t = TypeManager.DropGenericTypeArguments (expr.Type);
2992 if (t == TypeManager.byte_type ||
2993 t == TypeManager.sbyte_type ||
2994 t == TypeManager.ushort_type ||
2995 t == TypeManager.short_type ||
2996 t == TypeManager.uint32_type ||
2997 t == TypeManager.int32_type ||
2998 t == TypeManager.uint64_type ||
2999 t == TypeManager.int64_type ||
3000 t == TypeManager.char_type ||
3001 t == TypeManager.string_type ||
3002 t == TypeManager.bool_type ||
3003 t.IsSubclassOf (TypeManager.enum_type))
3006 if (allowed_types == null){
3007 allowed_types = new Type [] {
3008 TypeManager.sbyte_type,
3009 TypeManager.byte_type,
3010 TypeManager.short_type,
3011 TypeManager.ushort_type,
3012 TypeManager.int32_type,
3013 TypeManager.uint32_type,
3014 TypeManager.int64_type,
3015 TypeManager.uint64_type,
3016 TypeManager.char_type,
3017 TypeManager.string_type,
3018 TypeManager.bool_type
3023 // Try to find a *user* defined implicit conversion.
3025 // If there is no implicit conversion, or if there are multiple
3026 // conversions, we have to report an error
3028 Expression converted = null;
3029 foreach (Type tt in allowed_types){
3032 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3037 // Ignore over-worked ImplicitUserConversions that do
3038 // an implicit conversion in addition to the user conversion.
3040 if (!(e is UserCast))
3043 if (converted != null){
3044 Report.ExtraInformation (
3046 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3047 TypeManager.CSharpName (expr.Type)));
3057 // Performs the basic sanity checks on the switch statement
3058 // (looks for duplicate keys and non-constant expressions).
3060 // It also returns a hashtable with the keys that we will later
3061 // use to compute the switch tables
3063 bool CheckSwitch (EmitContext ec)
3066 Elements = Sections.Count > 10 ?
3067 (IDictionary)new Hashtable () :
3068 (IDictionary)new ListDictionary ();
3070 foreach (SwitchSection ss in Sections){
3071 foreach (SwitchLabel sl in ss.Labels){
3072 if (sl.Label == null){
3073 if (default_section != null){
3074 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3077 default_section = ss;
3081 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3086 object key = sl.Converted;
3088 Elements.Add (key, sl);
3089 } catch (ArgumentException) {
3090 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3098 void EmitObjectInteger (ILGenerator ig, object k)
3101 IntConstant.EmitInt (ig, (int) k);
3102 else if (k is Constant) {
3103 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3106 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3109 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3111 IntConstant.EmitInt (ig, (int) (long) k);
3112 ig.Emit (OpCodes.Conv_I8);
3115 LongConstant.EmitLong (ig, (long) k);
3117 else if (k is ulong)
3119 ulong ul = (ulong) k;
3122 IntConstant.EmitInt (ig, unchecked ((int) ul));
3123 ig.Emit (OpCodes.Conv_U8);
3127 LongConstant.EmitLong (ig, unchecked ((long) ul));
3131 IntConstant.EmitInt (ig, (int) ((char) k));
3132 else if (k is sbyte)
3133 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3135 IntConstant.EmitInt (ig, (int) ((byte) k));
3136 else if (k is short)
3137 IntConstant.EmitInt (ig, (int) ((short) k));
3138 else if (k is ushort)
3139 IntConstant.EmitInt (ig, (int) ((ushort) k));
3141 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3143 throw new Exception ("Unhandled case");
3146 // structure used to hold blocks of keys while calculating table switch
3147 class KeyBlock : IComparable
3149 public KeyBlock (long _nFirst)
3151 nFirst = nLast = _nFirst;
3155 public ArrayList rgKeys = null;
3156 // how many items are in the bucket
3157 public int Size = 1;
3160 get { return (int) (nLast - nFirst + 1); }
3162 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
3164 return kbLast.nLast - kbFirst.nFirst + 1;
3166 public int CompareTo (object obj)
3168 KeyBlock kb = (KeyBlock) obj;
3169 int nLength = Length;
3170 int nLengthOther = kb.Length;
3171 if (nLengthOther == nLength)
3172 return (int) (kb.nFirst - nFirst);
3173 return nLength - nLengthOther;
3178 /// This method emits code for a lookup-based switch statement (non-string)
3179 /// Basically it groups the cases into blocks that are at least half full,
3180 /// and then spits out individual lookup opcodes for each block.
3181 /// It emits the longest blocks first, and short blocks are just
3182 /// handled with direct compares.
3184 /// <param name="ec"></param>
3185 /// <param name="val"></param>
3186 /// <returns></returns>
3187 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3189 int cElements = Elements.Count;
3190 object [] rgKeys = new object [cElements];
3191 Elements.Keys.CopyTo (rgKeys, 0);
3192 Array.Sort (rgKeys);
3194 // initialize the block list with one element per key
3195 ArrayList rgKeyBlocks = new ArrayList ();
3196 foreach (object key in rgKeys)
3197 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3200 // iteratively merge the blocks while they are at least half full
3201 // there's probably a really cool way to do this with a tree...
3202 while (rgKeyBlocks.Count > 1)
3204 ArrayList rgKeyBlocksNew = new ArrayList ();
3205 kbCurr = (KeyBlock) rgKeyBlocks [0];
3206 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3208 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3209 if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
3212 kbCurr.nLast = kb.nLast;
3213 kbCurr.Size += kb.Size;
3217 // start a new block
3218 rgKeyBlocksNew.Add (kbCurr);
3222 rgKeyBlocksNew.Add (kbCurr);
3223 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3225 rgKeyBlocks = rgKeyBlocksNew;
3228 // initialize the key lists
3229 foreach (KeyBlock kb in rgKeyBlocks)
3230 kb.rgKeys = new ArrayList ();
3232 // fill the key lists
3234 if (rgKeyBlocks.Count > 0) {
3235 kbCurr = (KeyBlock) rgKeyBlocks [0];
3236 foreach (object key in rgKeys)
3238 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
3239 System.Convert.ToInt64 (key) > kbCurr.nLast;
3241 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3242 kbCurr.rgKeys.Add (key);
3246 // sort the blocks so we can tackle the largest ones first
3247 rgKeyBlocks.Sort ();
3249 // okay now we can start...
3250 ILGenerator ig = ec.ig;
3251 Label lblEnd = ig.DefineLabel (); // at the end ;-)
3252 Label lblDefault = ig.DefineLabel ();
3254 Type typeKeys = null;
3255 if (rgKeys.Length > 0)
3256 typeKeys = rgKeys [0].GetType (); // used for conversions
3260 if (TypeManager.IsEnumType (SwitchType))
3261 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3263 compare_type = SwitchType;
3265 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3267 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3268 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3271 foreach (object key in kb.rgKeys)
3273 ig.Emit (OpCodes.Ldloc, val);
3274 EmitObjectInteger (ig, key);
3275 SwitchLabel sl = (SwitchLabel) Elements [key];
3276 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3281 // TODO: if all the keys in the block are the same and there are
3282 // no gaps/defaults then just use a range-check.
3283 if (compare_type == TypeManager.int64_type ||
3284 compare_type == TypeManager.uint64_type)
3286 // TODO: optimize constant/I4 cases
3288 // check block range (could be > 2^31)
3289 ig.Emit (OpCodes.Ldloc, val);
3290 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3291 ig.Emit (OpCodes.Blt, lblDefault);
3292 ig.Emit (OpCodes.Ldloc, val);
3293 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
3294 ig.Emit (OpCodes.Bgt, lblDefault);
3297 ig.Emit (OpCodes.Ldloc, val);
3300 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3301 ig.Emit (OpCodes.Sub);
3303 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3308 ig.Emit (OpCodes.Ldloc, val);
3309 int nFirst = (int) kb.nFirst;
3312 IntConstant.EmitInt (ig, nFirst);
3313 ig.Emit (OpCodes.Sub);
3315 else if (nFirst < 0)
3317 IntConstant.EmitInt (ig, -nFirst);
3318 ig.Emit (OpCodes.Add);
3322 // first, build the list of labels for the switch
3324 int cJumps = kb.Length;
3325 Label [] rgLabels = new Label [cJumps];
3326 for (int iJump = 0; iJump < cJumps; iJump++)
3328 object key = kb.rgKeys [iKey];
3329 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
3331 SwitchLabel sl = (SwitchLabel) Elements [key];
3332 rgLabels [iJump] = sl.GetILLabel (ec);
3336 rgLabels [iJump] = lblDefault;
3338 // emit the switch opcode
3339 ig.Emit (OpCodes.Switch, rgLabels);
3342 // mark the default for this block
3344 ig.MarkLabel (lblDefault);
3347 // TODO: find the default case and emit it here,
3348 // to prevent having to do the following jump.
3349 // make sure to mark other labels in the default section
3351 // the last default just goes to the end
3352 ig.Emit (OpCodes.Br, lblDefault);
3354 // now emit the code for the sections
3355 bool fFoundDefault = false;
3356 bool fFoundNull = false;
3357 foreach (SwitchSection ss in Sections)
3359 foreach (SwitchLabel sl in ss.Labels)
3360 if (sl.Converted == SwitchLabel.NullStringCase)
3364 foreach (SwitchSection ss in Sections)
3366 foreach (SwitchLabel sl in ss.Labels)
3368 ig.MarkLabel (sl.GetILLabel (ec));
3369 ig.MarkLabel (sl.GetILLabelCode (ec));
3370 if (sl.Converted == SwitchLabel.NullStringCase)
3371 ig.MarkLabel (null_target);
3372 else if (sl.Label == null) {
3373 ig.MarkLabel (lblDefault);
3374 fFoundDefault = true;
3376 ig.MarkLabel (null_target);
3382 if (!fFoundDefault) {
3383 ig.MarkLabel (lblDefault);
3384 if (HaveUnwrap && !fFoundNull) {
3385 ig.MarkLabel (null_target);
3389 ig.MarkLabel (lblEnd);
3392 // This simple emit switch works, but does not take advantage of the
3394 // TODO: remove non-string logic from here
3395 // TODO: binary search strings?
3397 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3399 ILGenerator ig = ec.ig;
3400 Label end_of_switch = ig.DefineLabel ();
3401 Label next_test = ig.DefineLabel ();
3402 bool first_test = true;
3403 bool pending_goto_end = false;
3404 bool null_marked = false;
3406 int section_count = Sections.Count;
3408 // TODO: implement switch optimization for string by using Hashtable
3409 //if (SwitchType == TypeManager.string_type && section_count > 7)
3410 // Console.WriteLine ("Switch optimization possible " + loc);
3412 ig.Emit (OpCodes.Ldloc, val);
3414 if (Elements.Contains (SwitchLabel.NullStringCase)){
3415 ig.Emit (OpCodes.Brfalse, null_target);
3417 ig.Emit (OpCodes.Brfalse, default_target);
3419 ig.Emit (OpCodes.Ldloc, val);
3420 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3421 ig.Emit (OpCodes.Stloc, val);
3423 for (int section = 0; section < section_count; section++){
3424 SwitchSection ss = (SwitchSection) Sections [section];
3426 if (ss == default_section)
3429 Label sec_begin = ig.DefineLabel ();
3431 ig.Emit (OpCodes.Nop);
3433 if (pending_goto_end)
3434 ig.Emit (OpCodes.Br, end_of_switch);
3436 int label_count = ss.Labels.Count;
3438 for (int label = 0; label < label_count; label++){
3439 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3440 ig.MarkLabel (sl.GetILLabel (ec));
3443 ig.MarkLabel (next_test);
3444 next_test = ig.DefineLabel ();
3447 // If we are the default target
3449 if (sl.Label != null){
3450 object lit = sl.Converted;
3452 if (lit == SwitchLabel.NullStringCase){
3454 if (label + 1 == label_count)
3455 ig.Emit (OpCodes.Br, next_test);
3459 ig.Emit (OpCodes.Ldloc, val);
3460 ig.Emit (OpCodes.Ldstr, (string)lit);
3461 if (label_count == 1)
3462 ig.Emit (OpCodes.Bne_Un, next_test);
3464 if (label+1 == label_count)
3465 ig.Emit (OpCodes.Bne_Un, next_test);
3467 ig.Emit (OpCodes.Beq, sec_begin);
3472 ig.MarkLabel (null_target);
3475 ig.MarkLabel (sec_begin);
3476 foreach (SwitchLabel sl in ss.Labels)
3477 ig.MarkLabel (sl.GetILLabelCode (ec));
3480 pending_goto_end = !ss.Block.HasRet;
3483 ig.MarkLabel (next_test);
3484 ig.MarkLabel (default_target);
3486 ig.MarkLabel (null_target);
3487 if (default_section != null)
3488 default_section.Block.Emit (ec);
3489 ig.MarkLabel (end_of_switch);
3492 SwitchSection FindSection (SwitchLabel label)
3494 foreach (SwitchSection ss in Sections){
3495 foreach (SwitchLabel sl in ss.Labels){
3504 public override bool Resolve (EmitContext ec)
3506 Expr = Expr.Resolve (ec);
3510 new_expr = SwitchGoverningType (ec, Expr);
3513 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3514 unwrap = Nullable.Unwrap.Create (Expr, ec);
3518 new_expr = SwitchGoverningType (ec, unwrap);
3522 if (new_expr == null){
3523 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3528 SwitchType = new_expr.Type;
3530 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3531 Report.FeatureIsNotISO1 (loc, "switch expression of boolean type");
3535 if (!CheckSwitch (ec))
3539 Elements.Remove (SwitchLabel.NullStringCase);
3541 Switch old_switch = ec.Switch;
3543 ec.Switch.SwitchType = SwitchType;
3545 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3546 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3548 is_constant = new_expr is Constant;
3550 object key = ((Constant) new_expr).GetValue ();
3551 SwitchLabel label = (SwitchLabel) Elements [key];
3553 constant_section = FindSection (label);
3554 if (constant_section == null)
3555 constant_section = default_section;
3559 foreach (SwitchSection ss in Sections){
3561 ec.CurrentBranching.CreateSibling (
3562 null, FlowBranching.SiblingType.SwitchSection);
3566 if (is_constant && (ss != constant_section)) {
3567 // If we're a constant switch, we're only emitting
3568 // one single section - mark all the others as
3570 ec.CurrentBranching.CurrentUsageVector.Goto ();
3571 if (!ss.Block.ResolveUnreachable (ec, true))
3574 if (!ss.Block.Resolve (ec))
3579 if (default_section == null)
3580 ec.CurrentBranching.CreateSibling (
3581 null, FlowBranching.SiblingType.SwitchSection);
3583 ec.EndFlowBranching ();
3584 ec.Switch = old_switch;
3586 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3591 protected override void DoEmit (EmitContext ec)
3593 ILGenerator ig = ec.ig;
3595 default_target = ig.DefineLabel ();
3596 null_target = ig.DefineLabel ();
3598 // Store variable for comparission purposes
3601 value = ig.DeclareLocal (SwitchType);
3603 unwrap.EmitCheck (ec);
3604 ig.Emit (OpCodes.Brfalse, null_target);
3606 ig.Emit (OpCodes.Stloc, value);
3608 } else if (!is_constant) {
3609 value = ig.DeclareLocal (SwitchType);
3611 ig.Emit (OpCodes.Stloc, value);
3616 // Setup the codegen context
3618 Label old_end = ec.LoopEnd;
3619 Switch old_switch = ec.Switch;
3621 ec.LoopEnd = ig.DefineLabel ();
3626 if (constant_section != null)
3627 constant_section.Block.Emit (ec);
3628 } else if (SwitchType == TypeManager.string_type)
3629 SimpleSwitchEmit (ec, value);
3631 TableSwitchEmit (ec, value);
3633 // Restore context state.
3634 ig.MarkLabel (ec.LoopEnd);
3637 // Restore the previous context
3639 ec.LoopEnd = old_end;
3640 ec.Switch = old_switch;
3643 protected override void CloneTo (CloneContext clonectx, Statement t)
3645 Switch target = (Switch) t;
3647 target.Expr = Expr.Clone (clonectx);
3648 target.Sections = new ArrayList ();
3649 foreach (SwitchSection ss in Sections){
3650 target.Sections.Add (ss.Clone (clonectx));
3655 public abstract class ExceptionStatement : Statement
3657 public abstract void EmitFinally (EmitContext ec);
3659 protected bool emit_finally = true;
3660 ArrayList parent_vectors;
3662 protected void DoEmitFinally (EmitContext ec)
3665 ec.ig.BeginFinallyBlock ();
3666 else if (ec.InIterator)
3667 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3671 protected void ResolveFinally (FlowBranchingException branching)
3673 emit_finally = branching.EmitFinally;
3675 branching.Parent.StealFinallyClauses (ref parent_vectors);
3679 public class Lock : ExceptionStatement {
3681 public Statement Statement;
3682 TemporaryVariable temp;
3684 public Lock (Expression expr, Statement stmt, Location l)
3691 public override bool Resolve (EmitContext ec)
3693 expr = expr.Resolve (ec);
3697 if (expr.Type.IsValueType){
3698 Report.Error (185, loc,
3699 "`{0}' is not a reference type as required by the lock statement",
3700 TypeManager.CSharpName (expr.Type));
3704 FlowBranchingException branching = ec.StartFlowBranching (this);
3705 bool ok = Statement.Resolve (ec);
3707 ResolveFinally (branching);
3709 ec.EndFlowBranching ();
3711 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3712 // So, ensure there's some IL code after the finally block.
3713 ec.NeedReturnLabel ();
3715 // Avoid creating libraries that reference the internal
3718 if (t == TypeManager.null_type)
3719 t = TypeManager.object_type;
3721 temp = new TemporaryVariable (t, loc);
3727 protected override void DoEmit (EmitContext ec)
3729 ILGenerator ig = ec.ig;
3731 temp.Store (ec, expr);
3733 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3737 ig.BeginExceptionBlock ();
3738 Statement.Emit (ec);
3743 ig.EndExceptionBlock ();
3746 public override void EmitFinally (EmitContext ec)
3749 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3752 protected override void CloneTo (CloneContext clonectx, Statement t)
3754 Lock target = (Lock) t;
3756 target.expr = expr.Clone (clonectx);
3757 target.Statement = Statement.Clone (clonectx);
3761 public class Unchecked : Statement {
3764 public Unchecked (Block b)
3770 public override bool Resolve (EmitContext ec)
3772 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3773 return Block.Resolve (ec);
3776 protected override void DoEmit (EmitContext ec)
3778 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3782 protected override void CloneTo (CloneContext clonectx, Statement t)
3784 Unchecked target = (Unchecked) t;
3786 target.Block = clonectx.LookupBlock (Block);
3790 public class Checked : Statement {
3793 public Checked (Block b)
3796 b.Unchecked = false;
3799 public override bool Resolve (EmitContext ec)
3801 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3802 return Block.Resolve (ec);
3805 protected override void DoEmit (EmitContext ec)
3807 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3811 protected override void CloneTo (CloneContext clonectx, Statement t)
3813 Checked target = (Checked) t;
3815 target.Block = clonectx.LookupBlock (Block);
3819 public class Unsafe : Statement {
3822 public Unsafe (Block b)
3825 Block.Unsafe = true;
3828 public override bool Resolve (EmitContext ec)
3830 using (ec.With (EmitContext.Flags.InUnsafe, true))
3831 return Block.Resolve (ec);
3834 protected override void DoEmit (EmitContext ec)
3836 using (ec.With (EmitContext.Flags.InUnsafe, true))
3839 protected override void CloneTo (CloneContext clonectx, Statement t)
3841 Unsafe target = (Unsafe) t;
3843 target.Block = clonectx.LookupBlock (Block);
3850 public class Fixed : Statement {
3852 ArrayList declarators;
3853 Statement statement;
3858 abstract class Emitter
3860 protected LocalInfo vi;
3861 protected Expression converted;
3863 protected Emitter (Expression expr, LocalInfo li)
3869 public abstract void Emit (EmitContext ec);
3870 public abstract void EmitExit (EmitContext ec);
3873 class ExpressionEmitter : Emitter {
3874 public ExpressionEmitter (Expression converted, LocalInfo li) :
3875 base (converted, li)
3879 public override void Emit (EmitContext ec) {
3881 // Store pointer in pinned location
3883 converted.Emit (ec);
3884 vi.Variable.EmitAssign (ec);
3887 public override void EmitExit (EmitContext ec)
3889 ec.ig.Emit (OpCodes.Ldc_I4_0);
3890 ec.ig.Emit (OpCodes.Conv_U);
3891 vi.Variable.EmitAssign (ec);
3895 class StringEmitter : Emitter {
3896 LocalBuilder pinned_string;
3899 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3905 public override void Emit (EmitContext ec)
3907 ILGenerator ig = ec.ig;
3908 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3910 converted.Emit (ec);
3911 ig.Emit (OpCodes.Stloc, pinned_string);
3913 Expression sptr = new StringPtr (pinned_string, loc);
3914 converted = Convert.ImplicitConversionRequired (
3915 ec, sptr, vi.VariableType, loc);
3917 if (converted == null)
3920 converted.Emit (ec);
3921 vi.Variable.EmitAssign (ec);
3924 public override void EmitExit (EmitContext ec)
3926 ec.ig.Emit (OpCodes.Ldnull);
3927 ec.ig.Emit (OpCodes.Stloc, pinned_string);
3931 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3934 declarators = decls;
3939 public Statement Statement {
3940 get { return statement; }
3943 public override bool Resolve (EmitContext ec)
3946 Expression.UnsafeError (loc);
3950 TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
3954 expr_type = texpr.Type;
3956 data = new Emitter [declarators.Count];
3958 if (!expr_type.IsPointer){
3959 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
3964 foreach (Pair p in declarators){
3965 LocalInfo vi = (LocalInfo) p.First;
3966 Expression e = (Expression) p.Second;
3968 vi.VariableInfo.SetAssigned (ec);
3969 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
3972 // The rules for the possible declarators are pretty wise,
3973 // but the production on the grammar is more concise.
3975 // So we have to enforce these rules here.
3977 // We do not resolve before doing the case 1 test,
3978 // because the grammar is explicit in that the token &
3979 // is present, so we need to test for this particular case.
3983 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
3988 // Case 1: & object.
3990 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3991 Expression child = ((Unary) e).Expr;
3993 if (child is ParameterReference || child is LocalVariableReference){
3996 "No need to use fixed statement for parameters or " +
3997 "local variable declarations (address is already " +
4002 ec.InFixedInitializer = true;
4004 ec.InFixedInitializer = false;
4008 child = ((Unary) e).Expr;
4010 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4013 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4014 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4018 data [i] = new ExpressionEmitter (e, vi);
4024 ec.InFixedInitializer = true;
4026 ec.InFixedInitializer = false;
4033 if (e.Type.IsArray){
4034 Type array_type = TypeManager.GetElementType (e.Type);
4037 // Provided that array_type is unmanaged,
4039 if (!TypeManager.VerifyUnManaged (array_type, loc))
4043 // and T* is implicitly convertible to the
4044 // pointer type given in the fixed statement.
4046 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4048 Expression converted = Convert.ImplicitConversionRequired (
4049 ec, array_ptr, vi.VariableType, loc);
4050 if (converted == null)
4053 data [i] = new ExpressionEmitter (converted, vi);
4062 if (e.Type == TypeManager.string_type){
4063 data [i] = new StringEmitter (e, vi, loc);
4068 // Case 4: fixed buffer
4069 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4070 if (fixed_buffer_ptr != null) {
4071 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4076 // For other cases, flag a `this is already fixed expression'
4078 if (e is LocalVariableReference || e is ParameterReference ||
4079 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4081 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4085 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4089 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4090 bool ok = statement.Resolve (ec);
4091 bool flow_unreachable = ec.EndFlowBranching ();
4092 has_ret = flow_unreachable;
4097 protected override void DoEmit (EmitContext ec)
4099 for (int i = 0; i < data.Length; i++) {
4103 statement.Emit (ec);
4109 // Clear the pinned variable
4111 for (int i = 0; i < data.Length; i++) {
4112 data [i].EmitExit (ec);
4116 protected override void CloneTo (CloneContext clonectx, Statement t)
4118 Fixed target = (Fixed) t;
4120 target.type = type.Clone (clonectx);
4121 target.declarators = new ArrayList ();
4122 foreach (LocalInfo var in declarators)
4123 target.declarators.Add (clonectx.LookupVariable (var));
4124 target.statement = statement.Clone (clonectx);
4128 public class Catch : Statement {
4129 public readonly string Name;
4131 public Block VarBlock;
4133 Expression type_expr;
4136 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4141 VarBlock = var_block;
4145 public Type CatchType {
4151 public bool IsGeneral {
4153 return type_expr == null;
4157 protected override void DoEmit(EmitContext ec)
4159 ILGenerator ig = ec.ig;
4161 if (CatchType != null)
4162 ig.BeginCatchBlock (CatchType);
4164 ig.BeginCatchBlock (TypeManager.object_type);
4166 if (VarBlock != null)
4170 LocalInfo vi = Block.GetLocalInfo (Name);
4172 throw new Exception ("Variable does not exist in this block");
4174 if (vi.Variable.NeedsTemporary) {
4175 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4176 ig.Emit (OpCodes.Stloc, e);
4178 vi.Variable.EmitInstance (ec);
4179 ig.Emit (OpCodes.Ldloc, e);
4180 vi.Variable.EmitAssign (ec);
4182 vi.Variable.EmitAssign (ec);
4184 ig.Emit (OpCodes.Pop);
4189 public override bool Resolve (EmitContext ec)
4191 using (ec.With (EmitContext.Flags.InCatch, true)) {
4192 if (type_expr != null) {
4193 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4199 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4200 Error (155, "The type caught or thrown must be derived from System.Exception");
4206 if (!Block.Resolve (ec))
4209 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4210 // emit the "unused variable" warnings.
4211 if (VarBlock != null)
4212 return VarBlock.Resolve (ec);
4218 protected override void CloneTo (CloneContext clonectx, Statement t)
4220 Catch target = (Catch) t;
4222 if (type_expr != null)
4223 target.type_expr = type_expr.Clone (clonectx);
4224 if (VarBlock != null)
4225 target.VarBlock = clonectx.LookupBlock (VarBlock);
4226 target.Block = clonectx.LookupBlock (Block);
4230 public class Try : ExceptionStatement {
4231 public Block Fini, Block;
4232 public ArrayList Specific;
4233 public Catch General;
4235 bool need_exc_block;
4238 // specific, general and fini might all be null.
4240 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4242 if (specific == null && general == null){
4243 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4247 this.Specific = specific;
4248 this.General = general;
4253 public override bool Resolve (EmitContext ec)
4257 FlowBranchingException branching = ec.StartFlowBranching (this);
4259 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4261 if (!Block.Resolve (ec))
4264 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4266 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4268 Type[] prevCatches = new Type [Specific.Count];
4270 foreach (Catch c in Specific){
4271 ec.CurrentBranching.CreateSibling (
4272 c.Block, FlowBranching.SiblingType.Catch);
4274 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4276 if (c.Name != null) {
4277 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4279 throw new Exception ();
4281 vi.VariableInfo = null;
4284 if (!c.Resolve (ec))
4287 Type resolvedType = c.CatchType;
4288 for (int ii = 0; ii < last_index; ++ii) {
4289 if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
4290 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches [ii].FullName);
4295 prevCatches [last_index++] = resolvedType;
4296 need_exc_block = true;
4299 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4301 if (General != null){
4302 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4303 foreach (Catch c in Specific){
4304 if (c.CatchType == TypeManager.exception_type) {
4305 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'");
4310 ec.CurrentBranching.CreateSibling (
4311 General.Block, FlowBranching.SiblingType.Catch);
4313 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4315 if (!General.Resolve (ec))
4318 need_exc_block = true;
4321 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4325 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4327 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4328 using (ec.With (EmitContext.Flags.InFinally, true)) {
4329 if (!Fini.Resolve (ec))
4334 need_exc_block = true;
4337 if (ec.InIterator) {
4338 ResolveFinally (branching);
4339 need_exc_block |= emit_finally;
4341 emit_finally = Fini != null;
4343 ec.EndFlowBranching ();
4345 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4346 // So, ensure there's some IL code after the finally block.
4347 ec.NeedReturnLabel ();
4349 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4351 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4356 protected override void DoEmit (EmitContext ec)
4358 ILGenerator ig = ec.ig;
4361 ig.BeginExceptionBlock ();
4364 foreach (Catch c in Specific)
4367 if (General != null)
4372 ig.EndExceptionBlock ();
4375 public override void EmitFinally (EmitContext ec)
4381 public bool HasCatch
4384 return General != null || Specific.Count > 0;
4388 protected override void CloneTo (CloneContext clonectx, Statement t)
4390 Try target = (Try) t;
4392 target.Block = clonectx.LookupBlock (Block);
4394 target.Fini = clonectx.LookupBlock (Fini);
4395 if (General != null)
4396 target.General = (Catch) General.Clone (clonectx);
4397 if (Specific != null){
4398 target.Specific = new ArrayList ();
4399 foreach (Catch c in Specific)
4400 target.Specific.Add (c.Clone (clonectx));
4405 public class Using : ExceptionStatement {
4406 object expression_or_block;
4407 public Statement Statement;
4411 Expression [] resolved_vars;
4412 Expression [] converted_vars;
4413 Expression [] assign;
4414 TemporaryVariable local_copy;
4416 public Using (object expression_or_block, Statement stmt, Location l)
4418 this.expression_or_block = expression_or_block;
4424 // Resolves for the case of using using a local variable declaration.
4426 bool ResolveLocalVariableDecls (EmitContext ec)
4428 resolved_vars = new Expression[var_list.Count];
4429 assign = new Expression [var_list.Count];
4430 converted_vars = new Expression[var_list.Count];
4432 for (int i = 0; i < assign.Length; ++i) {
4433 DictionaryEntry e = (DictionaryEntry) var_list [i];
4434 Expression var = (Expression) e.Key;
4435 Expression new_expr = (Expression) e.Value;
4437 Expression a = new Assign (var, new_expr, loc);
4442 resolved_vars [i] = var;
4445 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4446 converted_vars [i] = var;
4450 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4452 Error_IsNotConvertibleToIDisposable (var);
4456 converted_vars [i] = a;
4462 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4464 Report.SymbolRelatedToPreviousError (expr.Type);
4465 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4466 expr.GetSignatureForError ());
4469 bool ResolveExpression (EmitContext ec)
4471 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4472 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4473 Error_IsNotConvertibleToIDisposable (expr);
4478 local_copy = new TemporaryVariable (expr_type, loc);
4479 local_copy.Resolve (ec);
4485 // Emits the code for the case of using using a local variable declaration.
4487 void EmitLocalVariableDecls (EmitContext ec)
4489 ILGenerator ig = ec.ig;
4492 for (i = 0; i < assign.Length; i++) {
4493 ExpressionStatement es = assign [i] as ExpressionStatement;
4496 es.EmitStatement (ec);
4498 assign [i].Emit (ec);
4499 ig.Emit (OpCodes.Pop);
4503 ig.BeginExceptionBlock ();
4505 Statement.Emit (ec);
4507 var_list.Reverse ();
4512 void EmitLocalVariableDeclFinally (EmitContext ec)
4514 ILGenerator ig = ec.ig;
4516 int i = assign.Length;
4517 for (int ii = 0; ii < var_list.Count; ++ii){
4518 Expression var = resolved_vars [--i];
4519 Label skip = ig.DefineLabel ();
4522 ig.BeginFinallyBlock ();
4524 if (!var.Type.IsValueType) {
4526 ig.Emit (OpCodes.Brfalse, skip);
4527 converted_vars [i].Emit (ec);
4528 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4530 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4532 if (!(ml is MethodGroupExpr)) {
4534 ig.Emit (OpCodes.Box, var.Type);
4535 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4537 MethodInfo mi = null;
4539 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4540 if (TypeManager.GetParameterData (mk).Count == 0) {
4547 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4551 IMemoryLocation mloc = (IMemoryLocation) var;
4553 mloc.AddressOf (ec, AddressOp.Load);
4554 ig.Emit (OpCodes.Call, mi);
4558 ig.MarkLabel (skip);
4561 ig.EndExceptionBlock ();
4563 ig.BeginFinallyBlock ();
4568 void EmitExpression (EmitContext ec)
4571 // Make a copy of the expression and operate on that.
4573 ILGenerator ig = ec.ig;
4575 local_copy.Store (ec, expr);
4578 ig.BeginExceptionBlock ();
4580 Statement.Emit (ec);
4584 ig.EndExceptionBlock ();
4587 void EmitExpressionFinally (EmitContext ec)
4589 ILGenerator ig = ec.ig;
4590 if (!expr_type.IsValueType) {
4591 Label skip = ig.DefineLabel ();
4592 local_copy.Emit (ec);
4593 ig.Emit (OpCodes.Brfalse, skip);
4594 local_copy.Emit (ec);
4595 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4596 ig.MarkLabel (skip);
4598 Expression ml = Expression.MemberLookup (
4599 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4600 "Dispose", Location.Null);
4602 if (!(ml is MethodGroupExpr)) {
4603 local_copy.Emit (ec);
4604 ig.Emit (OpCodes.Box, expr_type);
4605 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4607 MethodInfo mi = null;
4609 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4610 if (TypeManager.GetParameterData (mk).Count == 0) {
4617 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4621 local_copy.AddressOf (ec, AddressOp.Load);
4622 ig.Emit (OpCodes.Call, mi);
4627 public override bool Resolve (EmitContext ec)
4629 if (expression_or_block is DictionaryEntry){
4630 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4631 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4633 if (!ResolveLocalVariableDecls (ec))
4636 } else if (expression_or_block is Expression){
4637 expr = (Expression) expression_or_block;
4639 expr = expr.Resolve (ec);
4643 expr_type = expr.Type;
4645 if (!ResolveExpression (ec))
4649 FlowBranchingException branching = ec.StartFlowBranching (this);
4651 bool ok = Statement.Resolve (ec);
4653 ResolveFinally (branching);
4655 ec.EndFlowBranching ();
4657 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4658 // So, ensure there's some IL code after the finally block.
4659 ec.NeedReturnLabel ();
4664 protected override void DoEmit (EmitContext ec)
4666 if (expression_or_block is DictionaryEntry)
4667 EmitLocalVariableDecls (ec);
4668 else if (expression_or_block is Expression)
4669 EmitExpression (ec);
4672 public override void EmitFinally (EmitContext ec)
4674 if (expression_or_block is DictionaryEntry)
4675 EmitLocalVariableDeclFinally (ec);
4676 else if (expression_or_block is Expression)
4677 EmitExpressionFinally (ec);
4680 protected override void CloneTo (CloneContext clonectx, Statement t)
4682 Using target = (Using) t;
4684 if (expression_or_block is Expression)
4685 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4687 target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
4689 target.Statement = Statement.Clone (clonectx);
4694 /// Implementation of the foreach C# statement
4696 public class Foreach : Statement {
4698 Expression variable;
4700 Statement statement;
4702 CollectionForeach collection;
4704 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4705 Statement stmt, Location l)
4708 this.variable = var;
4714 public Statement Statement {
4715 get { return statement; }
4718 public override bool Resolve (EmitContext ec)
4720 expr = expr.Resolve (ec);
4724 if (expr.Type == TypeManager.null_type) {
4725 Report.Error (186, loc, "Use of null is not valid in this context");
4729 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4730 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4731 expr.ExprClassName);
4736 // We need an instance variable. Not sure this is the best
4737 // way of doing this.
4739 // FIXME: When we implement propertyaccess, will those turn
4740 // out to return values in ExprClass? I think they should.
4742 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4743 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4744 collection.Error_Enumerator ();
4748 if (expr.Type.IsArray) {
4749 array = new ArrayForeach (type, variable, expr, statement, loc);
4750 return array.Resolve (ec);
4753 collection = new CollectionForeach (type, variable, expr, statement, loc);
4754 return collection.Resolve (ec);
4757 protected override void DoEmit (EmitContext ec)
4759 ILGenerator ig = ec.ig;
4761 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4762 ec.LoopBegin = ig.DefineLabel ();
4763 ec.LoopEnd = ig.DefineLabel ();
4765 if (collection != null)
4766 collection.Emit (ec);
4770 ec.LoopBegin = old_begin;
4771 ec.LoopEnd = old_end;
4774 protected class ArrayCounter : TemporaryVariable
4776 public ArrayCounter (Location loc)
4777 : base (TypeManager.int32_type, loc)
4780 public void Initialize (EmitContext ec)
4783 ec.ig.Emit (OpCodes.Ldc_I4_0);
4787 public void Increment (EmitContext ec)
4791 ec.ig.Emit (OpCodes.Ldc_I4_1);
4792 ec.ig.Emit (OpCodes.Add);
4797 protected class ArrayForeach : Statement
4799 Expression variable, expr, conv;
4800 Statement statement;
4802 Expression var_type;
4803 TemporaryVariable[] lengths;
4804 ArrayCounter[] counter;
4807 TemporaryVariable copy;
4810 public ArrayForeach (Expression var_type, Expression var,
4811 Expression expr, Statement stmt, Location l)
4813 this.var_type = var_type;
4814 this.variable = var;
4820 public override bool Resolve (EmitContext ec)
4822 array_type = expr.Type;
4823 rank = array_type.GetArrayRank ();
4825 copy = new TemporaryVariable (array_type, loc);
4828 counter = new ArrayCounter [rank];
4829 lengths = new TemporaryVariable [rank];
4831 ArrayList list = new ArrayList ();
4832 for (int i = 0; i < rank; i++) {
4833 counter [i] = new ArrayCounter (loc);
4834 counter [i].Resolve (ec);
4836 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4837 lengths [i].Resolve (ec);
4839 list.Add (counter [i]);
4842 access = new ElementAccess (copy, list).Resolve (ec);
4846 VarExpr ve = var_type as VarExpr;
4848 // Infer implicitly typed local variable from foreach array type
4849 var_type = new TypeExpression (access.Type, ve.Location);
4852 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4853 if (var_type == null)
4856 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4862 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4863 ec.CurrentBranching.CreateSibling ();
4865 variable = variable.ResolveLValue (ec, conv, loc);
4866 if (variable == null)
4869 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4870 if (!statement.Resolve (ec))
4872 ec.EndFlowBranching ();
4874 // There's no direct control flow from the end of the embedded statement to the end of the loop
4875 ec.CurrentBranching.CurrentUsageVector.Goto ();
4877 ec.EndFlowBranching ();
4882 protected override void DoEmit (EmitContext ec)
4884 ILGenerator ig = ec.ig;
4886 copy.Store (ec, expr);
4888 Label[] test = new Label [rank];
4889 Label[] loop = new Label [rank];
4891 for (int i = 0; i < rank; i++) {
4892 test [i] = ig.DefineLabel ();
4893 loop [i] = ig.DefineLabel ();
4895 lengths [i].EmitThis (ec);
4896 ((ArrayAccess) access).EmitGetLength (ec, i);
4897 lengths [i].EmitStore (ec);
4900 for (int i = 0; i < rank; i++) {
4901 counter [i].Initialize (ec);
4903 ig.Emit (OpCodes.Br, test [i]);
4904 ig.MarkLabel (loop [i]);
4907 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4909 statement.Emit (ec);
4911 ig.MarkLabel (ec.LoopBegin);
4913 for (int i = rank - 1; i >= 0; i--){
4914 counter [i].Increment (ec);
4916 ig.MarkLabel (test [i]);
4917 counter [i].Emit (ec);
4918 lengths [i].Emit (ec);
4919 ig.Emit (OpCodes.Blt, loop [i]);
4922 ig.MarkLabel (ec.LoopEnd);
4926 protected class CollectionForeach : ExceptionStatement
4928 Expression variable, expr;
4929 Statement statement;
4931 TemporaryVariable enumerator;
4935 MethodGroupExpr get_enumerator;
4936 PropertyExpr get_current;
4937 MethodInfo move_next;
4938 Expression var_type;
4939 Type enumerator_type;
4941 bool enumerator_found;
4943 public CollectionForeach (Expression var_type, Expression var,
4944 Expression expr, Statement stmt, Location l)
4946 this.var_type = var_type;
4947 this.variable = var;
4953 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
4955 Type return_type = mi.ReturnType;
4957 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
4959 // Apply the same optimization as MS: skip the GetEnumerator
4960 // returning an IEnumerator, and use the one returning a
4961 // CharEnumerator instead. This allows us to avoid the
4962 // try-finally block and the boxing.
4967 // Ok, we can access it, now make sure that we can do something
4968 // with this `GetEnumerator'
4971 if (return_type == TypeManager.ienumerator_type ||
4972 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
4973 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
4975 // If it is not an interface, lets try to find the methods ourselves.
4976 // For example, if we have:
4977 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4978 // We can avoid the iface call. This is a runtime perf boost.
4979 // even bigger if we have a ValueType, because we avoid the cost
4982 // We have to make sure that both methods exist for us to take
4983 // this path. If one of the methods does not exist, we will just
4984 // use the interface. Sadly, this complex if statement is the only
4985 // way I could do this without a goto
4990 // Prefer a generic enumerator over a non-generic one.
4992 if (return_type.IsInterface && return_type.IsGenericType) {
4993 enumerator_type = return_type;
4994 if (!FetchGetCurrent (ec, return_type))
4995 get_current = new PropertyExpr (
4996 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
4997 if (!FetchMoveNext (return_type))
4998 move_next = TypeManager.bool_movenext_void;
5003 if (return_type.IsInterface ||
5004 !FetchMoveNext (return_type) ||
5005 !FetchGetCurrent (ec, return_type)) {
5006 enumerator_type = return_type;
5007 move_next = TypeManager.bool_movenext_void;
5008 get_current = new PropertyExpr (
5009 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5014 // Ok, so they dont return an IEnumerable, we will have to
5015 // find if they support the GetEnumerator pattern.
5018 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5019 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",
5020 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5025 enumerator_type = return_type;
5026 is_disposable = !enumerator_type.IsSealed ||
5027 TypeManager.ImplementsInterface (
5028 enumerator_type, TypeManager.idisposable_type);
5034 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5036 bool FetchMoveNext (Type t)
5038 MemberList move_next_list;
5040 move_next_list = TypeContainer.FindMembers (
5041 t, MemberTypes.Method,
5042 BindingFlags.Public | BindingFlags.Instance,
5043 Type.FilterName, "MoveNext");
5044 if (move_next_list.Count == 0)
5047 foreach (MemberInfo m in move_next_list){
5048 MethodInfo mi = (MethodInfo) m;
5050 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5051 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5061 // Retrieves a `public T get_Current ()' method from the Type `t'
5063 bool FetchGetCurrent (EmitContext ec, Type t)
5065 PropertyExpr pe = Expression.MemberLookup (
5066 ec.ContainerType, t, "Current", MemberTypes.Property,
5067 Expression.AllBindingFlags, loc) as PropertyExpr;
5076 // Retrieves a `public void Dispose ()' method from the Type `t'
5078 static MethodInfo FetchMethodDispose (Type t)
5080 MemberList dispose_list;
5082 dispose_list = TypeContainer.FindMembers (
5083 t, MemberTypes.Method,
5084 BindingFlags.Public | BindingFlags.Instance,
5085 Type.FilterName, "Dispose");
5086 if (dispose_list.Count == 0)
5089 foreach (MemberInfo m in dispose_list){
5090 MethodInfo mi = (MethodInfo) m;
5092 if (TypeManager.GetParameterData (mi).Count == 0){
5093 if (mi.ReturnType == TypeManager.void_type)
5100 public void Error_Enumerator ()
5102 if (enumerator_found) {
5106 Report.Error (1579, loc,
5107 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5108 TypeManager.CSharpName (expr.Type));
5111 bool IsOverride (MethodInfo m)
5113 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5115 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5117 if (m is MethodBuilder)
5120 MethodInfo base_method = m.GetBaseDefinition ();
5121 return base_method != m;
5124 bool TryType (EmitContext ec, Type t)
5126 MethodGroupExpr mg = Expression.MemberLookup (
5127 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5128 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5132 MethodInfo result = null;
5133 MethodInfo tmp_move_next = null;
5134 PropertyExpr tmp_get_cur = null;
5135 Type tmp_enumerator_type = enumerator_type;
5136 foreach (MethodInfo mi in mg.Methods) {
5137 if (TypeManager.GetParameterData (mi).Count != 0)
5140 // Check whether GetEnumerator is public
5141 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5144 if (IsOverride (mi))
5147 enumerator_found = true;
5149 if (!GetEnumeratorFilter (ec, mi))
5152 if (result != null) {
5153 if (TypeManager.IsGenericType (result.ReturnType)) {
5154 if (!TypeManager.IsGenericType (mi.ReturnType))
5157 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5158 Report.SymbolRelatedToPreviousError (t);
5159 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5160 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5161 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5165 // Always prefer generics enumerators
5166 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5167 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5168 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5171 Report.SymbolRelatedToPreviousError (result);
5172 Report.SymbolRelatedToPreviousError (mi);
5173 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5174 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5179 tmp_move_next = move_next;
5180 tmp_get_cur = get_current;
5181 tmp_enumerator_type = enumerator_type;
5182 if (mi.DeclaringType == t)
5186 if (result != null) {
5187 move_next = tmp_move_next;
5188 get_current = tmp_get_cur;
5189 enumerator_type = tmp_enumerator_type;
5190 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5191 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5193 if (t != expr.Type) {
5194 expr = Convert.ExplicitConversion (
5197 throw new InternalErrorException ();
5200 get_enumerator.InstanceExpression = expr;
5201 get_enumerator.IsBase = t != expr.Type;
5209 bool ProbeCollectionType (EmitContext ec, Type t)
5211 int errors = Report.Errors;
5212 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5213 if (TryType (ec, tt))
5218 if (Report.Errors > errors)
5222 // Now try to find the method in the interfaces
5224 Type [] ifaces = TypeManager.GetInterfaces (t);
5225 foreach (Type i in ifaces){
5226 if (TryType (ec, i))
5233 public override bool Resolve (EmitContext ec)
5235 enumerator_type = TypeManager.ienumerator_type;
5236 is_disposable = true;
5238 if (!ProbeCollectionType (ec, expr.Type)) {
5239 Error_Enumerator ();
5243 VarExpr ve = var_type as VarExpr;
5245 // Infer implicitly typed local variable from foreach enumerable type
5246 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5249 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5250 if (var_type == null)
5253 enumerator = new TemporaryVariable (enumerator_type, loc);
5254 enumerator.Resolve (ec);
5256 init = new Invocation (get_enumerator, null);
5257 init = init.Resolve (ec);
5261 Expression move_next_expr;
5263 MemberInfo[] mi = new MemberInfo[] { move_next };
5264 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5265 mg.InstanceExpression = enumerator;
5267 move_next_expr = new Invocation (mg, null);
5270 get_current.InstanceExpression = enumerator;
5272 Statement block = new CollectionForeachStatement (
5273 var_type.Type, variable, get_current, statement, loc);
5275 loop = new While (move_next_expr, block, loc);
5279 FlowBranchingException branching = null;
5281 branching = ec.StartFlowBranching (this);
5283 if (!loop.Resolve (ec))
5286 if (is_disposable) {
5287 ResolveFinally (branching);
5288 ec.EndFlowBranching ();
5290 emit_finally = true;
5295 protected override void DoEmit (EmitContext ec)
5297 ILGenerator ig = ec.ig;
5299 enumerator.Store (ec, init);
5302 // Protect the code in a try/finalize block, so that
5303 // if the beast implement IDisposable, we get rid of it
5305 if (is_disposable && emit_finally)
5306 ig.BeginExceptionBlock ();
5311 // Now the finally block
5313 if (is_disposable) {
5316 ig.EndExceptionBlock ();
5321 public override void EmitFinally (EmitContext ec)
5323 ILGenerator ig = ec.ig;
5325 if (enumerator_type.IsValueType) {
5326 MethodInfo mi = FetchMethodDispose (enumerator_type);
5328 enumerator.EmitLoadAddress (ec);
5329 ig.Emit (OpCodes.Call, mi);
5331 enumerator.Emit (ec);
5332 ig.Emit (OpCodes.Box, enumerator_type);
5333 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5336 Label call_dispose = ig.DefineLabel ();
5338 enumerator.Emit (ec);
5339 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5340 ig.Emit (OpCodes.Dup);
5341 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5342 ig.Emit (OpCodes.Pop);
5344 Label end_finally = ig.DefineLabel ();
5345 ig.Emit (OpCodes.Br, end_finally);
5347 ig.MarkLabel (call_dispose);
5348 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5349 ig.MarkLabel (end_finally);
5354 protected class CollectionForeachStatement : Statement
5357 Expression variable, current, conv;
5358 Statement statement;
5361 public CollectionForeachStatement (Type type, Expression variable,
5362 Expression current, Statement statement,
5366 this.variable = variable;
5367 this.current = current;
5368 this.statement = statement;
5372 public override bool Resolve (EmitContext ec)
5374 current = current.Resolve (ec);
5375 if (current == null)
5378 conv = Convert.ExplicitConversion (ec, current, type, loc);
5382 assign = new Assign (variable, conv, loc);
5383 if (assign.Resolve (ec) == null)
5386 if (!statement.Resolve (ec))
5392 protected override void DoEmit (EmitContext ec)
5394 assign.EmitStatement (ec);
5395 statement.Emit (ec);
5399 protected override void CloneTo (CloneContext clonectx, Statement t)
5401 Foreach target = (Foreach) t;
5403 target.type = type.Clone (clonectx);
5404 target.variable = variable.Clone (clonectx);
5405 target.expr = expr.Clone (clonectx);
5406 target.statement = statement.Clone (clonectx);