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;
1488 protected int current_statement;
1492 // An array of Blocks. We keep track of children just
1493 // to generate the local variable declarations.
1495 // Statements and child statements are handled through the
1501 // Labels. (label, block) pairs.
1506 // Keeps track of (name, type) pairs
1508 IDictionary variables;
1511 // Keeps track of constants
1512 Hashtable constants;
1515 // Temporary variables.
1517 ArrayList temporary_variables;
1520 // If this is a switch section, the enclosing switch block.
1524 ExpressionStatement scope_init;
1526 ArrayList anonymous_children;
1528 protected static int id;
1532 int assignable_slots;
1533 protected ScopeInfo scope_info;
1534 bool unreachable_shown;
1537 public Block (Block parent)
1538 : this (parent, (Flags) 0, Location.Null, Location.Null)
1541 public Block (Block parent, Flags flags)
1542 : this (parent, flags, Location.Null, Location.Null)
1545 public Block (Block parent, Location start, Location end)
1546 : this (parent, (Flags) 0, start, end)
1549 public Block (Block parent, Flags flags, Location start, Location end)
1551 if (parent != null) {
1552 parent.AddChild (this);
1554 // the appropriate constructors will fixup these fields
1555 Toplevel = parent.Toplevel;
1556 Explicit = parent.Explicit;
1559 this.Parent = parent;
1561 this.StartLocation = start;
1562 this.EndLocation = end;
1565 statements = new ArrayList ();
1568 public Block CreateSwitchBlock (Location start)
1570 // FIXME: should this be implicit?
1571 Block new_block = new ExplicitBlock (this, start, start);
1572 new_block.switch_block = this;
1577 get { return this_id; }
1580 public IDictionary Variables {
1582 if (variables == null)
1583 variables = new ListDictionary ();
1588 void AddChild (Block b)
1590 if (children == null)
1591 children = new ArrayList ();
1596 public void SetEndLocation (Location loc)
1601 protected static void Error_158 (string name, Location loc)
1603 Report.Error (158, loc, "The label `{0}' shadows another label " +
1604 "by the same name in a contained scope", name);
1608 /// Adds a label to the current block.
1612 /// false if the name already exists in this block. true
1616 public bool AddLabel (LabeledStatement target)
1618 if (switch_block != null)
1619 return switch_block.AddLabel (target);
1621 string name = target.Name;
1624 while (cur != null) {
1625 LabeledStatement s = cur.DoLookupLabel (name);
1627 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1628 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1632 if (this == Explicit)
1638 while (cur != null) {
1639 if (cur.DoLookupLabel (name) != null) {
1640 Error_158 (name, target.loc);
1644 if (children != null) {
1645 foreach (Block b in children) {
1646 LabeledStatement s = b.DoLookupLabel (name);
1650 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1651 Error_158 (name, target.loc);
1659 Toplevel.CheckError158 (name, target.loc);
1662 labels = new Hashtable ();
1664 labels.Add (name, target);
1668 public LabeledStatement LookupLabel (string name)
1670 LabeledStatement s = DoLookupLabel (name);
1674 if (children == null)
1677 foreach (Block child in children) {
1678 if (Explicit != child.Explicit)
1681 s = child.LookupLabel (name);
1689 LabeledStatement DoLookupLabel (string name)
1691 if (switch_block != null)
1692 return switch_block.LookupLabel (name);
1695 if (labels.Contains (name))
1696 return ((LabeledStatement) labels [name]);
1701 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1704 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1705 while (kvi == null) {
1706 b = b.Explicit.Parent;
1709 kvi = b.Explicit.GetKnownVariable (name);
1715 // Is kvi.Block nested inside 'b'
1716 if (b.Explicit != kvi.Block.Explicit) {
1718 // If a variable by the same name it defined in a nested block of this
1719 // block, we violate the invariant meaning in a block.
1722 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1723 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1728 // It's ok if the definition is in a nested subblock of b, but not
1729 // nested inside this block -- a definition in a sibling block
1730 // should not affect us.
1736 // Block 'b' and kvi.Block are the same textual block.
1737 // However, different variables are extant.
1739 // Check if the variable is in scope in both blocks. We use
1740 // an indirect check that depends on AddVariable doing its
1741 // part in maintaining the invariant-meaning-in-block property.
1743 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1747 // Even though we detected the error when the name is used, we
1748 // treat it as if the variable declaration was in error.
1750 Report.SymbolRelatedToPreviousError (loc, name);
1751 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1755 public LocalInfo AddVariable (Expression type, string name, Location l)
1757 LocalInfo vi = GetLocalInfo (name);
1759 Report.SymbolRelatedToPreviousError (vi.Location, name);
1760 if (Explicit == vi.Block.Explicit)
1761 Report.Error (128, l,
1762 "A local variable named `{0}' is already defined in this scope", name);
1764 Error_AlreadyDeclared (l, name, "parent");
1768 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1770 Report.SymbolRelatedToPreviousError (pi.Location, name);
1771 Error_AlreadyDeclared (loc, name,
1772 pi.Block == Toplevel ? "method argument" : "parent or current");
1775 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1777 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1778 Error_AlreadyDeclared (l, name, "child");
1782 vi = new LocalInfo (type, name, this, l);
1783 Variables.Add (name, vi);
1784 Explicit.AddKnownVariable (name, vi);
1786 if ((flags & Flags.VariablesInitialized) != 0)
1787 throw new InternalErrorException ("block has already been resolved");
1792 protected static void Error_AlreadyDeclared (Location loc, string var, string reason)
1794 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1795 "in this scope because it would give a different meaning " +
1796 "to `{0}', which is already used in a `{1}' scope " +
1797 "to denote something else", var, reason);
1800 public bool AddConstant (Expression type, string name, Expression value, Location l)
1802 if (AddVariable (type, name, l) == null)
1805 if (constants == null)
1806 constants = new Hashtable ();
1808 constants.Add (name, value);
1810 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1815 static int next_temp_id = 0;
1817 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1819 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1821 if (temporary_variables == null)
1822 temporary_variables = new ArrayList ();
1824 int id = ++next_temp_id;
1825 string name = "$s_" + id.ToString ();
1827 LocalInfo li = new LocalInfo (te, name, this, loc);
1828 li.CompilerGenerated = true;
1829 temporary_variables.Add (li);
1833 public LocalInfo GetLocalInfo (string name)
1835 for (Block b = this; b != null; b = b.Parent) {
1836 if (b.variables != null) {
1837 LocalInfo ret = b.variables [name] as LocalInfo;
1845 public Expression GetVariableType (string name)
1847 LocalInfo vi = GetLocalInfo (name);
1848 return vi == null ? null : vi.Type;
1851 public Expression GetConstantExpression (string name)
1853 for (Block b = this; b != null; b = b.Parent) {
1854 if (b.constants != null) {
1855 Expression ret = b.constants [name] as Expression;
1863 public void AddStatement (Statement s)
1866 flags |= Flags.BlockUsed;
1869 public void InsertStatementAfterCurrent (Statement statement)
1871 statements.Insert (current_statement + 1, statement);
1872 flags |= Flags.BlockUsed;
1876 get { return (flags & Flags.BlockUsed) != 0; }
1881 flags |= Flags.BlockUsed;
1884 public bool HasRet {
1885 get { return (flags & Flags.HasRet) != 0; }
1888 public bool IsDestructor {
1889 get { return (flags & Flags.IsDestructor) != 0; }
1892 public void SetDestructor ()
1894 flags |= Flags.IsDestructor;
1897 public int AssignableSlots {
1899 if ((flags & Flags.VariablesInitialized) == 0)
1900 throw new Exception ("Variables have not been initialized yet");
1901 return assignable_slots;
1905 public ScopeInfo ScopeInfo {
1906 get { return scope_info; }
1909 public ScopeInfo CreateScopeInfo ()
1911 if (scope_info == null)
1912 scope_info = ScopeInfo.CreateScope (this);
1917 public ArrayList AnonymousChildren {
1918 get { return anonymous_children; }
1921 public void AddAnonymousChild (ToplevelBlock b)
1923 if (anonymous_children == null)
1924 anonymous_children = new ArrayList ();
1926 anonymous_children.Add (b);
1929 void DoResolveConstants (EmitContext ec)
1931 if (constants == null)
1934 if (variables == null)
1935 throw new InternalErrorException ("cannot happen");
1937 foreach (DictionaryEntry de in variables) {
1938 string name = (string) de.Key;
1939 LocalInfo vi = (LocalInfo) de.Value;
1940 Type variable_type = vi.VariableType;
1942 if (variable_type == null)
1945 Expression cv = (Expression) constants [name];
1949 // Don't let 'const int Foo = Foo;' succeed.
1950 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1951 // which in turn causes the 'must be constant' error to be triggered.
1952 constants.Remove (name);
1954 if (!Const.IsConstantTypeValid (variable_type)) {
1955 Const.Error_InvalidConstantType (variable_type, loc);
1959 ec.CurrentBlock = this;
1961 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1962 e = cv.Resolve (ec);
1967 Constant ce = e as Constant;
1969 Const.Error_ExpressionMustBeConstant (vi.Location, name);
1973 e = ce.ConvertImplicitly (variable_type);
1975 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
1976 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
1978 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
1982 constants.Add (name, e);
1983 vi.IsConstant = true;
1987 protected void ResolveMeta (EmitContext ec, int offset)
1989 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1991 // If some parent block was unsafe, we remain unsafe even if this block
1992 // isn't explicitly marked as such.
1993 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
1994 flags |= Flags.VariablesInitialized;
1996 if (variables != null) {
1997 foreach (LocalInfo li in variables.Values) {
1998 if (!li.Resolve (ec))
2000 li.VariableInfo = new VariableInfo (li, offset);
2001 offset += li.VariableInfo.Length;
2004 assignable_slots = offset;
2006 DoResolveConstants (ec);
2008 if (children == null)
2010 foreach (Block b in children)
2011 b.ResolveMeta (ec, offset);
2016 // Emits the local variable declarations for a block
2018 public virtual void EmitMeta (EmitContext ec)
2020 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2021 if (ScopeInfo != null) {
2022 scope_init = ScopeInfo.GetScopeInitializer (ec);
2023 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2027 if (variables != null){
2028 foreach (LocalInfo vi in variables.Values)
2029 vi.ResolveVariable (ec);
2032 if (temporary_variables != null) {
2033 foreach (LocalInfo vi in temporary_variables)
2034 vi.ResolveVariable (ec);
2037 if (children != null){
2038 foreach (Block b in children)
2043 void UsageWarning (FlowBranching.UsageVector vector)
2047 if ((variables != null) && (Report.WarningLevel >= 3)) {
2048 foreach (DictionaryEntry de in variables){
2049 LocalInfo vi = (LocalInfo) de.Value;
2054 name = (string) de.Key;
2056 // vi.VariableInfo can be null for 'catch' variables
2057 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2058 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2060 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2066 private void CheckPossibleMistakenEmptyStatement (Statement s)
2070 // Some statements are wrapped by a Block. Since
2071 // others' internal could be changed, here I treat
2072 // them as possibly wrapped by Block equally.
2073 Block b = s as Block;
2074 if (b != null && b.statements.Count == 1)
2075 s = (Statement) b.statements [0];
2078 body = ((Lock) s).Statement;
2080 body = ((For) s).Statement;
2081 else if (s is Foreach)
2082 body = ((Foreach) s).Statement;
2083 else if (s is While)
2084 body = ((While) s).Statement;
2085 else if (s is Using)
2086 body = ((Using) s).Statement;
2087 else if (s is Fixed)
2088 body = ((Fixed) s).Statement;
2092 if (body == null || body is EmptyStatement)
2093 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2096 public override bool Resolve (EmitContext ec)
2098 Block prev_block = ec.CurrentBlock;
2101 int errors = Report.Errors;
2103 ec.CurrentBlock = this;
2104 ec.StartFlowBranching (this);
2106 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2109 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2110 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2111 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2112 // responsible for handling the situation.
2114 for (current_statement = 0; current_statement < statements.Count; current_statement++) {
2115 Statement s = (Statement) statements [current_statement];
2116 // Check possible empty statement (CS0642)
2117 if (Report.WarningLevel >= 3 &&
2118 current_statement + 1 < statements.Count &&
2119 statements [current_statement + 1] is Block)
2120 CheckPossibleMistakenEmptyStatement (s);
2123 // Warn if we detect unreachable code.
2126 if (s is EmptyStatement)
2130 ((Block) s).unreachable = true;
2132 if (!unreachable_shown && !(s is LabeledStatement)) {
2133 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2134 unreachable_shown = true;
2139 // Note that we're not using ResolveUnreachable() for unreachable
2140 // statements here. ResolveUnreachable() creates a temporary
2141 // flow branching and kills it afterwards. This leads to problems
2142 // if you have two unreachable statements where the first one
2143 // assigns a variable and the second one tries to access it.
2146 if (!s.Resolve (ec)) {
2147 if (ec.IsInProbingMode)
2151 statements [current_statement] = EmptyStatement.Value;
2155 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2156 statements [current_statement] = EmptyStatement.Value;
2158 num_statements = current_statement + 1;
2160 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2161 if (unreachable && s is LabeledStatement)
2162 throw new InternalErrorException ("should not happen");
2165 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2166 ec.CurrentBranching, statements.Count, num_statements);
2171 while (ec.CurrentBranching is FlowBranchingLabeled)
2172 ec.EndFlowBranching ();
2174 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2176 ec.CurrentBlock = prev_block;
2178 // If we're a non-static `struct' constructor which doesn't have an
2179 // initializer, then we must initialize all of the struct's fields.
2180 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2183 if ((labels != null) && (Report.WarningLevel >= 2)) {
2184 foreach (LabeledStatement label in labels.Values)
2185 if (!label.HasBeenReferenced)
2186 Report.Warning (164, 2, label.loc,
2187 "This label has not been referenced");
2190 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2192 if (vector.IsUnreachable)
2193 flags |= Flags.HasRet;
2195 if (ok && (errors == Report.Errors)) {
2196 UsageWarning (vector);
2202 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2204 unreachable_shown = true;
2208 Report.Warning (162, 2, loc, "Unreachable code detected");
2210 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2211 bool ok = Resolve (ec);
2212 ec.KillFlowBranching ();
2217 protected override void DoEmit (EmitContext ec)
2219 for (int ix = 0; ix < num_statements; ix++){
2220 Statement s = (Statement) statements [ix];
2225 public override void Emit (EmitContext ec)
2227 Block prev_block = ec.CurrentBlock;
2229 ec.CurrentBlock = this;
2231 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2232 bool is_lexical_block = this == Explicit && Parent != null;
2234 if (emit_debug_info) {
2235 if (is_lexical_block)
2238 ec.Mark (StartLocation, true);
2239 if (scope_init != null)
2240 scope_init.EmitStatement (ec);
2242 ec.Mark (EndLocation, true);
2244 if (emit_debug_info) {
2245 if (is_lexical_block)
2248 if (variables != null) {
2249 foreach (DictionaryEntry de in variables) {
2250 string name = (string) de.Key;
2251 LocalInfo vi = (LocalInfo) de.Value;
2253 vi.EmitSymbolInfo (ec, name);
2258 ec.CurrentBlock = prev_block;
2261 public override string ToString ()
2263 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2266 protected override void CloneTo (CloneContext clonectx, Statement t)
2268 Block target = (Block) t;
2270 clonectx.AddBlockMap (this, target);
2272 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2273 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2275 target.Parent = clonectx.RemapBlockCopy (Parent);
2277 if (variables != null){
2278 target.variables = new Hashtable ();
2280 foreach (DictionaryEntry de in variables){
2281 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2282 target.variables [de.Key] = newlocal;
2283 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2287 target.statements = new ArrayList (statements.Count);
2288 foreach (Statement s in statements)
2289 target.statements.Add (s.Clone (clonectx));
2291 if (target.children != null){
2292 target.children = new ArrayList (children.Count);
2293 foreach (Block b in children){
2294 target.children.Add (clonectx.LookupBlock (b));
2299 // TODO: labels, switch_block, constants (?), anonymous_children
2304 public class ExplicitBlock : Block {
2305 public ExplicitBlock (Block parent, Location start, Location end)
2306 : this (parent, (Flags) 0, start, end)
2310 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2311 : base (parent, flags, start, end)
2313 this.Explicit = this;
2316 Hashtable known_variables;
2319 // Marks a variable with name @name as being used in this or a child block.
2320 // If a variable name has been used in a child block, it's illegal to
2321 // declare a variable with the same name in the current block.
2323 internal void AddKnownVariable (string name, IKnownVariable info)
2325 if (known_variables == null)
2326 known_variables = new Hashtable ();
2328 known_variables [name] = info;
2331 Parent.Explicit.AddKnownVariable (name, info);
2334 internal IKnownVariable GetKnownVariable (string name)
2336 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2339 protected override void CloneTo (CloneContext clonectx, Statement t)
2341 ExplicitBlock target = (ExplicitBlock) t;
2342 target.known_variables = null;
2343 base.CloneTo (clonectx, t);
2347 public class ToplevelParameterInfo : IKnownVariable {
2348 public readonly ToplevelBlock Block;
2349 public readonly int Index;
2350 public VariableInfo VariableInfo;
2352 Block IKnownVariable.Block {
2353 get { return Block; }
2355 public Parameter Parameter {
2356 get { return Block.Parameters [Index]; }
2358 public Location Location {
2359 get { return Parameter.Location; }
2362 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2370 // A toplevel block contains extra information, the split is done
2371 // only to separate information that would otherwise bloat the more
2372 // lightweight Block.
2374 // In particular, this was introduced when the support for Anonymous
2375 // Methods was implemented.
2377 public class ToplevelBlock : ExplicitBlock {
2378 GenericMethod generic;
2379 FlowBranchingToplevel top_level_branching;
2380 AnonymousContainer anonymous_container;
2381 RootScopeInfo root_scope;
2382 Parameters parameters;
2383 ToplevelParameterInfo[] parameter_info;
2385 public bool HasVarargs {
2386 get { return (flags & Flags.HasVarargs) != 0; }
2387 set { flags |= Flags.HasVarargs; }
2390 public bool IsIterator {
2391 get { return (flags & Flags.IsIterator) != 0; }
2395 // The parameters for the block.
2397 public Parameters Parameters {
2398 get { return parameters; }
2401 public bool CompleteContexts (EmitContext ec)
2403 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2405 if (root_scope != null)
2406 root_scope.LinkScopes ();
2408 if (Parent == null && root_scope != null) {
2409 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2411 if (root_scope.DefineType () == null)
2413 if (!root_scope.ResolveType ())
2415 if (!root_scope.ResolveMembers ())
2417 if (!root_scope.DefineMembers ())
2424 public GenericMethod GenericMethod {
2425 get { return generic; }
2428 public ToplevelBlock Container {
2429 get { return Parent == null ? null : Parent.Toplevel; }
2432 public AnonymousContainer AnonymousContainer {
2433 get { return anonymous_container; }
2434 set { anonymous_container = value; }
2437 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2438 this (parent, (Flags) 0, parameters, start)
2442 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2443 this (parent, parameters, start)
2445 this.generic = generic;
2448 public ToplevelBlock (Parameters parameters, Location start) :
2449 this (null, (Flags) 0, parameters, start)
2453 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2454 this (null, flags, parameters, start)
2458 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2459 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2460 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2461 base (null, flags, start, Location.Null)
2463 this.Toplevel = this;
2465 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2466 this.Parent = parent;
2468 parent.AddAnonymousChild (this);
2470 if (this.parameters.Count != 0)
2471 ProcessParameters ();
2474 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2478 protected override void CloneTo (CloneContext clonectx, Statement t)
2480 ToplevelBlock target = (ToplevelBlock) t;
2481 base.CloneTo (clonectx, t);
2483 if (parameters.Count != 0)
2484 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2485 for (int i = 0; i < parameters.Count; ++i)
2486 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2489 public bool CheckError158 (string name, Location loc)
2491 if (AnonymousChildren != null) {
2492 foreach (ToplevelBlock child in AnonymousChildren) {
2493 if (!child.CheckError158 (name, loc))
2498 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2499 if (!c.DoCheckError158 (name, loc))
2506 void ProcessParameters ()
2508 int n = parameters.Count;
2509 parameter_info = new ToplevelParameterInfo [n];
2510 for (int i = 0; i < n; ++i) {
2511 parameter_info [i] = new ToplevelParameterInfo (this, i);
2513 Parameter p = parameters [i];
2517 string name = p.Name;
2518 LocalInfo vi = GetLocalInfo (name);
2520 Report.SymbolRelatedToPreviousError (vi.Location, name);
2521 Error_AlreadyDeclared (loc, name, "parent or current");
2525 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2527 Report.SymbolRelatedToPreviousError (pi.Location, name);
2528 Error_AlreadyDeclared (loc, name, "parent or current");
2532 AddKnownVariable (name, parameter_info [i]);
2535 // mark this block as "used" so that we create local declarations in a sub-block
2536 // FIXME: This appears to uncover a lot of bugs
2540 bool DoCheckError158 (string name, Location loc)
2542 LabeledStatement s = LookupLabel (name);
2544 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2545 Error_158 (name, loc);
2552 public RootScopeInfo CreateRootScope (TypeContainer host)
2554 if (root_scope != null)
2557 if (Container == null)
2558 root_scope = new RootScopeInfo (
2559 this, host, generic, StartLocation);
2561 if (scope_info != null)
2562 throw new InternalErrorException ();
2564 scope_info = root_scope;
2568 public void CreateIteratorHost (RootScopeInfo root)
2570 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2572 if (Parent != null || root_scope != null)
2573 throw new InternalErrorException ();
2575 scope_info = root_scope = root;
2578 public RootScopeInfo RootScope {
2580 if (root_scope != null)
2582 else if (Container != null)
2583 return Container.RootScope;
2589 public FlowBranchingToplevel TopLevelBranching {
2590 get { return top_level_branching; }
2594 // This is used if anonymous methods are used inside an iterator
2595 // (see 2test-22.cs for an example).
2597 // The AnonymousMethod is created while parsing - at a time when we don't
2598 // know yet that we're inside an iterator, so it's `Container' is initially
2599 // null. Later on, when resolving the iterator, we need to move the
2600 // anonymous method into that iterator.
2602 public void ReParent (ToplevelBlock new_parent)
2604 if ((flags & Flags.VariablesInitialized) != 0)
2605 throw new InternalErrorException ("block has already been resolved");
2607 Parent = new_parent;
2611 // Returns a `ParameterReference' for the given name, or null if there
2612 // is no such parameter
2614 public ParameterReference GetParameterReference (string name, Location loc)
2616 ToplevelParameterInfo p = GetParameterInfo (name);
2617 return p == null ? null : new ParameterReference (this, p, loc);
2620 public ToplevelParameterInfo GetParameterInfo (string name)
2623 for (ToplevelBlock t = this; t != null; t = t.Container) {
2624 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2626 return t.parameter_info [idx];
2632 // Whether the parameter named `name' is local to this block,
2633 // or false, if the parameter belongs to an encompassing block.
2635 public bool IsLocalParameter (string name)
2637 return Parameters.GetParameterByName (name) != null;
2641 // Whether the `name' is a parameter reference
2643 public bool IsParameterReference (string name)
2645 for (ToplevelBlock t = this; t != null; t = t.Container) {
2646 if (t.IsLocalParameter (name))
2652 LocalInfo this_variable = null;
2655 // Returns the "this" instance variable of this block.
2656 // See AddThisVariable() for more information.
2658 public LocalInfo ThisVariable {
2659 get { return this_variable; }
2664 // This is used by non-static `struct' constructors which do not have an
2665 // initializer - in this case, the constructor must initialize all of the
2666 // struct's fields. To do this, we add a "this" variable and use the flow
2667 // analysis code to ensure that it's been fully initialized before control
2668 // leaves the constructor.
2670 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2672 if (this_variable == null) {
2673 this_variable = new LocalInfo (ds, this, l);
2674 this_variable.Used = true;
2675 this_variable.IsThis = true;
2677 Variables.Add ("this", this_variable);
2680 return this_variable;
2683 public bool IsThisAssigned (EmitContext ec)
2685 return this_variable == null || this_variable.IsThisAssigned (ec);
2688 public bool ResolveMeta (EmitContext ec, Parameters ip)
2690 int errors = Report.Errors;
2691 int orig_count = parameters.Count;
2693 if (top_level_branching != null)
2699 // Assert: orig_count != parameter.Count => orig_count == 0
2700 if (orig_count != 0 && orig_count != parameters.Count)
2701 throw new InternalErrorException ("parameter information mismatch");
2703 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2705 for (int i = 0; i < orig_count; ++i) {
2706 Parameter.Modifier mod = parameters.ParameterModifier (i);
2708 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2711 VariableInfo vi = new VariableInfo (ip, i, offset);
2712 parameter_info [i].VariableInfo = vi;
2713 offset += vi.Length;
2716 ResolveMeta (ec, offset);
2718 top_level_branching = ec.StartFlowBranching (this);
2720 return Report.Errors == errors;
2724 // Check whether all `out' parameters have been assigned.
2726 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2728 if (vector.IsUnreachable)
2731 int n = parameter_info == null ? 0 : parameter_info.Length;
2733 for (int i = 0; i < n; i++) {
2734 VariableInfo var = parameter_info [i].VariableInfo;
2739 if (vector.IsAssigned (var, false))
2742 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2747 public override void EmitMeta (EmitContext ec)
2750 parameters.ResolveVariable (this);
2753 public void MakeIterator (Iterator iterator)
2755 flags |= Flags.IsIterator;
2757 Block block = new ExplicitBlock (this, StartLocation, EndLocation);
2758 foreach (Statement stmt in statements)
2759 block.AddStatement (stmt);
2760 statements.Clear ();
2761 statements.Add (new MoveNextStatement (iterator, block));
2764 protected class MoveNextStatement : Statement {
2768 public MoveNextStatement (Iterator iterator, Block block)
2770 this.iterator = iterator;
2772 this.loc = iterator.Location;
2775 public override bool Resolve (EmitContext ec)
2777 return block.Resolve (ec);
2780 protected override void DoEmit (EmitContext ec)
2782 iterator.EmitMoveNext (ec, block);
2786 public override string ToString ()
2788 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2789 root_scope, anonymous_container != null ?
2790 anonymous_container.Scope : null);
2794 public class SwitchLabel {
2801 Label il_label_code;
2802 bool il_label_code_set;
2804 public static readonly object NullStringCase = new object ();
2807 // if expr == null, then it is the default case.
2809 public SwitchLabel (Expression expr, Location l)
2815 public Expression Label {
2821 public object Converted {
2827 public Label GetILLabel (EmitContext ec)
2830 il_label = ec.ig.DefineLabel ();
2831 il_label_set = true;
2836 public Label GetILLabelCode (EmitContext ec)
2838 if (!il_label_code_set){
2839 il_label_code = ec.ig.DefineLabel ();
2840 il_label_code_set = true;
2842 return il_label_code;
2846 // Resolves the expression, reduces it to a literal if possible
2847 // and then converts it to the requested type.
2849 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2851 Expression e = label.Resolve (ec);
2856 Constant c = e as Constant;
2858 Report.Error (150, loc, "A constant value is expected");
2862 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2863 converted = NullStringCase;
2867 if (allow_nullable && c.GetValue () == null) {
2868 converted = NullStringCase;
2872 c = c.ImplicitConversionRequired (required_type, loc);
2876 converted = c.GetValue ();
2880 public void Erorr_AlreadyOccurs (Type switchType, SwitchLabel collisionWith)
2883 if (converted == null)
2885 else if (converted == NullStringCase)
2887 else if (TypeManager.IsEnumType (switchType))
2888 label = TypeManager.CSharpEnumValue (switchType, converted);
2890 label = converted.ToString ();
2892 Report.SymbolRelatedToPreviousError (collisionWith.loc, null);
2893 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2896 public SwitchLabel Clone (CloneContext clonectx)
2898 return new SwitchLabel (label.Clone (clonectx), loc);
2902 public class SwitchSection {
2903 // An array of SwitchLabels.
2904 public readonly ArrayList Labels;
2905 public readonly Block Block;
2907 public SwitchSection (ArrayList labels, Block block)
2913 public SwitchSection Clone (CloneContext clonectx)
2915 ArrayList cloned_labels = new ArrayList ();
2917 foreach (SwitchLabel sl in cloned_labels)
2918 cloned_labels.Add (sl.Clone (clonectx));
2920 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
2924 public class Switch : Statement {
2925 public ArrayList Sections;
2926 public Expression Expr;
2929 /// Maps constants whose type type SwitchType to their SwitchLabels.
2931 public IDictionary Elements;
2934 /// The governing switch type
2936 public Type SwitchType;
2941 Label default_target;
2943 Expression new_expr;
2945 SwitchSection constant_section;
2946 SwitchSection default_section;
2950 // Nullable Types support for GMCS.
2952 Nullable.Unwrap unwrap;
2954 protected bool HaveUnwrap {
2955 get { return unwrap != null; }
2958 protected bool HaveUnwrap {
2959 get { return false; }
2964 // The types allowed to be implicitly cast from
2965 // on the governing type
2967 static Type [] allowed_types;
2969 public Switch (Expression e, ArrayList sects, Location l)
2976 public bool GotDefault {
2978 return default_section != null;
2982 public Label DefaultTarget {
2984 return default_target;
2989 // Determines the governing type for a switch. The returned
2990 // expression might be the expression from the switch, or an
2991 // expression that includes any potential conversions to the
2992 // integral types or to string.
2994 Expression SwitchGoverningType (EmitContext ec, Expression expr)
2996 Type t = TypeManager.DropGenericTypeArguments (expr.Type);
2998 if (t == TypeManager.byte_type ||
2999 t == TypeManager.sbyte_type ||
3000 t == TypeManager.ushort_type ||
3001 t == TypeManager.short_type ||
3002 t == TypeManager.uint32_type ||
3003 t == TypeManager.int32_type ||
3004 t == TypeManager.uint64_type ||
3005 t == TypeManager.int64_type ||
3006 t == TypeManager.char_type ||
3007 t == TypeManager.string_type ||
3008 t == TypeManager.bool_type ||
3009 t.IsSubclassOf (TypeManager.enum_type))
3012 if (allowed_types == null){
3013 allowed_types = new Type [] {
3014 TypeManager.sbyte_type,
3015 TypeManager.byte_type,
3016 TypeManager.short_type,
3017 TypeManager.ushort_type,
3018 TypeManager.int32_type,
3019 TypeManager.uint32_type,
3020 TypeManager.int64_type,
3021 TypeManager.uint64_type,
3022 TypeManager.char_type,
3023 TypeManager.string_type,
3024 TypeManager.bool_type
3029 // Try to find a *user* defined implicit conversion.
3031 // If there is no implicit conversion, or if there are multiple
3032 // conversions, we have to report an error
3034 Expression converted = null;
3035 foreach (Type tt in allowed_types){
3038 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3043 // Ignore over-worked ImplicitUserConversions that do
3044 // an implicit conversion in addition to the user conversion.
3046 if (!(e is UserCast))
3049 if (converted != null){
3050 Report.ExtraInformation (
3052 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3053 TypeManager.CSharpName (expr.Type)));
3063 // Performs the basic sanity checks on the switch statement
3064 // (looks for duplicate keys and non-constant expressions).
3066 // It also returns a hashtable with the keys that we will later
3067 // use to compute the switch tables
3069 bool CheckSwitch (EmitContext ec)
3072 Elements = Sections.Count > 10 ?
3073 (IDictionary)new Hashtable () :
3074 (IDictionary)new ListDictionary ();
3076 foreach (SwitchSection ss in Sections){
3077 foreach (SwitchLabel sl in ss.Labels){
3078 if (sl.Label == null){
3079 if (default_section != null){
3080 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3083 default_section = ss;
3087 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3092 object key = sl.Converted;
3094 Elements.Add (key, sl);
3095 } catch (ArgumentException) {
3096 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3104 void EmitObjectInteger (ILGenerator ig, object k)
3107 IntConstant.EmitInt (ig, (int) k);
3108 else if (k is Constant) {
3109 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3112 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3115 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3117 IntConstant.EmitInt (ig, (int) (long) k);
3118 ig.Emit (OpCodes.Conv_I8);
3121 LongConstant.EmitLong (ig, (long) k);
3123 else if (k is ulong)
3125 ulong ul = (ulong) k;
3128 IntConstant.EmitInt (ig, unchecked ((int) ul));
3129 ig.Emit (OpCodes.Conv_U8);
3133 LongConstant.EmitLong (ig, unchecked ((long) ul));
3137 IntConstant.EmitInt (ig, (int) ((char) k));
3138 else if (k is sbyte)
3139 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3141 IntConstant.EmitInt (ig, (int) ((byte) k));
3142 else if (k is short)
3143 IntConstant.EmitInt (ig, (int) ((short) k));
3144 else if (k is ushort)
3145 IntConstant.EmitInt (ig, (int) ((ushort) k));
3147 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3149 throw new Exception ("Unhandled case");
3152 // structure used to hold blocks of keys while calculating table switch
3153 class KeyBlock : IComparable
3155 public KeyBlock (long _nFirst)
3157 nFirst = nLast = _nFirst;
3161 public ArrayList rgKeys = null;
3162 // how many items are in the bucket
3163 public int Size = 1;
3166 get { return (int) (nLast - nFirst + 1); }
3168 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
3170 return kbLast.nLast - kbFirst.nFirst + 1;
3172 public int CompareTo (object obj)
3174 KeyBlock kb = (KeyBlock) obj;
3175 int nLength = Length;
3176 int nLengthOther = kb.Length;
3177 if (nLengthOther == nLength)
3178 return (int) (kb.nFirst - nFirst);
3179 return nLength - nLengthOther;
3184 /// This method emits code for a lookup-based switch statement (non-string)
3185 /// Basically it groups the cases into blocks that are at least half full,
3186 /// and then spits out individual lookup opcodes for each block.
3187 /// It emits the longest blocks first, and short blocks are just
3188 /// handled with direct compares.
3190 /// <param name="ec"></param>
3191 /// <param name="val"></param>
3192 /// <returns></returns>
3193 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3195 int cElements = Elements.Count;
3196 object [] rgKeys = new object [cElements];
3197 Elements.Keys.CopyTo (rgKeys, 0);
3198 Array.Sort (rgKeys);
3200 // initialize the block list with one element per key
3201 ArrayList rgKeyBlocks = new ArrayList ();
3202 foreach (object key in rgKeys)
3203 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3206 // iteratively merge the blocks while they are at least half full
3207 // there's probably a really cool way to do this with a tree...
3208 while (rgKeyBlocks.Count > 1)
3210 ArrayList rgKeyBlocksNew = new ArrayList ();
3211 kbCurr = (KeyBlock) rgKeyBlocks [0];
3212 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3214 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3215 if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
3218 kbCurr.nLast = kb.nLast;
3219 kbCurr.Size += kb.Size;
3223 // start a new block
3224 rgKeyBlocksNew.Add (kbCurr);
3228 rgKeyBlocksNew.Add (kbCurr);
3229 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3231 rgKeyBlocks = rgKeyBlocksNew;
3234 // initialize the key lists
3235 foreach (KeyBlock kb in rgKeyBlocks)
3236 kb.rgKeys = new ArrayList ();
3238 // fill the key lists
3240 if (rgKeyBlocks.Count > 0) {
3241 kbCurr = (KeyBlock) rgKeyBlocks [0];
3242 foreach (object key in rgKeys)
3244 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
3245 System.Convert.ToInt64 (key) > kbCurr.nLast;
3247 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3248 kbCurr.rgKeys.Add (key);
3252 // sort the blocks so we can tackle the largest ones first
3253 rgKeyBlocks.Sort ();
3255 // okay now we can start...
3256 ILGenerator ig = ec.ig;
3257 Label lblEnd = ig.DefineLabel (); // at the end ;-)
3258 Label lblDefault = ig.DefineLabel ();
3260 Type typeKeys = null;
3261 if (rgKeys.Length > 0)
3262 typeKeys = rgKeys [0].GetType (); // used for conversions
3266 if (TypeManager.IsEnumType (SwitchType))
3267 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3269 compare_type = SwitchType;
3271 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3273 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3274 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3277 foreach (object key in kb.rgKeys)
3279 ig.Emit (OpCodes.Ldloc, val);
3280 EmitObjectInteger (ig, key);
3281 SwitchLabel sl = (SwitchLabel) Elements [key];
3282 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3287 // TODO: if all the keys in the block are the same and there are
3288 // no gaps/defaults then just use a range-check.
3289 if (compare_type == TypeManager.int64_type ||
3290 compare_type == TypeManager.uint64_type)
3292 // TODO: optimize constant/I4 cases
3294 // check block range (could be > 2^31)
3295 ig.Emit (OpCodes.Ldloc, val);
3296 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3297 ig.Emit (OpCodes.Blt, lblDefault);
3298 ig.Emit (OpCodes.Ldloc, val);
3299 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
3300 ig.Emit (OpCodes.Bgt, lblDefault);
3303 ig.Emit (OpCodes.Ldloc, val);
3306 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3307 ig.Emit (OpCodes.Sub);
3309 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3314 ig.Emit (OpCodes.Ldloc, val);
3315 int nFirst = (int) kb.nFirst;
3318 IntConstant.EmitInt (ig, nFirst);
3319 ig.Emit (OpCodes.Sub);
3321 else if (nFirst < 0)
3323 IntConstant.EmitInt (ig, -nFirst);
3324 ig.Emit (OpCodes.Add);
3328 // first, build the list of labels for the switch
3330 int cJumps = kb.Length;
3331 Label [] rgLabels = new Label [cJumps];
3332 for (int iJump = 0; iJump < cJumps; iJump++)
3334 object key = kb.rgKeys [iKey];
3335 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
3337 SwitchLabel sl = (SwitchLabel) Elements [key];
3338 rgLabels [iJump] = sl.GetILLabel (ec);
3342 rgLabels [iJump] = lblDefault;
3344 // emit the switch opcode
3345 ig.Emit (OpCodes.Switch, rgLabels);
3348 // mark the default for this block
3350 ig.MarkLabel (lblDefault);
3353 // TODO: find the default case and emit it here,
3354 // to prevent having to do the following jump.
3355 // make sure to mark other labels in the default section
3357 // the last default just goes to the end
3358 ig.Emit (OpCodes.Br, lblDefault);
3360 // now emit the code for the sections
3361 bool fFoundDefault = false;
3362 bool fFoundNull = false;
3363 foreach (SwitchSection ss in Sections)
3365 foreach (SwitchLabel sl in ss.Labels)
3366 if (sl.Converted == SwitchLabel.NullStringCase)
3370 foreach (SwitchSection ss in Sections)
3372 foreach (SwitchLabel sl in ss.Labels)
3374 ig.MarkLabel (sl.GetILLabel (ec));
3375 ig.MarkLabel (sl.GetILLabelCode (ec));
3376 if (sl.Converted == SwitchLabel.NullStringCase)
3377 ig.MarkLabel (null_target);
3378 else if (sl.Label == null) {
3379 ig.MarkLabel (lblDefault);
3380 fFoundDefault = true;
3382 ig.MarkLabel (null_target);
3388 if (!fFoundDefault) {
3389 ig.MarkLabel (lblDefault);
3390 if (HaveUnwrap && !fFoundNull) {
3391 ig.MarkLabel (null_target);
3395 ig.MarkLabel (lblEnd);
3398 // This simple emit switch works, but does not take advantage of the
3400 // TODO: remove non-string logic from here
3401 // TODO: binary search strings?
3403 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3405 ILGenerator ig = ec.ig;
3406 Label end_of_switch = ig.DefineLabel ();
3407 Label next_test = ig.DefineLabel ();
3408 bool first_test = true;
3409 bool pending_goto_end = false;
3410 bool null_marked = false;
3412 int section_count = Sections.Count;
3414 // TODO: implement switch optimization for string by using Hashtable
3415 //if (SwitchType == TypeManager.string_type && section_count > 7)
3416 // Console.WriteLine ("Switch optimization possible " + loc);
3418 ig.Emit (OpCodes.Ldloc, val);
3420 if (Elements.Contains (SwitchLabel.NullStringCase)){
3421 ig.Emit (OpCodes.Brfalse, null_target);
3423 ig.Emit (OpCodes.Brfalse, default_target);
3425 ig.Emit (OpCodes.Ldloc, val);
3426 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3427 ig.Emit (OpCodes.Stloc, val);
3429 for (int section = 0; section < section_count; section++){
3430 SwitchSection ss = (SwitchSection) Sections [section];
3432 if (ss == default_section)
3435 Label sec_begin = ig.DefineLabel ();
3437 ig.Emit (OpCodes.Nop);
3439 if (pending_goto_end)
3440 ig.Emit (OpCodes.Br, end_of_switch);
3442 int label_count = ss.Labels.Count;
3444 for (int label = 0; label < label_count; label++){
3445 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3446 ig.MarkLabel (sl.GetILLabel (ec));
3449 ig.MarkLabel (next_test);
3450 next_test = ig.DefineLabel ();
3453 // If we are the default target
3455 if (sl.Label != null){
3456 object lit = sl.Converted;
3458 if (lit == SwitchLabel.NullStringCase){
3460 if (label + 1 == label_count)
3461 ig.Emit (OpCodes.Br, next_test);
3465 ig.Emit (OpCodes.Ldloc, val);
3466 ig.Emit (OpCodes.Ldstr, (string)lit);
3467 if (label_count == 1)
3468 ig.Emit (OpCodes.Bne_Un, next_test);
3470 if (label+1 == label_count)
3471 ig.Emit (OpCodes.Bne_Un, next_test);
3473 ig.Emit (OpCodes.Beq, sec_begin);
3478 ig.MarkLabel (null_target);
3481 ig.MarkLabel (sec_begin);
3482 foreach (SwitchLabel sl in ss.Labels)
3483 ig.MarkLabel (sl.GetILLabelCode (ec));
3486 pending_goto_end = !ss.Block.HasRet;
3489 ig.MarkLabel (next_test);
3490 ig.MarkLabel (default_target);
3492 ig.MarkLabel (null_target);
3493 if (default_section != null)
3494 default_section.Block.Emit (ec);
3495 ig.MarkLabel (end_of_switch);
3498 SwitchSection FindSection (SwitchLabel label)
3500 foreach (SwitchSection ss in Sections){
3501 foreach (SwitchLabel sl in ss.Labels){
3510 public override bool Resolve (EmitContext ec)
3512 Expr = Expr.Resolve (ec);
3516 new_expr = SwitchGoverningType (ec, Expr);
3519 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3520 unwrap = Nullable.Unwrap.Create (Expr, ec);
3524 new_expr = SwitchGoverningType (ec, unwrap);
3528 if (new_expr == null){
3529 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3534 SwitchType = new_expr.Type;
3536 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3537 Report.FeatureIsNotISO1 (loc, "switch expression of boolean type");
3541 if (!CheckSwitch (ec))
3545 Elements.Remove (SwitchLabel.NullStringCase);
3547 Switch old_switch = ec.Switch;
3549 ec.Switch.SwitchType = SwitchType;
3551 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3552 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3554 is_constant = new_expr is Constant;
3556 object key = ((Constant) new_expr).GetValue ();
3557 SwitchLabel label = (SwitchLabel) Elements [key];
3559 constant_section = FindSection (label);
3560 if (constant_section == null)
3561 constant_section = default_section;
3565 foreach (SwitchSection ss in Sections){
3567 ec.CurrentBranching.CreateSibling (
3568 null, FlowBranching.SiblingType.SwitchSection);
3572 if (is_constant && (ss != constant_section)) {
3573 // If we're a constant switch, we're only emitting
3574 // one single section - mark all the others as
3576 ec.CurrentBranching.CurrentUsageVector.Goto ();
3577 if (!ss.Block.ResolveUnreachable (ec, true))
3580 if (!ss.Block.Resolve (ec))
3585 if (default_section == null)
3586 ec.CurrentBranching.CreateSibling (
3587 null, FlowBranching.SiblingType.SwitchSection);
3589 ec.EndFlowBranching ();
3590 ec.Switch = old_switch;
3592 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3597 protected override void DoEmit (EmitContext ec)
3599 ILGenerator ig = ec.ig;
3601 default_target = ig.DefineLabel ();
3602 null_target = ig.DefineLabel ();
3604 // Store variable for comparission purposes
3607 value = ig.DeclareLocal (SwitchType);
3609 unwrap.EmitCheck (ec);
3610 ig.Emit (OpCodes.Brfalse, null_target);
3612 ig.Emit (OpCodes.Stloc, value);
3614 } else if (!is_constant) {
3615 value = ig.DeclareLocal (SwitchType);
3617 ig.Emit (OpCodes.Stloc, value);
3622 // Setup the codegen context
3624 Label old_end = ec.LoopEnd;
3625 Switch old_switch = ec.Switch;
3627 ec.LoopEnd = ig.DefineLabel ();
3632 if (constant_section != null)
3633 constant_section.Block.Emit (ec);
3634 } else if (SwitchType == TypeManager.string_type)
3635 SimpleSwitchEmit (ec, value);
3637 TableSwitchEmit (ec, value);
3639 // Restore context state.
3640 ig.MarkLabel (ec.LoopEnd);
3643 // Restore the previous context
3645 ec.LoopEnd = old_end;
3646 ec.Switch = old_switch;
3649 protected override void CloneTo (CloneContext clonectx, Statement t)
3651 Switch target = (Switch) t;
3653 target.Expr = Expr.Clone (clonectx);
3654 target.Sections = new ArrayList ();
3655 foreach (SwitchSection ss in Sections){
3656 target.Sections.Add (ss.Clone (clonectx));
3661 public abstract class ExceptionStatement : Statement
3663 public abstract void EmitFinally (EmitContext ec);
3665 protected bool emit_finally = true;
3666 ArrayList parent_vectors;
3668 protected void DoEmitFinally (EmitContext ec)
3671 ec.ig.BeginFinallyBlock ();
3672 else if (ec.InIterator)
3673 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3677 protected void ResolveFinally (FlowBranchingException branching)
3679 emit_finally = branching.EmitFinally;
3681 branching.Parent.StealFinallyClauses (ref parent_vectors);
3685 public class Lock : ExceptionStatement {
3687 public Statement Statement;
3688 TemporaryVariable temp;
3690 public Lock (Expression expr, Statement stmt, Location l)
3697 public override bool Resolve (EmitContext ec)
3699 expr = expr.Resolve (ec);
3703 if (expr.Type.IsValueType){
3704 Report.Error (185, loc,
3705 "`{0}' is not a reference type as required by the lock statement",
3706 TypeManager.CSharpName (expr.Type));
3710 FlowBranchingException branching = ec.StartFlowBranching (this);
3711 bool ok = Statement.Resolve (ec);
3713 ResolveFinally (branching);
3715 ec.EndFlowBranching ();
3717 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3718 // So, ensure there's some IL code after the finally block.
3719 ec.NeedReturnLabel ();
3721 // Avoid creating libraries that reference the internal
3724 if (t == TypeManager.null_type)
3725 t = TypeManager.object_type;
3727 temp = new TemporaryVariable (t, loc);
3733 protected override void DoEmit (EmitContext ec)
3735 ILGenerator ig = ec.ig;
3737 temp.Store (ec, expr);
3739 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3743 ig.BeginExceptionBlock ();
3744 Statement.Emit (ec);
3749 ig.EndExceptionBlock ();
3752 public override void EmitFinally (EmitContext ec)
3755 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3758 protected override void CloneTo (CloneContext clonectx, Statement t)
3760 Lock target = (Lock) t;
3762 target.expr = expr.Clone (clonectx);
3763 target.Statement = Statement.Clone (clonectx);
3767 public class Unchecked : Statement {
3770 public Unchecked (Block b)
3776 public override bool Resolve (EmitContext ec)
3778 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3779 return Block.Resolve (ec);
3782 protected override void DoEmit (EmitContext ec)
3784 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3788 protected override void CloneTo (CloneContext clonectx, Statement t)
3790 Unchecked target = (Unchecked) t;
3792 target.Block = clonectx.LookupBlock (Block);
3796 public class Checked : Statement {
3799 public Checked (Block b)
3802 b.Unchecked = false;
3805 public override bool Resolve (EmitContext ec)
3807 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3808 return Block.Resolve (ec);
3811 protected override void DoEmit (EmitContext ec)
3813 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3817 protected override void CloneTo (CloneContext clonectx, Statement t)
3819 Checked target = (Checked) t;
3821 target.Block = clonectx.LookupBlock (Block);
3825 public class Unsafe : Statement {
3828 public Unsafe (Block b)
3831 Block.Unsafe = true;
3834 public override bool Resolve (EmitContext ec)
3836 using (ec.With (EmitContext.Flags.InUnsafe, true))
3837 return Block.Resolve (ec);
3840 protected override void DoEmit (EmitContext ec)
3842 using (ec.With (EmitContext.Flags.InUnsafe, true))
3845 protected override void CloneTo (CloneContext clonectx, Statement t)
3847 Unsafe target = (Unsafe) t;
3849 target.Block = clonectx.LookupBlock (Block);
3856 public class Fixed : Statement {
3858 ArrayList declarators;
3859 Statement statement;
3864 abstract class Emitter
3866 protected LocalInfo vi;
3867 protected Expression converted;
3869 protected Emitter (Expression expr, LocalInfo li)
3875 public abstract void Emit (EmitContext ec);
3876 public abstract void EmitExit (EmitContext ec);
3879 class ExpressionEmitter : Emitter {
3880 public ExpressionEmitter (Expression converted, LocalInfo li) :
3881 base (converted, li)
3885 public override void Emit (EmitContext ec) {
3887 // Store pointer in pinned location
3889 converted.Emit (ec);
3890 vi.Variable.EmitAssign (ec);
3893 public override void EmitExit (EmitContext ec)
3895 ec.ig.Emit (OpCodes.Ldc_I4_0);
3896 ec.ig.Emit (OpCodes.Conv_U);
3897 vi.Variable.EmitAssign (ec);
3901 class StringEmitter : Emitter {
3902 LocalBuilder pinned_string;
3905 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3911 public override void Emit (EmitContext ec)
3913 ILGenerator ig = ec.ig;
3914 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3916 converted.Emit (ec);
3917 ig.Emit (OpCodes.Stloc, pinned_string);
3919 Expression sptr = new StringPtr (pinned_string, loc);
3920 converted = Convert.ImplicitConversionRequired (
3921 ec, sptr, vi.VariableType, loc);
3923 if (converted == null)
3926 converted.Emit (ec);
3927 vi.Variable.EmitAssign (ec);
3930 public override void EmitExit (EmitContext ec)
3932 ec.ig.Emit (OpCodes.Ldnull);
3933 ec.ig.Emit (OpCodes.Stloc, pinned_string);
3937 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3940 declarators = decls;
3945 public Statement Statement {
3946 get { return statement; }
3949 public override bool Resolve (EmitContext ec)
3952 Expression.UnsafeError (loc);
3956 TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
3960 expr_type = texpr.Type;
3962 data = new Emitter [declarators.Count];
3964 if (!expr_type.IsPointer){
3965 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
3970 foreach (Pair p in declarators){
3971 LocalInfo vi = (LocalInfo) p.First;
3972 Expression e = (Expression) p.Second;
3974 vi.VariableInfo.SetAssigned (ec);
3975 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
3978 // The rules for the possible declarators are pretty wise,
3979 // but the production on the grammar is more concise.
3981 // So we have to enforce these rules here.
3983 // We do not resolve before doing the case 1 test,
3984 // because the grammar is explicit in that the token &
3985 // is present, so we need to test for this particular case.
3989 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
3994 // Case 1: & object.
3996 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3997 Expression child = ((Unary) e).Expr;
3999 if (child is ParameterReference || child is LocalVariableReference){
4002 "No need to use fixed statement for parameters or " +
4003 "local variable declarations (address is already " +
4008 ec.InFixedInitializer = true;
4010 ec.InFixedInitializer = false;
4014 child = ((Unary) e).Expr;
4016 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4019 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4020 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4024 data [i] = new ExpressionEmitter (e, vi);
4030 ec.InFixedInitializer = true;
4032 ec.InFixedInitializer = false;
4039 if (e.Type.IsArray){
4040 Type array_type = TypeManager.GetElementType (e.Type);
4043 // Provided that array_type is unmanaged,
4045 if (!TypeManager.VerifyUnManaged (array_type, loc))
4049 // and T* is implicitly convertible to the
4050 // pointer type given in the fixed statement.
4052 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4054 Expression converted = Convert.ImplicitConversionRequired (
4055 ec, array_ptr, vi.VariableType, loc);
4056 if (converted == null)
4059 data [i] = new ExpressionEmitter (converted, vi);
4068 if (e.Type == TypeManager.string_type){
4069 data [i] = new StringEmitter (e, vi, loc);
4074 // Case 4: fixed buffer
4075 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4076 if (fixed_buffer_ptr != null) {
4077 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4082 // For other cases, flag a `this is already fixed expression'
4084 if (e is LocalVariableReference || e is ParameterReference ||
4085 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4087 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4091 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4095 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4096 bool ok = statement.Resolve (ec);
4097 bool flow_unreachable = ec.EndFlowBranching ();
4098 has_ret = flow_unreachable;
4103 protected override void DoEmit (EmitContext ec)
4105 for (int i = 0; i < data.Length; i++) {
4109 statement.Emit (ec);
4115 // Clear the pinned variable
4117 for (int i = 0; i < data.Length; i++) {
4118 data [i].EmitExit (ec);
4122 protected override void CloneTo (CloneContext clonectx, Statement t)
4124 Fixed target = (Fixed) t;
4126 target.type = type.Clone (clonectx);
4127 target.declarators = new ArrayList ();
4128 foreach (LocalInfo var in declarators)
4129 target.declarators.Add (clonectx.LookupVariable (var));
4130 target.statement = statement.Clone (clonectx);
4134 public class Catch : Statement {
4135 public readonly string Name;
4137 public Block VarBlock;
4139 Expression type_expr;
4142 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4147 VarBlock = var_block;
4151 public Type CatchType {
4157 public bool IsGeneral {
4159 return type_expr == null;
4163 protected override void DoEmit(EmitContext ec)
4165 ILGenerator ig = ec.ig;
4167 if (CatchType != null)
4168 ig.BeginCatchBlock (CatchType);
4170 ig.BeginCatchBlock (TypeManager.object_type);
4172 if (VarBlock != null)
4176 LocalInfo vi = Block.GetLocalInfo (Name);
4178 throw new Exception ("Variable does not exist in this block");
4180 if (vi.Variable.NeedsTemporary) {
4181 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4182 ig.Emit (OpCodes.Stloc, e);
4184 vi.Variable.EmitInstance (ec);
4185 ig.Emit (OpCodes.Ldloc, e);
4186 vi.Variable.EmitAssign (ec);
4188 vi.Variable.EmitAssign (ec);
4190 ig.Emit (OpCodes.Pop);
4195 public override bool Resolve (EmitContext ec)
4197 using (ec.With (EmitContext.Flags.InCatch, true)) {
4198 if (type_expr != null) {
4199 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4205 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4206 Error (155, "The type caught or thrown must be derived from System.Exception");
4212 if (!Block.Resolve (ec))
4215 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4216 // emit the "unused variable" warnings.
4217 if (VarBlock != null)
4218 return VarBlock.Resolve (ec);
4224 protected override void CloneTo (CloneContext clonectx, Statement t)
4226 Catch target = (Catch) t;
4228 target.type_expr = type_expr.Clone (clonectx);
4229 target.VarBlock = clonectx.LookupBlock (VarBlock);
4230 target.Block = clonectx.LookupBlock (Block);
4234 public class Try : ExceptionStatement {
4235 public Block Fini, Block;
4236 public ArrayList Specific;
4237 public Catch General;
4239 bool need_exc_block;
4242 // specific, general and fini might all be null.
4244 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4246 if (specific == null && general == null){
4247 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4251 this.Specific = specific;
4252 this.General = general;
4257 public override bool Resolve (EmitContext ec)
4261 FlowBranchingException branching = ec.StartFlowBranching (this);
4263 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4265 if (!Block.Resolve (ec))
4268 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4270 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4272 Type[] prevCatches = new Type [Specific.Count];
4274 foreach (Catch c in Specific){
4275 ec.CurrentBranching.CreateSibling (
4276 c.Block, FlowBranching.SiblingType.Catch);
4278 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4280 if (c.Name != null) {
4281 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4283 throw new Exception ();
4285 vi.VariableInfo = null;
4288 if (!c.Resolve (ec))
4291 Type resolvedType = c.CatchType;
4292 for (int ii = 0; ii < last_index; ++ii) {
4293 if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
4294 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches [ii].FullName);
4299 prevCatches [last_index++] = resolvedType;
4300 need_exc_block = true;
4303 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4305 if (General != null){
4306 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4307 foreach (Catch c in Specific){
4308 if (c.CatchType == TypeManager.exception_type) {
4309 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'");
4314 ec.CurrentBranching.CreateSibling (
4315 General.Block, FlowBranching.SiblingType.Catch);
4317 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4319 if (!General.Resolve (ec))
4322 need_exc_block = true;
4325 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4329 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4331 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4332 using (ec.With (EmitContext.Flags.InFinally, true)) {
4333 if (!Fini.Resolve (ec))
4338 need_exc_block = true;
4341 if (ec.InIterator) {
4342 ResolveFinally (branching);
4343 need_exc_block |= emit_finally;
4345 emit_finally = Fini != null;
4347 ec.EndFlowBranching ();
4349 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4350 // So, ensure there's some IL code after the finally block.
4351 ec.NeedReturnLabel ();
4353 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4355 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4360 protected override void DoEmit (EmitContext ec)
4362 ILGenerator ig = ec.ig;
4365 ig.BeginExceptionBlock ();
4368 foreach (Catch c in Specific)
4371 if (General != null)
4376 ig.EndExceptionBlock ();
4379 public override void EmitFinally (EmitContext ec)
4385 public bool HasCatch
4388 return General != null || Specific.Count > 0;
4392 protected override void CloneTo (CloneContext clonectx, Statement t)
4394 Try target = (Try) t;
4396 target.Block = clonectx.LookupBlock (Block);
4398 target.Fini = clonectx.LookupBlock (Fini);
4399 if (General != null)
4400 target.General = (Catch) General.Clone (clonectx);
4401 if (Specific != null){
4402 target.Specific = new ArrayList ();
4403 foreach (Catch c in Specific)
4404 target.Specific.Add (c.Clone (clonectx));
4409 public class Using : ExceptionStatement {
4410 object expression_or_block;
4411 public Statement Statement;
4415 Expression [] resolved_vars;
4416 Expression [] converted_vars;
4417 Expression [] assign;
4418 TemporaryVariable local_copy;
4420 public Using (object expression_or_block, Statement stmt, Location l)
4422 this.expression_or_block = expression_or_block;
4428 // Resolves for the case of using using a local variable declaration.
4430 bool ResolveLocalVariableDecls (EmitContext ec)
4432 resolved_vars = new Expression[var_list.Count];
4433 assign = new Expression [var_list.Count];
4434 converted_vars = new Expression[var_list.Count];
4436 for (int i = 0; i < assign.Length; ++i) {
4437 DictionaryEntry e = (DictionaryEntry) var_list [i];
4438 Expression var = (Expression) e.Key;
4439 Expression new_expr = (Expression) e.Value;
4441 Expression a = new Assign (var, new_expr, loc);
4446 resolved_vars [i] = var;
4449 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4450 converted_vars [i] = var;
4454 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4456 Error_IsNotConvertibleToIDisposable (var);
4460 converted_vars [i] = a;
4466 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4468 Report.SymbolRelatedToPreviousError (expr.Type);
4469 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4470 expr.GetSignatureForError ());
4473 bool ResolveExpression (EmitContext ec)
4475 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4476 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4477 Error_IsNotConvertibleToIDisposable (expr);
4482 local_copy = new TemporaryVariable (expr_type, loc);
4483 local_copy.Resolve (ec);
4489 // Emits the code for the case of using using a local variable declaration.
4491 void EmitLocalVariableDecls (EmitContext ec)
4493 ILGenerator ig = ec.ig;
4496 for (i = 0; i < assign.Length; i++) {
4497 ExpressionStatement es = assign [i] as ExpressionStatement;
4500 es.EmitStatement (ec);
4502 assign [i].Emit (ec);
4503 ig.Emit (OpCodes.Pop);
4507 ig.BeginExceptionBlock ();
4509 Statement.Emit (ec);
4511 var_list.Reverse ();
4516 void EmitLocalVariableDeclFinally (EmitContext ec)
4518 ILGenerator ig = ec.ig;
4520 int i = assign.Length;
4521 for (int ii = 0; ii < var_list.Count; ++ii){
4522 Expression var = resolved_vars [--i];
4523 Label skip = ig.DefineLabel ();
4526 ig.BeginFinallyBlock ();
4528 if (!var.Type.IsValueType) {
4530 ig.Emit (OpCodes.Brfalse, skip);
4531 converted_vars [i].Emit (ec);
4532 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4534 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4536 if (!(ml is MethodGroupExpr)) {
4538 ig.Emit (OpCodes.Box, var.Type);
4539 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4541 MethodInfo mi = null;
4543 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4544 if (TypeManager.GetParameterData (mk).Count == 0) {
4551 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4555 IMemoryLocation mloc = (IMemoryLocation) var;
4557 mloc.AddressOf (ec, AddressOp.Load);
4558 ig.Emit (OpCodes.Call, mi);
4562 ig.MarkLabel (skip);
4565 ig.EndExceptionBlock ();
4567 ig.BeginFinallyBlock ();
4572 void EmitExpression (EmitContext ec)
4575 // Make a copy of the expression and operate on that.
4577 ILGenerator ig = ec.ig;
4579 local_copy.Store (ec, expr);
4582 ig.BeginExceptionBlock ();
4584 Statement.Emit (ec);
4588 ig.EndExceptionBlock ();
4591 void EmitExpressionFinally (EmitContext ec)
4593 ILGenerator ig = ec.ig;
4594 if (!expr_type.IsValueType) {
4595 Label skip = ig.DefineLabel ();
4596 local_copy.Emit (ec);
4597 ig.Emit (OpCodes.Brfalse, skip);
4598 local_copy.Emit (ec);
4599 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4600 ig.MarkLabel (skip);
4602 Expression ml = Expression.MemberLookup (
4603 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4604 "Dispose", Location.Null);
4606 if (!(ml is MethodGroupExpr)) {
4607 local_copy.Emit (ec);
4608 ig.Emit (OpCodes.Box, expr_type);
4609 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4611 MethodInfo mi = null;
4613 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4614 if (TypeManager.GetParameterData (mk).Count == 0) {
4621 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4625 local_copy.AddressOf (ec, AddressOp.Load);
4626 ig.Emit (OpCodes.Call, mi);
4631 public override bool Resolve (EmitContext ec)
4633 if (expression_or_block is DictionaryEntry){
4634 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4635 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4637 if (!ResolveLocalVariableDecls (ec))
4640 } else if (expression_or_block is Expression){
4641 expr = (Expression) expression_or_block;
4643 expr = expr.Resolve (ec);
4647 expr_type = expr.Type;
4649 if (!ResolveExpression (ec))
4653 FlowBranchingException branching = ec.StartFlowBranching (this);
4655 bool ok = Statement.Resolve (ec);
4657 ResolveFinally (branching);
4659 ec.EndFlowBranching ();
4661 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4662 // So, ensure there's some IL code after the finally block.
4663 ec.NeedReturnLabel ();
4668 protected override void DoEmit (EmitContext ec)
4670 if (expression_or_block is DictionaryEntry)
4671 EmitLocalVariableDecls (ec);
4672 else if (expression_or_block is Expression)
4673 EmitExpression (ec);
4676 public override void EmitFinally (EmitContext ec)
4678 if (expression_or_block is DictionaryEntry)
4679 EmitLocalVariableDeclFinally (ec);
4680 else if (expression_or_block is Expression)
4681 EmitExpressionFinally (ec);
4684 protected override void CloneTo (CloneContext clonectx, Statement t)
4686 Using target = (Using) t;
4688 if (expression_or_block is Expression)
4689 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4691 target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
4693 target.Statement = Statement.Clone (clonectx);
4698 /// Implementation of the foreach C# statement
4700 public class Foreach : Statement {
4702 Expression variable;
4704 Statement statement;
4706 CollectionForeach collection;
4708 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4709 Statement stmt, Location l)
4712 this.variable = var;
4718 public Statement Statement {
4719 get { return statement; }
4722 public override bool Resolve (EmitContext ec)
4724 expr = expr.Resolve (ec);
4728 if (expr.Type == TypeManager.null_type) {
4729 Report.Error (186, loc, "Use of null is not valid in this context");
4733 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4734 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4735 expr.ExprClassName);
4740 // We need an instance variable. Not sure this is the best
4741 // way of doing this.
4743 // FIXME: When we implement propertyaccess, will those turn
4744 // out to return values in ExprClass? I think they should.
4746 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4747 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4748 collection.Error_Enumerator ();
4752 if (expr.Type.IsArray) {
4753 array = new ArrayForeach (type, variable, expr, statement, loc);
4754 return array.Resolve (ec);
4757 collection = new CollectionForeach (type, variable, expr, statement, loc);
4758 return collection.Resolve (ec);
4761 protected override void DoEmit (EmitContext ec)
4763 ILGenerator ig = ec.ig;
4765 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4766 ec.LoopBegin = ig.DefineLabel ();
4767 ec.LoopEnd = ig.DefineLabel ();
4769 if (collection != null)
4770 collection.Emit (ec);
4774 ec.LoopBegin = old_begin;
4775 ec.LoopEnd = old_end;
4778 protected class ArrayCounter : TemporaryVariable
4780 public ArrayCounter (Location loc)
4781 : base (TypeManager.int32_type, loc)
4784 public void Initialize (EmitContext ec)
4787 ec.ig.Emit (OpCodes.Ldc_I4_0);
4791 public void Increment (EmitContext ec)
4795 ec.ig.Emit (OpCodes.Ldc_I4_1);
4796 ec.ig.Emit (OpCodes.Add);
4801 protected class ArrayForeach : Statement
4803 Expression variable, expr, conv;
4804 Statement statement;
4806 Expression var_type;
4807 TemporaryVariable[] lengths;
4808 ArrayCounter[] counter;
4811 TemporaryVariable copy;
4814 public ArrayForeach (Expression var_type, Expression var,
4815 Expression expr, Statement stmt, Location l)
4817 this.var_type = var_type;
4818 this.variable = var;
4824 public override bool Resolve (EmitContext ec)
4826 array_type = expr.Type;
4827 rank = array_type.GetArrayRank ();
4829 copy = new TemporaryVariable (array_type, loc);
4832 counter = new ArrayCounter [rank];
4833 lengths = new TemporaryVariable [rank];
4835 ArrayList list = new ArrayList ();
4836 for (int i = 0; i < rank; i++) {
4837 counter [i] = new ArrayCounter (loc);
4838 counter [i].Resolve (ec);
4840 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4841 lengths [i].Resolve (ec);
4843 list.Add (counter [i]);
4846 access = new ElementAccess (copy, list).Resolve (ec);
4850 VarExpr ve = var_type as VarExpr;
4852 // Infer implicitly typed local variable from foreach array type
4853 var_type = new TypeExpression (access.Type, ve.Location);
4856 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4857 if (var_type == null)
4860 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4866 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4867 ec.CurrentBranching.CreateSibling ();
4869 variable = variable.ResolveLValue (ec, conv, loc);
4870 if (variable == null)
4873 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4874 if (!statement.Resolve (ec))
4876 ec.EndFlowBranching ();
4878 // There's no direct control flow from the end of the embedded statement to the end of the loop
4879 ec.CurrentBranching.CurrentUsageVector.Goto ();
4881 ec.EndFlowBranching ();
4886 protected override void DoEmit (EmitContext ec)
4888 ILGenerator ig = ec.ig;
4890 copy.Store (ec, expr);
4892 Label[] test = new Label [rank];
4893 Label[] loop = new Label [rank];
4895 for (int i = 0; i < rank; i++) {
4896 test [i] = ig.DefineLabel ();
4897 loop [i] = ig.DefineLabel ();
4899 lengths [i].EmitThis (ec);
4900 ((ArrayAccess) access).EmitGetLength (ec, i);
4901 lengths [i].EmitStore (ec);
4904 for (int i = 0; i < rank; i++) {
4905 counter [i].Initialize (ec);
4907 ig.Emit (OpCodes.Br, test [i]);
4908 ig.MarkLabel (loop [i]);
4911 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4913 statement.Emit (ec);
4915 ig.MarkLabel (ec.LoopBegin);
4917 for (int i = rank - 1; i >= 0; i--){
4918 counter [i].Increment (ec);
4920 ig.MarkLabel (test [i]);
4921 counter [i].Emit (ec);
4922 lengths [i].Emit (ec);
4923 ig.Emit (OpCodes.Blt, loop [i]);
4926 ig.MarkLabel (ec.LoopEnd);
4930 protected class CollectionForeach : ExceptionStatement
4932 Expression variable, expr;
4933 Statement statement;
4935 TemporaryVariable enumerator;
4939 MethodGroupExpr get_enumerator;
4940 PropertyExpr get_current;
4941 MethodInfo move_next;
4942 Expression var_type;
4943 Type enumerator_type;
4945 bool enumerator_found;
4947 public CollectionForeach (Expression var_type, Expression var,
4948 Expression expr, Statement stmt, Location l)
4950 this.var_type = var_type;
4951 this.variable = var;
4957 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
4959 Type return_type = mi.ReturnType;
4961 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
4963 // Apply the same optimization as MS: skip the GetEnumerator
4964 // returning an IEnumerator, and use the one returning a
4965 // CharEnumerator instead. This allows us to avoid the
4966 // try-finally block and the boxing.
4971 // Ok, we can access it, now make sure that we can do something
4972 // with this `GetEnumerator'
4975 if (return_type == TypeManager.ienumerator_type ||
4976 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
4977 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
4979 // If it is not an interface, lets try to find the methods ourselves.
4980 // For example, if we have:
4981 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4982 // We can avoid the iface call. This is a runtime perf boost.
4983 // even bigger if we have a ValueType, because we avoid the cost
4986 // We have to make sure that both methods exist for us to take
4987 // this path. If one of the methods does not exist, we will just
4988 // use the interface. Sadly, this complex if statement is the only
4989 // way I could do this without a goto
4994 // Prefer a generic enumerator over a non-generic one.
4996 if (return_type.IsInterface && return_type.IsGenericType) {
4997 enumerator_type = return_type;
4998 if (!FetchGetCurrent (ec, return_type))
4999 get_current = new PropertyExpr (
5000 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5001 if (!FetchMoveNext (return_type))
5002 move_next = TypeManager.bool_movenext_void;
5007 if (return_type.IsInterface ||
5008 !FetchMoveNext (return_type) ||
5009 !FetchGetCurrent (ec, return_type)) {
5010 enumerator_type = return_type;
5011 move_next = TypeManager.bool_movenext_void;
5012 get_current = new PropertyExpr (
5013 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5018 // Ok, so they dont return an IEnumerable, we will have to
5019 // find if they support the GetEnumerator pattern.
5022 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5023 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",
5024 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5029 enumerator_type = return_type;
5030 is_disposable = !enumerator_type.IsSealed ||
5031 TypeManager.ImplementsInterface (
5032 enumerator_type, TypeManager.idisposable_type);
5038 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5040 bool FetchMoveNext (Type t)
5042 MemberList move_next_list;
5044 move_next_list = TypeContainer.FindMembers (
5045 t, MemberTypes.Method,
5046 BindingFlags.Public | BindingFlags.Instance,
5047 Type.FilterName, "MoveNext");
5048 if (move_next_list.Count == 0)
5051 foreach (MemberInfo m in move_next_list){
5052 MethodInfo mi = (MethodInfo) m;
5054 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5055 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5065 // Retrieves a `public T get_Current ()' method from the Type `t'
5067 bool FetchGetCurrent (EmitContext ec, Type t)
5069 PropertyExpr pe = Expression.MemberLookup (
5070 ec.ContainerType, t, "Current", MemberTypes.Property,
5071 Expression.AllBindingFlags, loc) as PropertyExpr;
5080 // Retrieves a `public void Dispose ()' method from the Type `t'
5082 static MethodInfo FetchMethodDispose (Type t)
5084 MemberList dispose_list;
5086 dispose_list = TypeContainer.FindMembers (
5087 t, MemberTypes.Method,
5088 BindingFlags.Public | BindingFlags.Instance,
5089 Type.FilterName, "Dispose");
5090 if (dispose_list.Count == 0)
5093 foreach (MemberInfo m in dispose_list){
5094 MethodInfo mi = (MethodInfo) m;
5096 if (TypeManager.GetParameterData (mi).Count == 0){
5097 if (mi.ReturnType == TypeManager.void_type)
5104 public void Error_Enumerator ()
5106 if (enumerator_found) {
5110 Report.Error (1579, loc,
5111 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5112 TypeManager.CSharpName (expr.Type));
5115 public static bool IsOverride (MethodInfo m)
5117 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5119 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5121 if (m is MethodBuilder)
5124 MethodInfo base_method = m.GetBaseDefinition ();
5125 return base_method != m;
5128 bool TryType (EmitContext ec, Type t)
5130 MethodGroupExpr mg = Expression.MemberLookup (
5131 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5132 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5136 MethodInfo result = null;
5137 MethodInfo tmp_move_next = null;
5138 PropertyExpr tmp_get_cur = null;
5139 Type tmp_enumerator_type = enumerator_type;
5140 foreach (MethodInfo mi in mg.Methods) {
5141 if (TypeManager.GetParameterData (mi).Count != 0)
5144 // Check whether GetEnumerator is public
5145 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5148 if (IsOverride (mi))
5151 enumerator_found = true;
5153 if (!GetEnumeratorFilter (ec, mi))
5156 if (result != null) {
5157 if (TypeManager.IsGenericType (result.ReturnType)) {
5158 if (!TypeManager.IsGenericType (mi.ReturnType))
5161 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5162 Report.SymbolRelatedToPreviousError (t);
5163 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5164 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5165 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5169 // Always prefer generics enumerators
5170 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5171 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5172 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5175 Report.SymbolRelatedToPreviousError (result);
5176 Report.SymbolRelatedToPreviousError (mi);
5177 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5178 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5183 tmp_move_next = move_next;
5184 tmp_get_cur = get_current;
5185 tmp_enumerator_type = enumerator_type;
5186 if (mi.DeclaringType == t)
5190 if (result != null) {
5191 move_next = tmp_move_next;
5192 get_current = tmp_get_cur;
5193 enumerator_type = tmp_enumerator_type;
5194 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5195 get_enumerator = new MethodGroupExpr (mi, loc);
5197 if (t != expr.Type) {
5198 expr = Convert.ExplicitConversion (
5201 throw new InternalErrorException ();
5204 get_enumerator.InstanceExpression = expr;
5205 get_enumerator.IsBase = t != expr.Type;
5213 bool ProbeCollectionType (EmitContext ec, Type t)
5215 int errors = Report.Errors;
5216 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5217 if (TryType (ec, tt))
5222 if (Report.Errors > errors)
5226 // Now try to find the method in the interfaces
5228 Type [] ifaces = TypeManager.GetInterfaces (t);
5229 foreach (Type i in ifaces){
5230 if (TryType (ec, i))
5237 public override bool Resolve (EmitContext ec)
5239 enumerator_type = TypeManager.ienumerator_type;
5240 is_disposable = true;
5242 if (!ProbeCollectionType (ec, expr.Type)) {
5243 Error_Enumerator ();
5247 VarExpr ve = var_type as VarExpr;
5249 // Infer implicitly typed local variable from foreach enumerable type
5250 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5253 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5254 if (var_type == null)
5257 enumerator = new TemporaryVariable (enumerator_type, loc);
5258 enumerator.Resolve (ec);
5260 init = new Invocation (get_enumerator, null);
5261 init = init.Resolve (ec);
5265 Expression move_next_expr;
5267 MemberInfo[] mi = new MemberInfo[] { move_next };
5268 MethodGroupExpr mg = new MethodGroupExpr (mi, loc);
5269 mg.InstanceExpression = enumerator;
5271 move_next_expr = new Invocation (mg, null);
5274 get_current.InstanceExpression = enumerator;
5276 Statement block = new CollectionForeachStatement (
5277 var_type.Type, variable, get_current, statement, loc);
5279 loop = new While (move_next_expr, block, loc);
5283 FlowBranchingException branching = null;
5285 branching = ec.StartFlowBranching (this);
5287 if (!loop.Resolve (ec))
5290 if (is_disposable) {
5291 ResolveFinally (branching);
5292 ec.EndFlowBranching ();
5294 emit_finally = true;
5299 protected override void DoEmit (EmitContext ec)
5301 ILGenerator ig = ec.ig;
5303 enumerator.Store (ec, init);
5306 // Protect the code in a try/finalize block, so that
5307 // if the beast implement IDisposable, we get rid of it
5309 if (is_disposable && emit_finally)
5310 ig.BeginExceptionBlock ();
5315 // Now the finally block
5317 if (is_disposable) {
5320 ig.EndExceptionBlock ();
5325 public override void EmitFinally (EmitContext ec)
5327 ILGenerator ig = ec.ig;
5329 if (enumerator_type.IsValueType) {
5330 MethodInfo mi = FetchMethodDispose (enumerator_type);
5332 enumerator.EmitLoadAddress (ec);
5333 ig.Emit (OpCodes.Call, mi);
5335 enumerator.Emit (ec);
5336 ig.Emit (OpCodes.Box, enumerator_type);
5337 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5340 Label call_dispose = ig.DefineLabel ();
5342 enumerator.Emit (ec);
5343 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5344 ig.Emit (OpCodes.Dup);
5345 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5346 ig.Emit (OpCodes.Pop);
5348 Label end_finally = ig.DefineLabel ();
5349 ig.Emit (OpCodes.Br, end_finally);
5351 ig.MarkLabel (call_dispose);
5352 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5353 ig.MarkLabel (end_finally);
5358 protected class CollectionForeachStatement : Statement
5361 Expression variable, current, conv;
5362 Statement statement;
5365 public CollectionForeachStatement (Type type, Expression variable,
5366 Expression current, Statement statement,
5370 this.variable = variable;
5371 this.current = current;
5372 this.statement = statement;
5376 public override bool Resolve (EmitContext ec)
5378 current = current.Resolve (ec);
5379 if (current == null)
5382 conv = Convert.ExplicitConversion (ec, current, type, loc);
5386 assign = new Assign (variable, conv, loc);
5387 if (assign.Resolve (ec) == null)
5390 if (!statement.Resolve (ec))
5396 protected override void DoEmit (EmitContext ec)
5398 assign.EmitStatement (ec);
5399 statement.Emit (ec);
5403 protected override void CloneTo (CloneContext clonectx, Statement t)
5405 Foreach target = (Foreach) t;
5407 target.type = type.Clone (clonectx);
5408 target.variable = variable.Clone (clonectx);
5409 target.expr = expr.Clone (clonectx);
5410 target.statement = statement.Clone (clonectx);