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 true_statement, Location l)
215 TrueStatement = true_statement;
219 public If (Expression expr,
220 Statement true_statement,
221 Statement false_statement,
225 TrueStatement = true_statement;
226 FalseStatement = false_statement;
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 bool_expr, 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 bool_expr, Statement statement, Location l)
439 this.expr = bool_expr;
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 init_statement,
550 InitStatement = init_statement;
552 Increment = increment;
553 Statement = statement;
557 public override bool Resolve (EmitContext ec)
561 if (InitStatement != null){
562 if (!InitStatement.Resolve (ec))
567 Test = Expression.ResolveBoolean (ec, Test, loc);
570 else if (Test is BoolConstant){
571 BoolConstant bc = (BoolConstant) Test;
573 if (bc.Value == false){
574 if (!Statement.ResolveUnreachable (ec, true))
576 if ((Increment != null) &&
577 !Increment.ResolveUnreachable (ec, false))
587 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
589 ec.CurrentBranching.CreateSibling ();
591 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
593 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
594 if (!Statement.Resolve (ec))
596 ec.EndFlowBranching ();
598 if (Increment != null){
599 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
600 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
603 if (!Increment.Resolve (ec))
608 // There's no direct control flow from the end of the embedded statement to the end of the loop
609 ec.CurrentBranching.CurrentUsageVector.Goto ();
611 ec.EndFlowBranching ();
616 protected override void DoEmit (EmitContext ec)
621 ILGenerator ig = ec.ig;
622 Label old_begin = ec.LoopBegin;
623 Label old_end = ec.LoopEnd;
624 Label loop = ig.DefineLabel ();
625 Label test = ig.DefineLabel ();
627 if (InitStatement != null && InitStatement != EmptyStatement.Value)
628 InitStatement.Emit (ec);
630 ec.LoopBegin = ig.DefineLabel ();
631 ec.LoopEnd = ig.DefineLabel ();
633 ig.Emit (OpCodes.Br, test);
637 ig.MarkLabel (ec.LoopBegin);
638 if (Increment != EmptyStatement.Value)
643 // If test is null, there is no test, and we are just
648 // The Resolve code already catches the case for
649 // Test == BoolConstant (false) so we know that
652 if (Test is BoolConstant)
653 ig.Emit (OpCodes.Br, loop);
655 Test.EmitBranchable (ec, loop, true);
658 ig.Emit (OpCodes.Br, loop);
659 ig.MarkLabel (ec.LoopEnd);
661 ec.LoopBegin = old_begin;
662 ec.LoopEnd = old_end;
665 protected override void CloneTo (CloneContext clonectx, Statement t)
667 For target = (For) t;
669 if (InitStatement != null)
670 target.InitStatement = InitStatement.Clone (clonectx);
672 target.Test = Test.Clone (clonectx);
673 if (Increment != null)
674 target.Increment = Increment.Clone (clonectx);
675 target.Statement = Statement.Clone (clonectx);
679 public class StatementExpression : Statement {
680 ExpressionStatement expr;
682 public StatementExpression (ExpressionStatement expr)
688 public override bool Resolve (EmitContext ec)
691 expr = expr.ResolveStatement (ec);
695 protected override void DoEmit (EmitContext ec)
697 expr.EmitStatement (ec);
700 public override string ToString ()
702 return "StatementExpression (" + expr + ")";
705 protected override void CloneTo (CloneContext clonectx, Statement t)
707 StatementExpression target = (StatementExpression) t;
709 target.expr = (ExpressionStatement) expr.Clone (clonectx);
714 /// Implements the return statement
716 public class Return : Statement {
720 public Return (Expression expr, Location l)
726 bool DoResolve (EmitContext ec)
729 if (ec.ReturnType == TypeManager.void_type)
732 Error (126, "An object of a type convertible to `{0}' is required " +
733 "for the return statement",
734 TypeManager.CSharpName (ec.ReturnType));
738 AnonymousContainer am = ec.CurrentAnonymousMethod;
739 if ((am != null) && am.IsIterator && ec.InIterator) {
740 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
741 "statement to return a value, or yield break to end the iteration");
744 if (am == null && ec.ReturnType == TypeManager.void_type) {
745 MemberCore mc = ec.ResolveContext as MemberCore;
746 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
747 mc.GetSignatureForError ());
750 Expr = Expr.Resolve (ec);
754 if (Expr.Type != ec.ReturnType) {
755 if (ec.InferReturnType) {
756 ec.ReturnType = Expr.Type;
758 Expr = Convert.ImplicitConversionRequired (
759 ec, Expr, ec.ReturnType, loc);
763 Report.Error (1662, loc,
764 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
765 am.ContainerType, am.GetSignatureForError ());
775 public override bool Resolve (EmitContext ec)
780 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
782 ec.NeedReturnLabel ();
783 ec.CurrentBranching.CurrentUsageVector.Goto ();
787 protected override void DoEmit (EmitContext ec)
793 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
797 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
799 ec.ig.Emit (OpCodes.Ret);
802 protected override void CloneTo (CloneContext clonectx, Statement t)
804 Return target = (Return) t;
805 // It's null for simple return;
807 target.Expr = Expr.Clone (clonectx);
811 public class Goto : Statement {
813 LabeledStatement label;
816 public override bool Resolve (EmitContext ec)
818 int errors = Report.Errors;
819 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
820 ec.CurrentBranching.CurrentUsageVector.Goto ();
821 return errors == Report.Errors;
824 public Goto (string label, Location l)
830 public string Target {
831 get { return target; }
834 public void SetResolvedTarget (LabeledStatement label)
837 label.AddReference ();
840 protected override void DoEmit (EmitContext ec)
843 throw new InternalErrorException ("goto emitted before target resolved");
844 Label l = label.LabelTarget (ec);
845 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
849 public class LabeledStatement : Statement {
856 FlowBranching.UsageVector vectors;
858 public LabeledStatement (string name, Location l)
864 public Label LabelTarget (EmitContext ec)
869 label = ec.ig.DefineLabel ();
879 public bool IsDefined {
880 get { return defined; }
883 public bool HasBeenReferenced {
884 get { return referenced; }
887 public FlowBranching.UsageVector JumpOrigins {
888 get { return vectors; }
891 public void AddUsageVector (FlowBranching.UsageVector vector)
893 vector = vector.Clone ();
894 vector.Next = vectors;
898 public override bool Resolve (EmitContext ec)
900 // this flow-branching will be terminated when the surrounding block ends
901 ec.StartFlowBranching (this);
905 protected override void DoEmit (EmitContext ec)
907 if (ig != null && ig != ec.ig)
908 throw new InternalErrorException ("cannot happen");
910 ec.ig.MarkLabel (label);
913 public void AddReference ()
921 /// `goto default' statement
923 public class GotoDefault : Statement {
925 public GotoDefault (Location l)
930 public override bool Resolve (EmitContext ec)
932 ec.CurrentBranching.CurrentUsageVector.Goto ();
936 protected override void DoEmit (EmitContext ec)
938 if (ec.Switch == null){
939 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
943 if (!ec.Switch.GotDefault){
944 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
947 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
952 /// `goto case' statement
954 public class GotoCase : Statement {
958 public GotoCase (Expression e, Location l)
964 public override bool Resolve (EmitContext ec)
966 if (ec.Switch == null){
967 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
971 expr = expr.Resolve (ec);
975 Constant c = expr as Constant;
977 Error (150, "A constant value is expected");
981 Type type = ec.Switch.SwitchType;
982 if (!Convert.ImplicitStandardConversionExists (c, type))
983 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
984 "convertible to type `{0}'", TypeManager.CSharpName (type));
987 object val = c.GetValue ();
988 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
989 val = TypeManager.ChangeType (val, type, out fail);
992 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
993 c.GetSignatureForError (), TypeManager.CSharpName (type));
998 val = SwitchLabel.NullStringCase;
1000 sl = (SwitchLabel) ec.Switch.Elements [val];
1003 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1004 (c.GetValue () == null ? "null" : val.ToString ()));
1008 ec.CurrentBranching.CurrentUsageVector.Goto ();
1012 protected override void DoEmit (EmitContext ec)
1014 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1017 protected override void CloneTo (CloneContext clonectx, Statement t)
1019 GotoCase target = (GotoCase) t;
1021 target.expr = expr.Clone (clonectx);
1022 target.sl = sl.Clone (clonectx);
1026 public class Throw : Statement {
1029 public Throw (Expression expr, Location l)
1035 public override bool Resolve (EmitContext ec)
1037 ec.CurrentBranching.CurrentUsageVector.Goto ();
1040 expr = expr.Resolve (ec);
1044 ExprClass eclass = expr.eclass;
1046 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1047 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1048 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1054 if ((t != TypeManager.exception_type) &&
1055 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1056 !(expr is NullLiteral)) {
1058 "The type caught or thrown must be derived " +
1059 "from System.Exception");
1066 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
1071 Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1077 protected override void DoEmit (EmitContext ec)
1080 ec.ig.Emit (OpCodes.Rethrow);
1084 ec.ig.Emit (OpCodes.Throw);
1088 protected override void CloneTo (CloneContext clonectx, Statement t)
1090 Throw target = (Throw) t;
1092 target.expr = expr.Clone (clonectx);
1096 public class Break : Statement {
1098 public Break (Location l)
1103 bool unwind_protect;
1105 public override bool Resolve (EmitContext ec)
1107 int errors = Report.Errors;
1108 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1109 ec.CurrentBranching.CurrentUsageVector.Goto ();
1110 return errors == Report.Errors;
1113 protected override void DoEmit (EmitContext ec)
1115 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1118 protected override void CloneTo (CloneContext clonectx, Statement t)
1124 public class Continue : Statement {
1126 public Continue (Location l)
1131 bool unwind_protect;
1133 public override bool Resolve (EmitContext ec)
1135 int errors = Report.Errors;
1136 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1137 ec.CurrentBranching.CurrentUsageVector.Goto ();
1138 return errors == Report.Errors;
1141 protected override void DoEmit (EmitContext ec)
1143 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1146 protected override void CloneTo (CloneContext clonectx, Statement t)
1152 public abstract class Variable
1154 public abstract Type Type {
1158 public abstract bool HasInstance {
1162 public abstract bool NeedsTemporary {
1166 public abstract void EmitInstance (EmitContext ec);
1168 public abstract void Emit (EmitContext ec);
1170 public abstract void EmitAssign (EmitContext ec);
1172 public abstract void EmitAddressOf (EmitContext ec);
1175 public interface IKnownVariable {
1176 Block Block { get; }
1177 Location Location { get; }
1181 // The information about a user-perceived local variable
1183 public class LocalInfo : IKnownVariable {
1184 public Expression Type;
1186 public Type VariableType;
1187 public readonly string Name;
1188 public readonly Location Location;
1189 public readonly Block Block;
1191 public VariableInfo VariableInfo;
1194 public Variable Variable {
1206 CompilerGenerated = 64,
1210 public enum ReadOnlyContext: byte {
1217 ReadOnlyContext ro_context;
1218 LocalBuilder builder;
1220 public LocalInfo (Expression type, string name, Block block, Location l)
1228 public LocalInfo (DeclSpace ds, Block block, Location l)
1230 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1235 public void ResolveVariable (EmitContext ec)
1237 Block theblock = Block;
1238 if (theblock.ScopeInfo != null)
1239 var = theblock.ScopeInfo.GetCapturedVariable (this);
1244 // This is needed to compile on both .NET 1.x and .NET 2.x
1245 // the later introduced `DeclareLocal (Type t, bool pinned)'
1247 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1249 builder = ec.ig.DeclareLocal (VariableType);
1251 var = new LocalVariable (this, builder);
1255 public void EmitSymbolInfo (EmitContext ec, string name)
1257 if (builder != null)
1258 ec.DefineLocalVariable (name, builder);
1261 public bool IsThisAssigned (EmitContext ec)
1263 if (VariableInfo == null)
1264 throw new Exception ();
1266 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1269 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1272 public bool IsAssigned (EmitContext ec)
1274 if (VariableInfo == null)
1275 throw new Exception ();
1277 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1280 public bool Resolve (EmitContext ec)
1282 if (VariableType == null) {
1283 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1287 VariableType = texpr.Type;
1290 if (TypeManager.IsGenericParameter (VariableType))
1293 if (VariableType == TypeManager.void_type) {
1294 Expression.Error_VoidInvalidInTheContext (Location);
1298 if (VariableType.IsAbstract && VariableType.IsSealed) {
1299 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1303 if (VariableType.IsPointer && !ec.InUnsafe)
1304 Expression.UnsafeError (Location);
1309 public bool IsCaptured {
1310 get { return (flags & Flags.Captured) != 0; }
1311 set { flags |= Flags.Captured; }
1314 public bool IsConstant {
1315 get { return (flags & Flags.IsConstant) != 0; }
1316 set { flags |= Flags.IsConstant; }
1319 public bool AddressTaken {
1320 get { return (flags & Flags.AddressTaken) != 0; }
1321 set { flags |= Flags.AddressTaken; }
1324 public bool CompilerGenerated {
1325 get { return (flags & Flags.CompilerGenerated) != 0; }
1326 set { flags |= Flags.CompilerGenerated; }
1329 public override string ToString ()
1331 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1332 Name, Type, VariableInfo, Location);
1336 get { return (flags & Flags.Used) != 0; }
1337 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1340 public bool ReadOnly {
1341 get { return (flags & Flags.ReadOnly) != 0; }
1344 public void SetReadOnlyContext (ReadOnlyContext context)
1346 flags |= Flags.ReadOnly;
1347 ro_context = context;
1350 public string GetReadOnlyContext ()
1353 throw new InternalErrorException ("Variable is not readonly");
1355 switch (ro_context) {
1356 case ReadOnlyContext.Fixed:
1357 return "fixed variable";
1358 case ReadOnlyContext.Foreach:
1359 return "foreach iteration variable";
1360 case ReadOnlyContext.Using:
1361 return "using variable";
1363 throw new NotImplementedException ();
1367 // Whether the variable is pinned, if Pinned the variable has been
1368 // allocated in a pinned slot with DeclareLocal.
1370 public bool Pinned {
1371 get { return (flags & Flags.Pinned) != 0; }
1372 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1375 public bool IsThis {
1376 get { return (flags & Flags.IsThis) != 0; }
1377 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1380 Block IKnownVariable.Block {
1381 get { return Block; }
1384 Location IKnownVariable.Location {
1385 get { return Location; }
1388 protected class LocalVariable : Variable
1390 public readonly LocalInfo LocalInfo;
1391 LocalBuilder builder;
1393 public LocalVariable (LocalInfo local, LocalBuilder builder)
1395 this.LocalInfo = local;
1396 this.builder = builder;
1399 public override Type Type {
1400 get { return LocalInfo.VariableType; }
1403 public override bool HasInstance {
1404 get { return false; }
1407 public override bool NeedsTemporary {
1408 get { return false; }
1411 public override void EmitInstance (EmitContext ec)
1416 public override void Emit (EmitContext ec)
1418 ec.ig.Emit (OpCodes.Ldloc, builder);
1421 public override void EmitAssign (EmitContext ec)
1423 ec.ig.Emit (OpCodes.Stloc, builder);
1426 public override void EmitAddressOf (EmitContext ec)
1428 ec.ig.Emit (OpCodes.Ldloca, builder);
1432 public LocalInfo Clone (CloneContext clonectx)
1435 // Variables in anonymous block are not resolved yet
1437 if (VariableType == null)
1438 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1441 // Variables in method block are resolved
1443 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1444 li.VariableType = VariableType;
1450 /// Block represents a C# block.
1454 /// This class is used in a number of places: either to represent
1455 /// explicit blocks that the programmer places or implicit blocks.
1457 /// Implicit blocks are used as labels or to introduce variable
1460 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1461 /// they contain extra information that is not necessary on normal blocks.
1463 public class Block : Statement {
1464 public Block Parent;
1465 public readonly Location StartLocation;
1466 public Location EndLocation = Location.Null;
1468 public ExplicitBlock Explicit;
1469 public ToplevelBlock Toplevel;
1472 public enum Flags : byte {
1475 VariablesInitialized = 4,
1479 HasVarargs = 64, // Used in ToplevelBlock
1482 protected Flags flags;
1484 public bool Unchecked {
1485 get { return (flags & Flags.Unchecked) != 0; }
1486 set { flags |= Flags.Unchecked; }
1489 public bool Unsafe {
1490 get { return (flags & Flags.Unsafe) != 0; }
1491 set { flags |= Flags.Unsafe; }
1495 // The statements in this block
1497 protected ArrayList statements;
1501 // An array of Blocks. We keep track of children just
1502 // to generate the local variable declarations.
1504 // Statements and child statements are handled through the
1510 // Labels. (label, block) pairs.
1515 // Keeps track of (name, type) pairs
1517 IDictionary variables;
1520 // Keeps track of constants
1521 Hashtable constants;
1524 // Temporary variables.
1526 ArrayList temporary_variables;
1529 // If this is a switch section, the enclosing switch block.
1533 ExpressionStatement scope_init;
1535 ArrayList anonymous_children;
1537 protected static int id;
1541 int assignable_slots;
1542 protected ScopeInfo scope_info;
1543 bool unreachable_shown;
1546 public Block (Block parent)
1547 : this (parent, (Flags) 0, Location.Null, Location.Null)
1550 public Block (Block parent, Flags flags)
1551 : this (parent, flags, Location.Null, Location.Null)
1554 public Block (Block parent, Location start, Location end)
1555 : this (parent, (Flags) 0, start, end)
1558 public Block (Block parent, Flags flags, Location start, Location end)
1560 if (parent != null) {
1561 parent.AddChild (this);
1563 // the appropriate constructors will fixup these fields
1564 Toplevel = parent.Toplevel;
1565 Explicit = parent.Explicit;
1568 this.Parent = parent;
1570 this.StartLocation = start;
1571 this.EndLocation = end;
1574 statements = new ArrayList ();
1577 public Block CreateSwitchBlock (Location start)
1579 // FIXME: should this be implicit?
1580 Block new_block = new ExplicitBlock (this, start, start);
1581 new_block.switch_block = this;
1586 get { return this_id; }
1589 public IDictionary Variables {
1591 if (variables == null)
1592 variables = new ListDictionary ();
1597 void AddChild (Block b)
1599 if (children == null)
1600 children = new ArrayList ();
1605 public void SetEndLocation (Location loc)
1610 protected static void Error_158 (string name, Location loc)
1612 Report.Error (158, loc, "The label `{0}' shadows another label " +
1613 "by the same name in a contained scope", name);
1617 /// Adds a label to the current block.
1621 /// false if the name already exists in this block. true
1625 public bool AddLabel (LabeledStatement target)
1627 if (switch_block != null)
1628 return switch_block.AddLabel (target);
1630 string name = target.Name;
1633 while (cur != null) {
1634 LabeledStatement s = cur.DoLookupLabel (name);
1636 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1637 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1641 if (this == Explicit)
1647 while (cur != null) {
1648 if (cur.DoLookupLabel (name) != null) {
1649 Error_158 (name, target.loc);
1653 if (children != null) {
1654 foreach (Block b in children) {
1655 LabeledStatement s = b.DoLookupLabel (name);
1659 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1660 Error_158 (name, target.loc);
1668 Toplevel.CheckError158 (name, target.loc);
1671 labels = new Hashtable ();
1673 labels.Add (name, target);
1677 public LabeledStatement LookupLabel (string name)
1679 LabeledStatement s = DoLookupLabel (name);
1683 if (children == null)
1686 foreach (Block child in children) {
1687 if (Explicit != child.Explicit)
1690 s = child.LookupLabel (name);
1698 LabeledStatement DoLookupLabel (string name)
1700 if (switch_block != null)
1701 return switch_block.LookupLabel (name);
1704 if (labels.Contains (name))
1705 return ((LabeledStatement) labels [name]);
1710 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1713 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1714 while (kvi == null) {
1715 b = b.Explicit.Parent;
1718 kvi = b.Explicit.GetKnownVariable (name);
1724 // Is kvi.Block nested inside 'b'
1725 if (b.Explicit != kvi.Block.Explicit) {
1727 // If a variable by the same name it defined in a nested block of this
1728 // block, we violate the invariant meaning in a block.
1731 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1732 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1737 // It's ok if the definition is in a nested subblock of b, but not
1738 // nested inside this block -- a definition in a sibling block
1739 // should not affect us.
1745 // Block 'b' and kvi.Block are the same textual block.
1746 // However, different variables are extant.
1748 // Check if the variable is in scope in both blocks. We use
1749 // an indirect check that depends on AddVariable doing its
1750 // part in maintaining the invariant-meaning-in-block property.
1752 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1756 // Even though we detected the error when the name is used, we
1757 // treat it as if the variable declaration was in error.
1759 Report.SymbolRelatedToPreviousError (loc, name);
1760 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1764 public LocalInfo AddVariable (Expression type, string name, Location l)
1766 LocalInfo vi = GetLocalInfo (name);
1768 Report.SymbolRelatedToPreviousError (vi.Location, name);
1769 if (Explicit == vi.Block.Explicit)
1770 Error_AlreadyDeclared (l, name, null);
1772 Error_AlreadyDeclared (l, name, "parent");
1776 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1778 Report.SymbolRelatedToPreviousError (pi.Location, name);
1779 Error_AlreadyDeclared (loc, name,
1780 pi.Block == Toplevel ? "method argument" : "parent or current");
1784 if (Toplevel.GenericMethod != null) {
1785 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1786 if (tp.Name == name) {
1787 Report.SymbolRelatedToPreviousError (tp);
1788 Error_AlreadyDeclaredTypeParameter (loc, name);
1794 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1796 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1797 Error_AlreadyDeclared (l, name, "child");
1801 vi = new LocalInfo (type, name, this, l);
1804 if ((flags & Flags.VariablesInitialized) != 0)
1805 throw new InternalErrorException ("block has already been resolved");
1810 protected virtual void AddVariable (LocalInfo li)
1812 Variables.Add (li.Name, li);
1813 Explicit.AddKnownVariable (li.Name, li);
1816 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1818 if (reason == null) {
1819 Error_AlreadyDeclared (loc, var);
1823 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1824 "in this scope because it would give a different meaning " +
1825 "to `{0}', which is already used in a `{1}' scope " +
1826 "to denote something else", var, reason);
1829 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1831 Report.Error (128, loc,
1832 "A local variable named `{0}' is already defined in this scope", name);
1835 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1837 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1840 public bool AddConstant (Expression type, string name, Expression value, Location l)
1842 if (AddVariable (type, name, l) == null)
1845 if (constants == null)
1846 constants = new Hashtable ();
1848 constants.Add (name, value);
1850 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1855 static int next_temp_id = 0;
1857 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1859 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1861 if (temporary_variables == null)
1862 temporary_variables = new ArrayList ();
1864 int id = ++next_temp_id;
1865 string name = "$s_" + id.ToString ();
1867 LocalInfo li = new LocalInfo (te, name, this, loc);
1868 li.CompilerGenerated = true;
1869 temporary_variables.Add (li);
1873 public LocalInfo GetLocalInfo (string name)
1875 for (Block b = this; b != null; b = b.Parent) {
1876 if (b.variables != null) {
1877 LocalInfo ret = b.variables [name] as LocalInfo;
1885 public Expression GetVariableType (string name)
1887 LocalInfo vi = GetLocalInfo (name);
1888 return vi == null ? null : vi.Type;
1891 public Expression GetConstantExpression (string name)
1893 for (Block b = this; b != null; b = b.Parent) {
1894 if (b.constants != null) {
1895 Expression ret = b.constants [name] as Expression;
1903 public void AddStatement (Statement s)
1906 flags |= Flags.BlockUsed;
1910 get { return (flags & Flags.BlockUsed) != 0; }
1915 flags |= Flags.BlockUsed;
1918 public bool HasRet {
1919 get { return (flags & Flags.HasRet) != 0; }
1922 public bool IsDestructor {
1923 get { return (flags & Flags.IsDestructor) != 0; }
1926 public void SetDestructor ()
1928 flags |= Flags.IsDestructor;
1931 public int AssignableSlots {
1933 if ((flags & Flags.VariablesInitialized) == 0)
1934 throw new Exception ("Variables have not been initialized yet");
1935 return assignable_slots;
1939 public ScopeInfo ScopeInfo {
1940 get { return scope_info; }
1943 public ScopeInfo CreateScopeInfo ()
1945 if (scope_info == null)
1946 scope_info = ScopeInfo.CreateScope (this);
1951 public ArrayList AnonymousChildren {
1952 get { return anonymous_children; }
1955 public void AddAnonymousChild (ToplevelBlock b)
1957 if (anonymous_children == null)
1958 anonymous_children = new ArrayList ();
1960 anonymous_children.Add (b);
1963 void DoResolveConstants (EmitContext ec)
1965 if (constants == null)
1968 if (variables == null)
1969 throw new InternalErrorException ("cannot happen");
1971 foreach (DictionaryEntry de in variables) {
1972 string name = (string) de.Key;
1973 LocalInfo vi = (LocalInfo) de.Value;
1974 Type variable_type = vi.VariableType;
1976 if (variable_type == null)
1979 Expression cv = (Expression) constants [name];
1983 // Don't let 'const int Foo = Foo;' succeed.
1984 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1985 // which in turn causes the 'must be constant' error to be triggered.
1986 constants.Remove (name);
1988 if (!Const.IsConstantTypeValid (variable_type)) {
1989 Const.Error_InvalidConstantType (variable_type, loc);
1993 ec.CurrentBlock = this;
1995 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1996 e = cv.Resolve (ec);
2001 Constant ce = e as Constant;
2003 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2007 e = ce.ConvertImplicitly (variable_type);
2009 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2010 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2012 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2016 constants.Add (name, e);
2017 vi.IsConstant = true;
2021 protected void ResolveMeta (EmitContext ec, int offset)
2023 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2025 // If some parent block was unsafe, we remain unsafe even if this block
2026 // isn't explicitly marked as such.
2027 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2028 flags |= Flags.VariablesInitialized;
2030 if (variables != null) {
2031 foreach (LocalInfo li in variables.Values) {
2032 if (!li.Resolve (ec))
2034 li.VariableInfo = new VariableInfo (li, offset);
2035 offset += li.VariableInfo.Length;
2038 assignable_slots = offset;
2040 DoResolveConstants (ec);
2042 if (children == null)
2044 foreach (Block b in children)
2045 b.ResolveMeta (ec, offset);
2050 // Emits the local variable declarations for a block
2052 public virtual void EmitMeta (EmitContext ec)
2054 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2055 if (ScopeInfo != null) {
2056 scope_init = ScopeInfo.GetScopeInitializer (ec);
2057 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2061 if (variables != null){
2062 foreach (LocalInfo vi in variables.Values)
2063 vi.ResolveVariable (ec);
2066 if (temporary_variables != null) {
2067 foreach (LocalInfo vi in temporary_variables)
2068 vi.ResolveVariable (ec);
2071 if (children != null){
2072 foreach (Block b in children)
2077 void UsageWarning (FlowBranching.UsageVector vector)
2081 if ((variables != null) && (Report.WarningLevel >= 3)) {
2082 foreach (DictionaryEntry de in variables){
2083 LocalInfo vi = (LocalInfo) de.Value;
2088 name = (string) de.Key;
2090 // vi.VariableInfo can be null for 'catch' variables
2091 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2092 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2094 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2100 private void CheckPossibleMistakenEmptyStatement (Statement s)
2104 // Some statements are wrapped by a Block. Since
2105 // others' internal could be changed, here I treat
2106 // them as possibly wrapped by Block equally.
2107 Block b = s as Block;
2108 if (b != null && b.statements.Count == 1)
2109 s = (Statement) b.statements [0];
2112 body = ((Lock) s).Statement;
2114 body = ((For) s).Statement;
2115 else if (s is Foreach)
2116 body = ((Foreach) s).Statement;
2117 else if (s is While)
2118 body = ((While) s).Statement;
2119 else if (s is Using)
2120 body = ((Using) s).Statement;
2121 else if (s is Fixed)
2122 body = ((Fixed) s).Statement;
2126 if (body == null || body is EmptyStatement)
2127 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2130 public override bool Resolve (EmitContext ec)
2132 Block prev_block = ec.CurrentBlock;
2135 int errors = Report.Errors;
2137 ec.CurrentBlock = this;
2138 ec.StartFlowBranching (this);
2140 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2143 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2144 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2145 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2146 // responsible for handling the situation.
2148 int statement_count = statements.Count;
2149 for (int ix = 0; ix < statement_count; ix++){
2150 Statement s = (Statement) statements [ix];
2151 // Check possible empty statement (CS0642)
2152 if (Report.WarningLevel >= 3 &&
2153 ix + 1 < statement_count &&
2154 statements [ix + 1] is Block)
2155 CheckPossibleMistakenEmptyStatement (s);
2158 // Warn if we detect unreachable code.
2161 if (s is EmptyStatement)
2165 ((Block) s).unreachable = true;
2167 if (!unreachable_shown && !(s is LabeledStatement)) {
2168 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2169 unreachable_shown = true;
2174 // Note that we're not using ResolveUnreachable() for unreachable
2175 // statements here. ResolveUnreachable() creates a temporary
2176 // flow branching and kills it afterwards. This leads to problems
2177 // if you have two unreachable statements where the first one
2178 // assigns a variable and the second one tries to access it.
2181 if (!s.Resolve (ec)) {
2182 if (ec.IsInProbingMode)
2186 statements [ix] = EmptyStatement.Value;
2190 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2191 statements [ix] = EmptyStatement.Value;
2193 num_statements = ix + 1;
2195 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2196 if (unreachable && s is LabeledStatement)
2197 throw new InternalErrorException ("should not happen");
2200 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2201 ec.CurrentBranching, statement_count, num_statements);
2206 while (ec.CurrentBranching is FlowBranchingLabeled)
2207 ec.EndFlowBranching ();
2209 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2211 ec.CurrentBlock = prev_block;
2213 // If we're a non-static `struct' constructor which doesn't have an
2214 // initializer, then we must initialize all of the struct's fields.
2215 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2218 if ((labels != null) && (Report.WarningLevel >= 2)) {
2219 foreach (LabeledStatement label in labels.Values)
2220 if (!label.HasBeenReferenced)
2221 Report.Warning (164, 2, label.loc,
2222 "This label has not been referenced");
2225 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2227 if (vector.IsUnreachable)
2228 flags |= Flags.HasRet;
2230 if (ok && (errors == Report.Errors)) {
2231 UsageWarning (vector);
2237 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2239 unreachable_shown = true;
2243 Report.Warning (162, 2, loc, "Unreachable code detected");
2245 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2246 bool ok = Resolve (ec);
2247 ec.KillFlowBranching ();
2252 protected override void DoEmit (EmitContext ec)
2254 for (int ix = 0; ix < num_statements; ix++){
2255 Statement s = (Statement) statements [ix];
2260 public override void Emit (EmitContext ec)
2262 Block prev_block = ec.CurrentBlock;
2264 ec.CurrentBlock = this;
2266 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2267 bool is_lexical_block = this == Explicit && Parent != null;
2269 if (emit_debug_info) {
2270 if (is_lexical_block)
2273 ec.Mark (StartLocation, true);
2274 if (scope_init != null)
2275 scope_init.EmitStatement (ec);
2277 ec.Mark (EndLocation, true);
2279 if (emit_debug_info) {
2280 if (is_lexical_block)
2283 if (variables != null) {
2284 foreach (DictionaryEntry de in variables) {
2285 string name = (string) de.Key;
2286 LocalInfo vi = (LocalInfo) de.Value;
2288 vi.EmitSymbolInfo (ec, name);
2293 ec.CurrentBlock = prev_block;
2296 public override string ToString ()
2298 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2301 protected override void CloneTo (CloneContext clonectx, Statement t)
2303 Block target = (Block) t;
2305 clonectx.AddBlockMap (this, target);
2307 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2308 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2310 target.Parent = clonectx.RemapBlockCopy (Parent);
2312 if (variables != null){
2313 target.variables = new Hashtable ();
2315 foreach (DictionaryEntry de in variables){
2316 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2317 target.variables [de.Key] = newlocal;
2318 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2322 target.statements = new ArrayList (statements.Count);
2323 foreach (Statement s in statements)
2324 target.statements.Add (s.Clone (clonectx));
2326 if (target.children != null){
2327 target.children = new ArrayList (children.Count);
2328 foreach (Block b in children){
2329 target.children.Add (clonectx.LookupBlock (b));
2334 // TODO: labels, switch_block, constants (?), anonymous_children
2339 public class ExplicitBlock : Block {
2340 public ExplicitBlock (Block parent, Location start, Location end)
2341 : this (parent, (Flags) 0, start, end)
2345 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2346 : base (parent, flags, start, end)
2348 this.Explicit = this;
2351 Hashtable known_variables;
2354 // Marks a variable with name @name as being used in this or a child block.
2355 // If a variable name has been used in a child block, it's illegal to
2356 // declare a variable with the same name in the current block.
2358 internal void AddKnownVariable (string name, IKnownVariable info)
2360 if (known_variables == null)
2361 known_variables = new Hashtable ();
2363 known_variables [name] = info;
2366 Parent.Explicit.AddKnownVariable (name, info);
2369 internal IKnownVariable GetKnownVariable (string name)
2371 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2374 protected override void CloneTo (CloneContext clonectx, Statement t)
2376 ExplicitBlock target = (ExplicitBlock) t;
2377 target.known_variables = null;
2378 base.CloneTo (clonectx, t);
2382 public class ToplevelParameterInfo : IKnownVariable {
2383 public readonly ToplevelBlock Block;
2384 public readonly int Index;
2385 public VariableInfo VariableInfo;
2387 Block IKnownVariable.Block {
2388 get { return Block; }
2390 public Parameter Parameter {
2391 get { return Block.Parameters [Index]; }
2393 public Location Location {
2394 get { return Parameter.Location; }
2397 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2405 // A toplevel block contains extra information, the split is done
2406 // only to separate information that would otherwise bloat the more
2407 // lightweight Block.
2409 // In particular, this was introduced when the support for Anonymous
2410 // Methods was implemented.
2412 public class ToplevelBlock : ExplicitBlock {
2413 GenericMethod generic;
2414 FlowBranchingToplevel top_level_branching;
2415 AnonymousContainer anonymous_container;
2416 RootScopeInfo root_scope;
2417 Parameters parameters;
2418 ToplevelParameterInfo[] parameter_info;
2420 public bool HasVarargs {
2421 get { return (flags & Flags.HasVarargs) != 0; }
2422 set { flags |= Flags.HasVarargs; }
2425 public bool IsIterator {
2426 get { return (flags & Flags.IsIterator) != 0; }
2430 // The parameters for the block.
2432 public Parameters Parameters {
2433 get { return parameters; }
2436 public bool CompleteContexts (EmitContext ec)
2438 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2440 if (root_scope != null)
2441 root_scope.LinkScopes ();
2443 if (Parent == null && root_scope != null) {
2444 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2446 if (root_scope.DefineType () == null)
2448 if (!root_scope.ResolveType ())
2450 if (!root_scope.ResolveMembers ())
2452 if (!root_scope.DefineMembers ())
2459 public GenericMethod GenericMethod {
2460 get { return generic; }
2463 public ToplevelBlock Container {
2464 get { return Parent == null ? null : Parent.Toplevel; }
2467 public AnonymousContainer AnonymousContainer {
2468 get { return anonymous_container; }
2469 set { anonymous_container = value; }
2472 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2473 this (parent, (Flags) 0, parameters, start)
2477 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2478 this (parent, parameters, start)
2480 this.generic = generic;
2483 public ToplevelBlock (Parameters parameters, Location start) :
2484 this (null, (Flags) 0, parameters, start)
2488 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2489 this (null, flags, parameters, start)
2493 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2494 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2495 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2496 base (null, flags, start, Location.Null)
2498 this.Toplevel = this;
2500 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2501 this.Parent = parent;
2503 parent.AddAnonymousChild (this);
2505 if (this.parameters.Count != 0)
2506 ProcessParameters ();
2509 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2513 protected override void CloneTo (CloneContext clonectx, Statement t)
2515 ToplevelBlock target = (ToplevelBlock) t;
2516 base.CloneTo (clonectx, t);
2518 if (parameters.Count != 0)
2519 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2520 for (int i = 0; i < parameters.Count; ++i)
2521 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2524 public bool CheckError158 (string name, Location loc)
2526 if (AnonymousChildren != null) {
2527 foreach (ToplevelBlock child in AnonymousChildren) {
2528 if (!child.CheckError158 (name, loc))
2533 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2534 if (!c.DoCheckError158 (name, loc))
2541 public virtual Expression GetTransparentIdentifier (string name)
2546 void ProcessParameters ()
2548 int n = parameters.Count;
2549 parameter_info = new ToplevelParameterInfo [n];
2550 for (int i = 0; i < n; ++i) {
2551 parameter_info [i] = new ToplevelParameterInfo (this, i);
2553 Parameter p = parameters [i];
2557 string name = p.Name;
2558 LocalInfo vi = GetLocalInfo (name);
2560 Report.SymbolRelatedToPreviousError (vi.Location, name);
2561 Error_AlreadyDeclared (loc, name, "parent or current");
2565 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2567 Report.SymbolRelatedToPreviousError (pi.Location, name);
2568 Error_AlreadyDeclared (loc, name, "parent or current");
2572 AddKnownVariable (name, parameter_info [i]);
2575 // mark this block as "used" so that we create local declarations in a sub-block
2576 // FIXME: This appears to uncover a lot of bugs
2580 bool DoCheckError158 (string name, Location loc)
2582 LabeledStatement s = LookupLabel (name);
2584 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2585 Error_158 (name, loc);
2592 public RootScopeInfo CreateRootScope (TypeContainer host)
2594 if (root_scope != null)
2597 if (Container == null)
2598 root_scope = new RootScopeInfo (
2599 this, host, generic, StartLocation);
2601 if (scope_info != null)
2602 throw new InternalErrorException ();
2604 scope_info = root_scope;
2608 public void CreateIteratorHost (RootScopeInfo root)
2610 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2612 if (Parent != null || root_scope != null)
2613 throw new InternalErrorException ();
2615 scope_info = root_scope = root;
2618 public RootScopeInfo RootScope {
2620 if (root_scope != null)
2622 else if (Container != null)
2623 return Container.RootScope;
2629 public FlowBranchingToplevel TopLevelBranching {
2630 get { return top_level_branching; }
2634 // This is used if anonymous methods are used inside an iterator
2635 // (see 2test-22.cs for an example).
2637 // The AnonymousMethod is created while parsing - at a time when we don't
2638 // know yet that we're inside an iterator, so it's `Container' is initially
2639 // null. Later on, when resolving the iterator, we need to move the
2640 // anonymous method into that iterator.
2642 public void ReParent (ToplevelBlock new_parent)
2644 if ((flags & Flags.VariablesInitialized) != 0)
2645 throw new InternalErrorException ("block has already been resolved");
2647 Parent = new_parent;
2651 // Returns a `ParameterReference' for the given name, or null if there
2652 // is no such parameter
2654 public ParameterReference GetParameterReference (string name, Location loc)
2656 ToplevelParameterInfo p = GetParameterInfo (name);
2657 return p == null ? null : new ParameterReference (this, p, loc);
2660 public ToplevelParameterInfo GetParameterInfo (string name)
2663 for (ToplevelBlock t = this; t != null; t = t.Container) {
2664 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2666 return t.parameter_info [idx];
2671 LocalInfo this_variable = null;
2674 // Returns the "this" instance variable of this block.
2675 // See AddThisVariable() for more information.
2677 public LocalInfo ThisVariable {
2678 get { return this_variable; }
2683 // This is used by non-static `struct' constructors which do not have an
2684 // initializer - in this case, the constructor must initialize all of the
2685 // struct's fields. To do this, we add a "this" variable and use the flow
2686 // analysis code to ensure that it's been fully initialized before control
2687 // leaves the constructor.
2689 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2691 if (this_variable == null) {
2692 this_variable = new LocalInfo (ds, this, l);
2693 this_variable.Used = true;
2694 this_variable.IsThis = true;
2696 Variables.Add ("this", this_variable);
2699 return this_variable;
2702 public bool IsThisAssigned (EmitContext ec)
2704 return this_variable == null || this_variable.IsThisAssigned (ec);
2707 public bool ResolveMeta (EmitContext ec, Parameters ip)
2709 int errors = Report.Errors;
2710 int orig_count = parameters.Count;
2712 if (top_level_branching != null)
2718 // Assert: orig_count != parameter.Count => orig_count == 0
2719 if (orig_count != 0 && orig_count != parameters.Count)
2720 throw new InternalErrorException ("parameter information mismatch");
2722 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2724 for (int i = 0; i < orig_count; ++i) {
2725 Parameter.Modifier mod = parameters.ParameterModifier (i);
2727 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2730 VariableInfo vi = new VariableInfo (ip, i, offset);
2731 parameter_info [i].VariableInfo = vi;
2732 offset += vi.Length;
2735 ResolveMeta (ec, offset);
2737 top_level_branching = ec.StartFlowBranching (this);
2739 return Report.Errors == errors;
2743 // Check whether all `out' parameters have been assigned.
2745 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2747 if (vector.IsUnreachable)
2750 int n = parameter_info == null ? 0 : parameter_info.Length;
2752 for (int i = 0; i < n; i++) {
2753 VariableInfo var = parameter_info [i].VariableInfo;
2758 if (vector.IsAssigned (var, false))
2761 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2766 public override void EmitMeta (EmitContext ec)
2769 parameters.ResolveVariable (this);
2772 public void MakeIterator (Iterator iterator)
2774 flags |= Flags.IsIterator;
2776 Block block = new ExplicitBlock (this, StartLocation, EndLocation);
2777 foreach (Statement stmt in statements)
2778 block.AddStatement (stmt);
2779 statements.Clear ();
2780 statements.Add (new MoveNextStatement (iterator, block));
2783 protected class MoveNextStatement : Statement {
2787 public MoveNextStatement (Iterator iterator, Block block)
2789 this.iterator = iterator;
2791 this.loc = iterator.Location;
2794 public override bool Resolve (EmitContext ec)
2796 return block.Resolve (ec);
2799 protected override void DoEmit (EmitContext ec)
2801 iterator.EmitMoveNext (ec, block);
2805 public override string ToString ()
2807 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2808 root_scope, anonymous_container != null ?
2809 anonymous_container.Scope : null);
2813 public class SwitchLabel {
2820 Label il_label_code;
2821 bool il_label_code_set;
2823 public static readonly object NullStringCase = new object ();
2826 // if expr == null, then it is the default case.
2828 public SwitchLabel (Expression expr, Location l)
2834 public Expression Label {
2840 public object Converted {
2846 public Label GetILLabel (EmitContext ec)
2849 il_label = ec.ig.DefineLabel ();
2850 il_label_set = true;
2855 public Label GetILLabelCode (EmitContext ec)
2857 if (!il_label_code_set){
2858 il_label_code = ec.ig.DefineLabel ();
2859 il_label_code_set = true;
2861 return il_label_code;
2865 // Resolves the expression, reduces it to a literal if possible
2866 // and then converts it to the requested type.
2868 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2870 Expression e = label.Resolve (ec);
2875 Constant c = e as Constant;
2877 Report.Error (150, loc, "A constant value is expected");
2881 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2882 converted = NullStringCase;
2886 if (allow_nullable && c.GetValue () == null) {
2887 converted = NullStringCase;
2891 c = c.ImplicitConversionRequired (required_type, loc);
2895 converted = c.GetValue ();
2899 public void Erorr_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2902 if (converted == null)
2904 else if (converted == NullStringCase)
2906 else if (TypeManager.IsEnumType (switch_type))
2907 label = TypeManager.CSharpEnumValue (switch_type, converted);
2909 label = converted.ToString ();
2911 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2912 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2915 public SwitchLabel Clone (CloneContext clonectx)
2917 return new SwitchLabel (label.Clone (clonectx), loc);
2921 public class SwitchSection {
2922 // An array of SwitchLabels.
2923 public readonly ArrayList Labels;
2924 public readonly Block Block;
2926 public SwitchSection (ArrayList labels, Block block)
2932 public SwitchSection Clone (CloneContext clonectx)
2934 ArrayList cloned_labels = new ArrayList ();
2936 foreach (SwitchLabel sl in cloned_labels)
2937 cloned_labels.Add (sl.Clone (clonectx));
2939 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
2943 public class Switch : Statement {
2944 public ArrayList Sections;
2945 public Expression Expr;
2948 /// Maps constants whose type type SwitchType to their SwitchLabels.
2950 public IDictionary Elements;
2953 /// The governing switch type
2955 public Type SwitchType;
2960 Label default_target;
2962 Expression new_expr;
2964 SwitchSection constant_section;
2965 SwitchSection default_section;
2969 // Nullable Types support for GMCS.
2971 Nullable.Unwrap unwrap;
2973 protected bool HaveUnwrap {
2974 get { return unwrap != null; }
2977 protected bool HaveUnwrap {
2978 get { return false; }
2983 // The types allowed to be implicitly cast from
2984 // on the governing type
2986 static Type [] allowed_types;
2988 public Switch (Expression e, ArrayList sects, Location l)
2995 public bool GotDefault {
2997 return default_section != null;
3001 public Label DefaultTarget {
3003 return default_target;
3008 // Determines the governing type for a switch. The returned
3009 // expression might be the expression from the switch, or an
3010 // expression that includes any potential conversions to the
3011 // integral types or to string.
3013 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3015 Type t = TypeManager.DropGenericTypeArguments (expr.Type);
3017 if (t == TypeManager.byte_type ||
3018 t == TypeManager.sbyte_type ||
3019 t == TypeManager.ushort_type ||
3020 t == TypeManager.short_type ||
3021 t == TypeManager.uint32_type ||
3022 t == TypeManager.int32_type ||
3023 t == TypeManager.uint64_type ||
3024 t == TypeManager.int64_type ||
3025 t == TypeManager.char_type ||
3026 t == TypeManager.string_type ||
3027 t == TypeManager.bool_type ||
3028 t.IsSubclassOf (TypeManager.enum_type))
3031 if (allowed_types == null){
3032 allowed_types = new Type [] {
3033 TypeManager.sbyte_type,
3034 TypeManager.byte_type,
3035 TypeManager.short_type,
3036 TypeManager.ushort_type,
3037 TypeManager.int32_type,
3038 TypeManager.uint32_type,
3039 TypeManager.int64_type,
3040 TypeManager.uint64_type,
3041 TypeManager.char_type,
3042 TypeManager.string_type,
3043 TypeManager.bool_type
3048 // Try to find a *user* defined implicit conversion.
3050 // If there is no implicit conversion, or if there are multiple
3051 // conversions, we have to report an error
3053 Expression converted = null;
3054 foreach (Type tt in allowed_types){
3057 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3062 // Ignore over-worked ImplicitUserConversions that do
3063 // an implicit conversion in addition to the user conversion.
3065 if (!(e is UserCast))
3068 if (converted != null){
3069 Report.ExtraInformation (
3071 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3072 TypeManager.CSharpName (expr.Type)));
3082 // Performs the basic sanity checks on the switch statement
3083 // (looks for duplicate keys and non-constant expressions).
3085 // It also returns a hashtable with the keys that we will later
3086 // use to compute the switch tables
3088 bool CheckSwitch (EmitContext ec)
3091 Elements = Sections.Count > 10 ?
3092 (IDictionary)new Hashtable () :
3093 (IDictionary)new ListDictionary ();
3095 foreach (SwitchSection ss in Sections){
3096 foreach (SwitchLabel sl in ss.Labels){
3097 if (sl.Label == null){
3098 if (default_section != null){
3099 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3102 default_section = ss;
3106 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3111 object key = sl.Converted;
3113 Elements.Add (key, sl);
3114 } catch (ArgumentException) {
3115 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3123 void EmitObjectInteger (ILGenerator ig, object k)
3126 IntConstant.EmitInt (ig, (int) k);
3127 else if (k is Constant) {
3128 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3131 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3134 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3136 IntConstant.EmitInt (ig, (int) (long) k);
3137 ig.Emit (OpCodes.Conv_I8);
3140 LongConstant.EmitLong (ig, (long) k);
3142 else if (k is ulong)
3144 ulong ul = (ulong) k;
3147 IntConstant.EmitInt (ig, unchecked ((int) ul));
3148 ig.Emit (OpCodes.Conv_U8);
3152 LongConstant.EmitLong (ig, unchecked ((long) ul));
3156 IntConstant.EmitInt (ig, (int) ((char) k));
3157 else if (k is sbyte)
3158 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3160 IntConstant.EmitInt (ig, (int) ((byte) k));
3161 else if (k is short)
3162 IntConstant.EmitInt (ig, (int) ((short) k));
3163 else if (k is ushort)
3164 IntConstant.EmitInt (ig, (int) ((ushort) k));
3166 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3168 throw new Exception ("Unhandled case");
3171 // structure used to hold blocks of keys while calculating table switch
3172 class KeyBlock : IComparable
3174 public KeyBlock (long _first)
3176 first = last = _first;
3180 public ArrayList element_keys = null;
3181 // how many items are in the bucket
3182 public int Size = 1;
3185 get { return (int) (last - first + 1); }
3187 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3189 return kb_last.last - kb_first.first + 1;
3191 public int CompareTo (object obj)
3193 KeyBlock kb = (KeyBlock) obj;
3194 int nLength = Length;
3195 int nLengthOther = kb.Length;
3196 if (nLengthOther == nLength)
3197 return (int) (kb.first - first);
3198 return nLength - nLengthOther;
3203 /// This method emits code for a lookup-based switch statement (non-string)
3204 /// Basically it groups the cases into blocks that are at least half full,
3205 /// and then spits out individual lookup opcodes for each block.
3206 /// It emits the longest blocks first, and short blocks are just
3207 /// handled with direct compares.
3209 /// <param name="ec"></param>
3210 /// <param name="val"></param>
3211 /// <returns></returns>
3212 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3214 int element_count = Elements.Count;
3215 object [] element_keys = new object [element_count];
3216 Elements.Keys.CopyTo (element_keys, 0);
3217 Array.Sort (element_keys);
3219 // initialize the block list with one element per key
3220 ArrayList key_blocks = new ArrayList ();
3221 foreach (object key in element_keys)
3222 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3224 KeyBlock current_kb;
3225 // iteratively merge the blocks while they are at least half full
3226 // there's probably a really cool way to do this with a tree...
3227 while (key_blocks.Count > 1)
3229 ArrayList key_blocks_new = new ArrayList ();
3230 current_kb = (KeyBlock) key_blocks [0];
3231 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3233 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3234 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3237 current_kb.last = kb.last;
3238 current_kb.Size += kb.Size;
3242 // start a new block
3243 key_blocks_new.Add (current_kb);
3247 key_blocks_new.Add (current_kb);
3248 if (key_blocks.Count == key_blocks_new.Count)
3250 key_blocks = key_blocks_new;
3253 // initialize the key lists
3254 foreach (KeyBlock kb in key_blocks)
3255 kb.element_keys = new ArrayList ();
3257 // fill the key lists
3259 if (key_blocks.Count > 0) {
3260 current_kb = (KeyBlock) key_blocks [0];
3261 foreach (object key in element_keys)
3263 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3264 System.Convert.ToInt64 (key) > current_kb.last;
3266 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3267 current_kb.element_keys.Add (key);
3271 // sort the blocks so we can tackle the largest ones first
3274 // okay now we can start...
3275 ILGenerator ig = ec.ig;
3276 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3277 Label lbl_default = ig.DefineLabel ();
3279 Type type_keys = null;
3280 if (element_keys.Length > 0)
3281 type_keys = element_keys [0].GetType (); // used for conversions
3285 if (TypeManager.IsEnumType (SwitchType))
3286 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3288 compare_type = SwitchType;
3290 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3292 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3293 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3296 foreach (object key in kb.element_keys)
3298 ig.Emit (OpCodes.Ldloc, val);
3299 EmitObjectInteger (ig, key);
3300 SwitchLabel sl = (SwitchLabel) Elements [key];
3301 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3306 // TODO: if all the keys in the block are the same and there are
3307 // no gaps/defaults then just use a range-check.
3308 if (compare_type == TypeManager.int64_type ||
3309 compare_type == TypeManager.uint64_type)
3311 // TODO: optimize constant/I4 cases
3313 // check block range (could be > 2^31)
3314 ig.Emit (OpCodes.Ldloc, val);
3315 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3316 ig.Emit (OpCodes.Blt, lbl_default);
3317 ig.Emit (OpCodes.Ldloc, val);
3318 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3319 ig.Emit (OpCodes.Bgt, lbl_default);
3322 ig.Emit (OpCodes.Ldloc, val);
3325 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3326 ig.Emit (OpCodes.Sub);
3328 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3333 ig.Emit (OpCodes.Ldloc, val);
3334 int first = (int) kb.first;
3337 IntConstant.EmitInt (ig, first);
3338 ig.Emit (OpCodes.Sub);
3342 IntConstant.EmitInt (ig, -first);
3343 ig.Emit (OpCodes.Add);
3347 // first, build the list of labels for the switch
3349 int cJumps = kb.Length;
3350 Label [] switch_labels = new Label [cJumps];
3351 for (int iJump = 0; iJump < cJumps; iJump++)
3353 object key = kb.element_keys [iKey];
3354 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3356 SwitchLabel sl = (SwitchLabel) Elements [key];
3357 switch_labels [iJump] = sl.GetILLabel (ec);
3361 switch_labels [iJump] = lbl_default;
3363 // emit the switch opcode
3364 ig.Emit (OpCodes.Switch, switch_labels);
3367 // mark the default for this block
3369 ig.MarkLabel (lbl_default);
3372 // TODO: find the default case and emit it here,
3373 // to prevent having to do the following jump.
3374 // make sure to mark other labels in the default section
3376 // the last default just goes to the end
3377 ig.Emit (OpCodes.Br, lbl_default);
3379 // now emit the code for the sections
3380 bool found_default = false;
3381 bool found_null = false;
3382 foreach (SwitchSection ss in Sections)
3384 foreach (SwitchLabel sl in ss.Labels)
3385 if (sl.Converted == SwitchLabel.NullStringCase)
3389 foreach (SwitchSection ss in Sections)
3391 foreach (SwitchLabel sl in ss.Labels)
3393 ig.MarkLabel (sl.GetILLabel (ec));
3394 ig.MarkLabel (sl.GetILLabelCode (ec));
3395 if (sl.Converted == SwitchLabel.NullStringCase)
3396 ig.MarkLabel (null_target);
3397 else if (sl.Label == null) {
3398 ig.MarkLabel (lbl_default);
3399 found_default = true;
3401 ig.MarkLabel (null_target);
3407 if (!found_default) {
3408 ig.MarkLabel (lbl_default);
3409 if (HaveUnwrap && !found_null) {
3410 ig.MarkLabel (null_target);
3414 ig.MarkLabel (lbl_end);
3417 // This simple emit switch works, but does not take advantage of the
3419 // TODO: remove non-string logic from here
3420 // TODO: binary search strings?
3422 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3424 ILGenerator ig = ec.ig;
3425 Label end_of_switch = ig.DefineLabel ();
3426 Label next_test = ig.DefineLabel ();
3427 bool first_test = true;
3428 bool pending_goto_end = false;
3429 bool null_marked = false;
3431 int section_count = Sections.Count;
3433 // TODO: implement switch optimization for string by using Hashtable
3434 //if (SwitchType == TypeManager.string_type && section_count > 7)
3435 // Console.WriteLine ("Switch optimization possible " + loc);
3437 ig.Emit (OpCodes.Ldloc, val);
3439 if (Elements.Contains (SwitchLabel.NullStringCase)){
3440 ig.Emit (OpCodes.Brfalse, null_target);
3442 ig.Emit (OpCodes.Brfalse, default_target);
3444 ig.Emit (OpCodes.Ldloc, val);
3445 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3446 ig.Emit (OpCodes.Stloc, val);
3448 for (int section = 0; section < section_count; section++){
3449 SwitchSection ss = (SwitchSection) Sections [section];
3451 if (ss == default_section)
3454 Label sec_begin = ig.DefineLabel ();
3456 ig.Emit (OpCodes.Nop);
3458 if (pending_goto_end)
3459 ig.Emit (OpCodes.Br, end_of_switch);
3461 int label_count = ss.Labels.Count;
3463 for (int label = 0; label < label_count; label++){
3464 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3465 ig.MarkLabel (sl.GetILLabel (ec));
3468 ig.MarkLabel (next_test);
3469 next_test = ig.DefineLabel ();
3472 // If we are the default target
3474 if (sl.Label != null){
3475 object lit = sl.Converted;
3477 if (lit == SwitchLabel.NullStringCase){
3479 if (label + 1 == label_count)
3480 ig.Emit (OpCodes.Br, next_test);
3484 ig.Emit (OpCodes.Ldloc, val);
3485 ig.Emit (OpCodes.Ldstr, (string)lit);
3486 if (label_count == 1)
3487 ig.Emit (OpCodes.Bne_Un, next_test);
3489 if (label+1 == label_count)
3490 ig.Emit (OpCodes.Bne_Un, next_test);
3492 ig.Emit (OpCodes.Beq, sec_begin);
3497 ig.MarkLabel (null_target);
3500 ig.MarkLabel (sec_begin);
3501 foreach (SwitchLabel sl in ss.Labels)
3502 ig.MarkLabel (sl.GetILLabelCode (ec));
3505 pending_goto_end = !ss.Block.HasRet;
3508 ig.MarkLabel (next_test);
3509 ig.MarkLabel (default_target);
3511 ig.MarkLabel (null_target);
3512 if (default_section != null)
3513 default_section.Block.Emit (ec);
3514 ig.MarkLabel (end_of_switch);
3517 SwitchSection FindSection (SwitchLabel label)
3519 foreach (SwitchSection ss in Sections){
3520 foreach (SwitchLabel sl in ss.Labels){
3529 public override bool Resolve (EmitContext ec)
3531 Expr = Expr.Resolve (ec);
3535 new_expr = SwitchGoverningType (ec, Expr);
3538 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3539 unwrap = Nullable.Unwrap.Create (Expr, ec);
3543 new_expr = SwitchGoverningType (ec, unwrap);
3547 if (new_expr == null){
3548 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3553 SwitchType = new_expr.Type;
3555 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3556 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3560 if (!CheckSwitch (ec))
3564 Elements.Remove (SwitchLabel.NullStringCase);
3566 Switch old_switch = ec.Switch;
3568 ec.Switch.SwitchType = SwitchType;
3570 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3571 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3573 is_constant = new_expr is Constant;
3575 object key = ((Constant) new_expr).GetValue ();
3576 SwitchLabel label = (SwitchLabel) Elements [key];
3578 constant_section = FindSection (label);
3579 if (constant_section == null)
3580 constant_section = default_section;
3584 foreach (SwitchSection ss in Sections){
3586 ec.CurrentBranching.CreateSibling (
3587 null, FlowBranching.SiblingType.SwitchSection);
3591 if (is_constant && (ss != constant_section)) {
3592 // If we're a constant switch, we're only emitting
3593 // one single section - mark all the others as
3595 ec.CurrentBranching.CurrentUsageVector.Goto ();
3596 if (!ss.Block.ResolveUnreachable (ec, true))
3599 if (!ss.Block.Resolve (ec))
3604 if (default_section == null)
3605 ec.CurrentBranching.CreateSibling (
3606 null, FlowBranching.SiblingType.SwitchSection);
3608 ec.EndFlowBranching ();
3609 ec.Switch = old_switch;
3611 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3616 protected override void DoEmit (EmitContext ec)
3618 ILGenerator ig = ec.ig;
3620 default_target = ig.DefineLabel ();
3621 null_target = ig.DefineLabel ();
3623 // Store variable for comparission purposes
3626 value = ig.DeclareLocal (SwitchType);
3628 unwrap.EmitCheck (ec);
3629 ig.Emit (OpCodes.Brfalse, null_target);
3631 ig.Emit (OpCodes.Stloc, value);
3633 } else if (!is_constant) {
3634 value = ig.DeclareLocal (SwitchType);
3636 ig.Emit (OpCodes.Stloc, value);
3641 // Setup the codegen context
3643 Label old_end = ec.LoopEnd;
3644 Switch old_switch = ec.Switch;
3646 ec.LoopEnd = ig.DefineLabel ();
3651 if (constant_section != null)
3652 constant_section.Block.Emit (ec);
3653 } else if (SwitchType == TypeManager.string_type)
3654 SimpleSwitchEmit (ec, value);
3656 TableSwitchEmit (ec, value);
3658 // Restore context state.
3659 ig.MarkLabel (ec.LoopEnd);
3662 // Restore the previous context
3664 ec.LoopEnd = old_end;
3665 ec.Switch = old_switch;
3668 protected override void CloneTo (CloneContext clonectx, Statement t)
3670 Switch target = (Switch) t;
3672 target.Expr = Expr.Clone (clonectx);
3673 target.Sections = new ArrayList ();
3674 foreach (SwitchSection ss in Sections){
3675 target.Sections.Add (ss.Clone (clonectx));
3680 public abstract class ExceptionStatement : Statement
3682 public abstract void EmitFinally (EmitContext ec);
3684 protected bool emit_finally = true;
3685 ArrayList parent_vectors;
3687 protected void DoEmitFinally (EmitContext ec)
3690 ec.ig.BeginFinallyBlock ();
3691 else if (ec.InIterator)
3692 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3696 protected void ResolveFinally (FlowBranchingException branching)
3698 emit_finally = branching.EmitFinally;
3700 branching.Parent.StealFinallyClauses (ref parent_vectors);
3704 public class Lock : ExceptionStatement {
3706 public Statement Statement;
3707 TemporaryVariable temp;
3709 public Lock (Expression expr, Statement stmt, Location l)
3716 public override bool Resolve (EmitContext ec)
3718 expr = expr.Resolve (ec);
3722 if (expr.Type.IsValueType){
3723 Report.Error (185, loc,
3724 "`{0}' is not a reference type as required by the lock statement",
3725 TypeManager.CSharpName (expr.Type));
3729 FlowBranchingException branching = ec.StartFlowBranching (this);
3730 bool ok = Statement.Resolve (ec);
3732 ResolveFinally (branching);
3734 ec.EndFlowBranching ();
3736 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3737 // So, ensure there's some IL code after the finally block.
3738 ec.NeedReturnLabel ();
3740 // Avoid creating libraries that reference the internal
3743 if (t == TypeManager.null_type)
3744 t = TypeManager.object_type;
3746 temp = new TemporaryVariable (t, loc);
3752 protected override void DoEmit (EmitContext ec)
3754 ILGenerator ig = ec.ig;
3756 temp.Store (ec, expr);
3758 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3762 ig.BeginExceptionBlock ();
3763 Statement.Emit (ec);
3768 ig.EndExceptionBlock ();
3771 public override void EmitFinally (EmitContext ec)
3774 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3777 protected override void CloneTo (CloneContext clonectx, Statement t)
3779 Lock target = (Lock) t;
3781 target.expr = expr.Clone (clonectx);
3782 target.Statement = Statement.Clone (clonectx);
3786 public class Unchecked : Statement {
3789 public Unchecked (Block b)
3795 public override bool Resolve (EmitContext ec)
3797 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3798 return Block.Resolve (ec);
3801 protected override void DoEmit (EmitContext ec)
3803 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3807 protected override void CloneTo (CloneContext clonectx, Statement t)
3809 Unchecked target = (Unchecked) t;
3811 target.Block = clonectx.LookupBlock (Block);
3815 public class Checked : Statement {
3818 public Checked (Block b)
3821 b.Unchecked = false;
3824 public override bool Resolve (EmitContext ec)
3826 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3827 return Block.Resolve (ec);
3830 protected override void DoEmit (EmitContext ec)
3832 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3836 protected override void CloneTo (CloneContext clonectx, Statement t)
3838 Checked target = (Checked) t;
3840 target.Block = clonectx.LookupBlock (Block);
3844 public class Unsafe : Statement {
3847 public Unsafe (Block b)
3850 Block.Unsafe = true;
3853 public override bool Resolve (EmitContext ec)
3855 using (ec.With (EmitContext.Flags.InUnsafe, true))
3856 return Block.Resolve (ec);
3859 protected override void DoEmit (EmitContext ec)
3861 using (ec.With (EmitContext.Flags.InUnsafe, true))
3864 protected override void CloneTo (CloneContext clonectx, Statement t)
3866 Unsafe target = (Unsafe) t;
3868 target.Block = clonectx.LookupBlock (Block);
3875 public class Fixed : Statement {
3877 ArrayList declarators;
3878 Statement statement;
3883 abstract class Emitter
3885 protected LocalInfo vi;
3886 protected Expression converted;
3888 protected Emitter (Expression expr, LocalInfo li)
3894 public abstract void Emit (EmitContext ec);
3895 public abstract void EmitExit (EmitContext ec);
3898 class ExpressionEmitter : Emitter {
3899 public ExpressionEmitter (Expression converted, LocalInfo li) :
3900 base (converted, li)
3904 public override void Emit (EmitContext ec) {
3906 // Store pointer in pinned location
3908 converted.Emit (ec);
3909 vi.Variable.EmitAssign (ec);
3912 public override void EmitExit (EmitContext ec)
3914 ec.ig.Emit (OpCodes.Ldc_I4_0);
3915 ec.ig.Emit (OpCodes.Conv_U);
3916 vi.Variable.EmitAssign (ec);
3920 class StringEmitter : Emitter {
3921 LocalBuilder pinned_string;
3924 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3930 public override void Emit (EmitContext ec)
3932 ILGenerator ig = ec.ig;
3933 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3935 converted.Emit (ec);
3936 ig.Emit (OpCodes.Stloc, pinned_string);
3938 Expression sptr = new StringPtr (pinned_string, loc);
3939 converted = Convert.ImplicitConversionRequired (
3940 ec, sptr, vi.VariableType, loc);
3942 if (converted == null)
3945 converted.Emit (ec);
3946 vi.Variable.EmitAssign (ec);
3949 public override void EmitExit (EmitContext ec)
3951 ec.ig.Emit (OpCodes.Ldnull);
3952 ec.ig.Emit (OpCodes.Stloc, pinned_string);
3956 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3959 declarators = decls;
3964 public Statement Statement {
3965 get { return statement; }
3968 public override bool Resolve (EmitContext ec)
3971 Expression.UnsafeError (loc);
3975 TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
3979 expr_type = texpr.Type;
3981 data = new Emitter [declarators.Count];
3983 if (!expr_type.IsPointer){
3984 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
3989 foreach (Pair p in declarators){
3990 LocalInfo vi = (LocalInfo) p.First;
3991 Expression e = (Expression) p.Second;
3993 vi.VariableInfo.SetAssigned (ec);
3994 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
3997 // The rules for the possible declarators are pretty wise,
3998 // but the production on the grammar is more concise.
4000 // So we have to enforce these rules here.
4002 // We do not resolve before doing the case 1 test,
4003 // because the grammar is explicit in that the token &
4004 // is present, so we need to test for this particular case.
4008 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4013 // Case 1: & object.
4015 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4016 Expression child = ((Unary) e).Expr;
4018 if (child is ParameterReference || child is LocalVariableReference){
4021 "No need to use fixed statement for parameters or " +
4022 "local variable declarations (address is already " +
4027 ec.InFixedInitializer = true;
4029 ec.InFixedInitializer = false;
4033 child = ((Unary) e).Expr;
4035 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4038 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4039 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4043 data [i] = new ExpressionEmitter (e, vi);
4049 ec.InFixedInitializer = true;
4051 ec.InFixedInitializer = false;
4058 if (e.Type.IsArray){
4059 Type array_type = TypeManager.GetElementType (e.Type);
4062 // Provided that array_type is unmanaged,
4064 if (!TypeManager.VerifyUnManaged (array_type, loc))
4068 // and T* is implicitly convertible to the
4069 // pointer type given in the fixed statement.
4071 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4073 Expression converted = Convert.ImplicitConversionRequired (
4074 ec, array_ptr, vi.VariableType, loc);
4075 if (converted == null)
4079 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4081 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4082 new Binary (Binary.Operator.Equality, e, new NullConstant (loc)),
4083 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4087 converted = converted.Resolve (ec);
4089 data [i] = new ExpressionEmitter (converted, vi);
4098 if (e.Type == TypeManager.string_type){
4099 data [i] = new StringEmitter (e, vi, loc);
4104 // Case 4: fixed buffer
4105 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4106 if (fixed_buffer_ptr != null) {
4107 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4112 // For other cases, flag a `this is already fixed expression'
4114 if (e is LocalVariableReference || e is ParameterReference ||
4115 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4117 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4121 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4125 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4126 bool ok = statement.Resolve (ec);
4127 bool flow_unreachable = ec.EndFlowBranching ();
4128 has_ret = flow_unreachable;
4133 protected override void DoEmit (EmitContext ec)
4135 for (int i = 0; i < data.Length; i++) {
4139 statement.Emit (ec);
4145 // Clear the pinned variable
4147 for (int i = 0; i < data.Length; i++) {
4148 data [i].EmitExit (ec);
4152 protected override void CloneTo (CloneContext clonectx, Statement t)
4154 Fixed target = (Fixed) t;
4156 target.type = type.Clone (clonectx);
4157 target.declarators = new ArrayList (declarators.Count);
4158 foreach (Pair p in declarators) {
4159 LocalInfo vi = (LocalInfo) p.First;
4160 Expression e = (Expression) p.Second;
4162 target.declarators.Add (
4163 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4166 target.statement = statement.Clone (clonectx);
4170 public class Catch : Statement {
4171 public readonly string Name;
4173 public Block VarBlock;
4175 Expression type_expr;
4178 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4183 VarBlock = var_block;
4187 public Type CatchType {
4193 public bool IsGeneral {
4195 return type_expr == null;
4199 protected override void DoEmit(EmitContext ec)
4201 ILGenerator ig = ec.ig;
4203 if (CatchType != null)
4204 ig.BeginCatchBlock (CatchType);
4206 ig.BeginCatchBlock (TypeManager.object_type);
4208 if (VarBlock != null)
4212 LocalInfo vi = Block.GetLocalInfo (Name);
4214 throw new Exception ("Variable does not exist in this block");
4216 if (vi.Variable.NeedsTemporary) {
4217 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4218 ig.Emit (OpCodes.Stloc, e);
4220 vi.Variable.EmitInstance (ec);
4221 ig.Emit (OpCodes.Ldloc, e);
4222 vi.Variable.EmitAssign (ec);
4224 vi.Variable.EmitAssign (ec);
4226 ig.Emit (OpCodes.Pop);
4231 public override bool Resolve (EmitContext ec)
4233 using (ec.With (EmitContext.Flags.InCatch, true)) {
4234 if (type_expr != null) {
4235 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4241 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4242 Error (155, "The type caught or thrown must be derived from System.Exception");
4248 if (!Block.Resolve (ec))
4251 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4252 // emit the "unused variable" warnings.
4253 if (VarBlock != null)
4254 return VarBlock.Resolve (ec);
4260 protected override void CloneTo (CloneContext clonectx, Statement t)
4262 Catch target = (Catch) t;
4264 if (type_expr != null)
4265 target.type_expr = type_expr.Clone (clonectx);
4266 if (VarBlock != null)
4267 target.VarBlock = clonectx.LookupBlock (VarBlock);
4268 target.Block = clonectx.LookupBlock (Block);
4272 public class Try : ExceptionStatement {
4273 public Block Fini, Block;
4274 public ArrayList Specific;
4275 public Catch General;
4277 bool need_exc_block;
4280 // specific, general and fini might all be null.
4282 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4284 if (specific == null && general == null){
4285 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4289 this.Specific = specific;
4290 this.General = general;
4295 public override bool Resolve (EmitContext ec)
4299 FlowBranchingException branching = ec.StartFlowBranching (this);
4301 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4303 if (!Block.Resolve (ec))
4306 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4308 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4310 Type[] prev_catches = new Type [Specific.Count];
4312 foreach (Catch c in Specific){
4313 ec.CurrentBranching.CreateSibling (
4314 c.Block, FlowBranching.SiblingType.Catch);
4316 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4318 if (c.Name != null) {
4319 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4321 throw new Exception ();
4323 vi.VariableInfo = null;
4326 if (!c.Resolve (ec))
4329 Type resolved_type = c.CatchType;
4330 for (int ii = 0; ii < last_index; ++ii) {
4331 if (resolved_type == prev_catches [ii] || resolved_type.IsSubclassOf (prev_catches [ii])) {
4332 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4337 prev_catches [last_index++] = resolved_type;
4338 need_exc_block = true;
4341 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4343 if (General != null){
4344 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4345 foreach (Catch c in Specific){
4346 if (c.CatchType == TypeManager.exception_type) {
4347 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'");
4352 ec.CurrentBranching.CreateSibling (
4353 General.Block, FlowBranching.SiblingType.Catch);
4355 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4357 if (!General.Resolve (ec))
4360 need_exc_block = true;
4363 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4367 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4369 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4370 using (ec.With (EmitContext.Flags.InFinally, true)) {
4371 if (!Fini.Resolve (ec))
4376 need_exc_block = true;
4379 if (ec.InIterator) {
4380 ResolveFinally (branching);
4381 need_exc_block |= emit_finally;
4383 emit_finally = Fini != null;
4385 ec.EndFlowBranching ();
4387 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4388 // So, ensure there's some IL code after the finally block.
4389 ec.NeedReturnLabel ();
4391 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4393 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4398 protected override void DoEmit (EmitContext ec)
4400 ILGenerator ig = ec.ig;
4403 ig.BeginExceptionBlock ();
4406 foreach (Catch c in Specific)
4409 if (General != null)
4414 ig.EndExceptionBlock ();
4417 public override void EmitFinally (EmitContext ec)
4423 public bool HasCatch
4426 return General != null || Specific.Count > 0;
4430 protected override void CloneTo (CloneContext clonectx, Statement t)
4432 Try target = (Try) t;
4434 target.Block = clonectx.LookupBlock (Block);
4436 target.Fini = clonectx.LookupBlock (Fini);
4437 if (General != null)
4438 target.General = (Catch) General.Clone (clonectx);
4439 if (Specific != null){
4440 target.Specific = new ArrayList ();
4441 foreach (Catch c in Specific)
4442 target.Specific.Add (c.Clone (clonectx));
4447 public class Using : ExceptionStatement {
4448 object expression_or_block;
4449 public Statement Statement;
4453 Expression [] resolved_vars;
4454 Expression [] converted_vars;
4455 Expression [] assign;
4456 TemporaryVariable local_copy;
4458 public Using (object expression_or_block, Statement stmt, Location l)
4460 this.expression_or_block = expression_or_block;
4466 // Resolves for the case of using using a local variable declaration.
4468 bool ResolveLocalVariableDecls (EmitContext ec)
4470 resolved_vars = new Expression[var_list.Count];
4471 assign = new Expression [var_list.Count];
4472 converted_vars = new Expression[var_list.Count];
4474 for (int i = 0; i < assign.Length; ++i) {
4475 DictionaryEntry e = (DictionaryEntry) var_list [i];
4476 Expression var = (Expression) e.Key;
4477 Expression new_expr = (Expression) e.Value;
4479 Expression a = new Assign (var, new_expr, loc);
4484 resolved_vars [i] = var;
4487 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4488 converted_vars [i] = var;
4492 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4494 Error_IsNotConvertibleToIDisposable (var);
4498 converted_vars [i] = a;
4504 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4506 Report.SymbolRelatedToPreviousError (expr.Type);
4507 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4508 expr.GetSignatureForError ());
4511 bool ResolveExpression (EmitContext ec)
4513 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4514 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4515 Error_IsNotConvertibleToIDisposable (expr);
4520 local_copy = new TemporaryVariable (expr_type, loc);
4521 local_copy.Resolve (ec);
4527 // Emits the code for the case of using using a local variable declaration.
4529 void EmitLocalVariableDecls (EmitContext ec)
4531 ILGenerator ig = ec.ig;
4534 for (i = 0; i < assign.Length; i++) {
4535 ExpressionStatement es = assign [i] as ExpressionStatement;
4538 es.EmitStatement (ec);
4540 assign [i].Emit (ec);
4541 ig.Emit (OpCodes.Pop);
4545 ig.BeginExceptionBlock ();
4547 Statement.Emit (ec);
4549 var_list.Reverse ();
4554 void EmitLocalVariableDeclFinally (EmitContext ec)
4556 ILGenerator ig = ec.ig;
4558 int i = assign.Length;
4559 for (int ii = 0; ii < var_list.Count; ++ii){
4560 Expression var = resolved_vars [--i];
4561 Label skip = ig.DefineLabel ();
4564 ig.BeginFinallyBlock ();
4566 if (!var.Type.IsValueType) {
4568 ig.Emit (OpCodes.Brfalse, skip);
4569 converted_vars [i].Emit (ec);
4570 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4572 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4574 if (!(ml is MethodGroupExpr)) {
4576 ig.Emit (OpCodes.Box, var.Type);
4577 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4579 MethodInfo mi = null;
4581 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4582 if (TypeManager.GetParameterData (mk).Count == 0) {
4589 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4593 IMemoryLocation mloc = (IMemoryLocation) var;
4595 mloc.AddressOf (ec, AddressOp.Load);
4596 ig.Emit (OpCodes.Call, mi);
4600 ig.MarkLabel (skip);
4603 ig.EndExceptionBlock ();
4605 ig.BeginFinallyBlock ();
4610 void EmitExpression (EmitContext ec)
4613 // Make a copy of the expression and operate on that.
4615 ILGenerator ig = ec.ig;
4617 local_copy.Store (ec, expr);
4620 ig.BeginExceptionBlock ();
4622 Statement.Emit (ec);
4626 ig.EndExceptionBlock ();
4629 void EmitExpressionFinally (EmitContext ec)
4631 ILGenerator ig = ec.ig;
4632 if (!expr_type.IsValueType) {
4633 Label skip = ig.DefineLabel ();
4634 local_copy.Emit (ec);
4635 ig.Emit (OpCodes.Brfalse, skip);
4636 local_copy.Emit (ec);
4637 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4638 ig.MarkLabel (skip);
4640 Expression ml = Expression.MemberLookup (
4641 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4642 "Dispose", Location.Null);
4644 if (!(ml is MethodGroupExpr)) {
4645 local_copy.Emit (ec);
4646 ig.Emit (OpCodes.Box, expr_type);
4647 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4649 MethodInfo mi = null;
4651 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4652 if (TypeManager.GetParameterData (mk).Count == 0) {
4659 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4663 local_copy.AddressOf (ec, AddressOp.Load);
4664 ig.Emit (OpCodes.Call, mi);
4669 public override bool Resolve (EmitContext ec)
4671 if (expression_or_block is DictionaryEntry){
4672 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4673 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4675 if (!ResolveLocalVariableDecls (ec))
4678 } else if (expression_or_block is Expression){
4679 expr = (Expression) expression_or_block;
4681 expr = expr.Resolve (ec);
4685 expr_type = expr.Type;
4687 if (!ResolveExpression (ec))
4691 FlowBranchingException branching = ec.StartFlowBranching (this);
4693 bool ok = Statement.Resolve (ec);
4695 ResolveFinally (branching);
4697 ec.EndFlowBranching ();
4699 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4700 // So, ensure there's some IL code after the finally block.
4701 ec.NeedReturnLabel ();
4706 protected override void DoEmit (EmitContext ec)
4708 if (expression_or_block is DictionaryEntry)
4709 EmitLocalVariableDecls (ec);
4710 else if (expression_or_block is Expression)
4711 EmitExpression (ec);
4714 public override void EmitFinally (EmitContext ec)
4716 if (expression_or_block is DictionaryEntry)
4717 EmitLocalVariableDeclFinally (ec);
4718 else if (expression_or_block is Expression)
4719 EmitExpressionFinally (ec);
4722 protected override void CloneTo (CloneContext clonectx, Statement t)
4724 Using target = (Using) t;
4726 if (expression_or_block is Expression)
4727 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4729 target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
4731 target.Statement = Statement.Clone (clonectx);
4736 /// Implementation of the foreach C# statement
4738 public class Foreach : Statement {
4740 Expression variable;
4742 Statement statement;
4744 CollectionForeach collection;
4746 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4747 Statement stmt, Location l)
4750 this.variable = var;
4756 public Statement Statement {
4757 get { return statement; }
4760 public override bool Resolve (EmitContext ec)
4762 expr = expr.Resolve (ec);
4766 if (expr.Type == TypeManager.null_type) {
4767 Report.Error (186, loc, "Use of null is not valid in this context");
4771 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4772 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4773 expr.ExprClassName);
4778 // We need an instance variable. Not sure this is the best
4779 // way of doing this.
4781 // FIXME: When we implement propertyaccess, will those turn
4782 // out to return values in ExprClass? I think they should.
4784 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4785 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4786 collection.Error_Enumerator ();
4790 if (expr.Type.IsArray) {
4791 array = new ArrayForeach (type, variable, expr, statement, loc);
4792 return array.Resolve (ec);
4795 collection = new CollectionForeach (type, variable, expr, statement, loc);
4796 return collection.Resolve (ec);
4799 protected override void DoEmit (EmitContext ec)
4801 ILGenerator ig = ec.ig;
4803 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4804 ec.LoopBegin = ig.DefineLabel ();
4805 ec.LoopEnd = ig.DefineLabel ();
4807 if (collection != null)
4808 collection.Emit (ec);
4812 ec.LoopBegin = old_begin;
4813 ec.LoopEnd = old_end;
4816 protected class ArrayCounter : TemporaryVariable
4818 public ArrayCounter (Location loc)
4819 : base (TypeManager.int32_type, loc)
4822 public void Initialize (EmitContext ec)
4825 ec.ig.Emit (OpCodes.Ldc_I4_0);
4829 public void Increment (EmitContext ec)
4833 ec.ig.Emit (OpCodes.Ldc_I4_1);
4834 ec.ig.Emit (OpCodes.Add);
4839 protected class ArrayForeach : Statement
4841 Expression variable, expr, conv;
4842 Statement statement;
4844 Expression var_type;
4845 TemporaryVariable[] lengths;
4846 ArrayCounter[] counter;
4849 TemporaryVariable copy;
4852 public ArrayForeach (Expression var_type, Expression var,
4853 Expression expr, Statement stmt, Location l)
4855 this.var_type = var_type;
4856 this.variable = var;
4862 public override bool Resolve (EmitContext ec)
4864 array_type = expr.Type;
4865 rank = array_type.GetArrayRank ();
4867 copy = new TemporaryVariable (array_type, loc);
4870 counter = new ArrayCounter [rank];
4871 lengths = new TemporaryVariable [rank];
4873 ArrayList list = new ArrayList ();
4874 for (int i = 0; i < rank; i++) {
4875 counter [i] = new ArrayCounter (loc);
4876 counter [i].Resolve (ec);
4878 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4879 lengths [i].Resolve (ec);
4881 list.Add (counter [i]);
4884 access = new ElementAccess (copy, list).Resolve (ec);
4888 VarExpr ve = var_type as VarExpr;
4890 // Infer implicitly typed local variable from foreach array type
4891 var_type = new TypeExpression (access.Type, ve.Location);
4894 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4895 if (var_type == null)
4898 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4904 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4905 ec.CurrentBranching.CreateSibling ();
4907 variable = variable.ResolveLValue (ec, conv, loc);
4908 if (variable == null)
4911 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4912 if (!statement.Resolve (ec))
4914 ec.EndFlowBranching ();
4916 // There's no direct control flow from the end of the embedded statement to the end of the loop
4917 ec.CurrentBranching.CurrentUsageVector.Goto ();
4919 ec.EndFlowBranching ();
4924 protected override void DoEmit (EmitContext ec)
4926 ILGenerator ig = ec.ig;
4928 copy.Store (ec, expr);
4930 Label[] test = new Label [rank];
4931 Label[] loop = new Label [rank];
4933 for (int i = 0; i < rank; i++) {
4934 test [i] = ig.DefineLabel ();
4935 loop [i] = ig.DefineLabel ();
4937 lengths [i].EmitThis (ec);
4938 ((ArrayAccess) access).EmitGetLength (ec, i);
4939 lengths [i].EmitStore (ec);
4942 for (int i = 0; i < rank; i++) {
4943 counter [i].Initialize (ec);
4945 ig.Emit (OpCodes.Br, test [i]);
4946 ig.MarkLabel (loop [i]);
4949 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4951 statement.Emit (ec);
4953 ig.MarkLabel (ec.LoopBegin);
4955 for (int i = rank - 1; i >= 0; i--){
4956 counter [i].Increment (ec);
4958 ig.MarkLabel (test [i]);
4959 counter [i].Emit (ec);
4960 lengths [i].Emit (ec);
4961 ig.Emit (OpCodes.Blt, loop [i]);
4964 ig.MarkLabel (ec.LoopEnd);
4968 protected class CollectionForeach : ExceptionStatement
4970 Expression variable, expr;
4971 Statement statement;
4973 TemporaryVariable enumerator;
4977 MethodGroupExpr get_enumerator;
4978 PropertyExpr get_current;
4979 MethodInfo move_next;
4980 Expression var_type;
4981 Type enumerator_type;
4983 bool enumerator_found;
4985 public CollectionForeach (Expression var_type, Expression var,
4986 Expression expr, Statement stmt, Location l)
4988 this.var_type = var_type;
4989 this.variable = var;
4995 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
4997 Type return_type = mi.ReturnType;
4999 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5001 // Apply the same optimization as MS: skip the GetEnumerator
5002 // returning an IEnumerator, and use the one returning a
5003 // CharEnumerator instead. This allows us to avoid the
5004 // try-finally block and the boxing.
5009 // Ok, we can access it, now make sure that we can do something
5010 // with this `GetEnumerator'
5013 if (return_type == TypeManager.ienumerator_type ||
5014 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5015 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5017 // If it is not an interface, lets try to find the methods ourselves.
5018 // For example, if we have:
5019 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5020 // We can avoid the iface call. This is a runtime perf boost.
5021 // even bigger if we have a ValueType, because we avoid the cost
5024 // We have to make sure that both methods exist for us to take
5025 // this path. If one of the methods does not exist, we will just
5026 // use the interface. Sadly, this complex if statement is the only
5027 // way I could do this without a goto
5032 // Prefer a generic enumerator over a non-generic one.
5034 if (return_type.IsInterface && return_type.IsGenericType) {
5035 enumerator_type = return_type;
5036 if (!FetchGetCurrent (ec, return_type))
5037 get_current = new PropertyExpr (
5038 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5039 if (!FetchMoveNext (return_type))
5040 move_next = TypeManager.bool_movenext_void;
5045 if (return_type.IsInterface ||
5046 !FetchMoveNext (return_type) ||
5047 !FetchGetCurrent (ec, return_type)) {
5048 enumerator_type = return_type;
5049 move_next = TypeManager.bool_movenext_void;
5050 get_current = new PropertyExpr (
5051 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5056 // Ok, so they dont return an IEnumerable, we will have to
5057 // find if they support the GetEnumerator pattern.
5060 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5061 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",
5062 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5067 enumerator_type = return_type;
5068 is_disposable = !enumerator_type.IsSealed ||
5069 TypeManager.ImplementsInterface (
5070 enumerator_type, TypeManager.idisposable_type);
5076 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5078 bool FetchMoveNext (Type t)
5080 MemberList move_next_list;
5082 move_next_list = TypeContainer.FindMembers (
5083 t, MemberTypes.Method,
5084 BindingFlags.Public | BindingFlags.Instance,
5085 Type.FilterName, "MoveNext");
5086 if (move_next_list.Count == 0)
5089 foreach (MemberInfo m in move_next_list){
5090 MethodInfo mi = (MethodInfo) m;
5092 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5093 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5103 // Retrieves a `public T get_Current ()' method from the Type `t'
5105 bool FetchGetCurrent (EmitContext ec, Type t)
5107 PropertyExpr pe = Expression.MemberLookup (
5108 ec.ContainerType, t, "Current", MemberTypes.Property,
5109 Expression.AllBindingFlags, loc) as PropertyExpr;
5118 // Retrieves a `public void Dispose ()' method from the Type `t'
5120 static MethodInfo FetchMethodDispose (Type t)
5122 MemberList dispose_list;
5124 dispose_list = TypeContainer.FindMembers (
5125 t, MemberTypes.Method,
5126 BindingFlags.Public | BindingFlags.Instance,
5127 Type.FilterName, "Dispose");
5128 if (dispose_list.Count == 0)
5131 foreach (MemberInfo m in dispose_list){
5132 MethodInfo mi = (MethodInfo) m;
5134 if (TypeManager.GetParameterData (mi).Count == 0){
5135 if (mi.ReturnType == TypeManager.void_type)
5142 public void Error_Enumerator ()
5144 if (enumerator_found) {
5148 Report.Error (1579, loc,
5149 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5150 TypeManager.CSharpName (expr.Type));
5153 bool IsOverride (MethodInfo m)
5155 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5157 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5159 if (m is MethodBuilder)
5162 MethodInfo base_method = m.GetBaseDefinition ();
5163 return base_method != m;
5166 bool TryType (EmitContext ec, Type t)
5168 MethodGroupExpr mg = Expression.MemberLookup (
5169 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5170 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5174 MethodInfo result = null;
5175 MethodInfo tmp_move_next = null;
5176 PropertyExpr tmp_get_cur = null;
5177 Type tmp_enumerator_type = enumerator_type;
5178 foreach (MethodInfo mi in mg.Methods) {
5179 if (TypeManager.GetParameterData (mi).Count != 0)
5182 // Check whether GetEnumerator is public
5183 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5186 if (IsOverride (mi))
5189 enumerator_found = true;
5191 if (!GetEnumeratorFilter (ec, mi))
5194 if (result != null) {
5195 if (TypeManager.IsGenericType (result.ReturnType)) {
5196 if (!TypeManager.IsGenericType (mi.ReturnType))
5199 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5200 Report.SymbolRelatedToPreviousError (t);
5201 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5202 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5203 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5207 // Always prefer generics enumerators
5208 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5209 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5210 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5213 Report.SymbolRelatedToPreviousError (result);
5214 Report.SymbolRelatedToPreviousError (mi);
5215 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5216 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5221 tmp_move_next = move_next;
5222 tmp_get_cur = get_current;
5223 tmp_enumerator_type = enumerator_type;
5224 if (mi.DeclaringType == t)
5228 if (result != null) {
5229 move_next = tmp_move_next;
5230 get_current = tmp_get_cur;
5231 enumerator_type = tmp_enumerator_type;
5232 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5233 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5235 if (t != expr.Type) {
5236 expr = Convert.ExplicitConversion (
5239 throw new InternalErrorException ();
5242 get_enumerator.InstanceExpression = expr;
5243 get_enumerator.IsBase = t != expr.Type;
5251 bool ProbeCollectionType (EmitContext ec, Type t)
5253 int errors = Report.Errors;
5254 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5255 if (TryType (ec, tt))
5260 if (Report.Errors > errors)
5264 // Now try to find the method in the interfaces
5266 Type [] ifaces = TypeManager.GetInterfaces (t);
5267 foreach (Type i in ifaces){
5268 if (TryType (ec, i))
5275 public override bool Resolve (EmitContext ec)
5277 enumerator_type = TypeManager.ienumerator_type;
5278 is_disposable = true;
5280 if (!ProbeCollectionType (ec, expr.Type)) {
5281 Error_Enumerator ();
5285 VarExpr ve = var_type as VarExpr;
5287 // Infer implicitly typed local variable from foreach enumerable type
5288 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5291 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5292 if (var_type == null)
5295 enumerator = new TemporaryVariable (enumerator_type, loc);
5296 enumerator.Resolve (ec);
5298 init = new Invocation (get_enumerator, null);
5299 init = init.Resolve (ec);
5303 Expression move_next_expr;
5305 MemberInfo[] mi = new MemberInfo[] { move_next };
5306 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5307 mg.InstanceExpression = enumerator;
5309 move_next_expr = new Invocation (mg, null);
5312 get_current.InstanceExpression = enumerator;
5314 Statement block = new CollectionForeachStatement (
5315 var_type.Type, variable, get_current, statement, loc);
5317 loop = new While (move_next_expr, block, loc);
5321 FlowBranchingException branching = null;
5323 branching = ec.StartFlowBranching (this);
5325 if (!loop.Resolve (ec))
5328 if (is_disposable) {
5329 ResolveFinally (branching);
5330 ec.EndFlowBranching ();
5332 emit_finally = true;
5337 protected override void DoEmit (EmitContext ec)
5339 ILGenerator ig = ec.ig;
5341 enumerator.Store (ec, init);
5344 // Protect the code in a try/finalize block, so that
5345 // if the beast implement IDisposable, we get rid of it
5347 if (is_disposable && emit_finally)
5348 ig.BeginExceptionBlock ();
5353 // Now the finally block
5355 if (is_disposable) {
5358 ig.EndExceptionBlock ();
5363 public override void EmitFinally (EmitContext ec)
5365 ILGenerator ig = ec.ig;
5367 if (enumerator_type.IsValueType) {
5368 MethodInfo mi = FetchMethodDispose (enumerator_type);
5370 enumerator.EmitLoadAddress (ec);
5371 ig.Emit (OpCodes.Call, mi);
5373 enumerator.Emit (ec);
5374 ig.Emit (OpCodes.Box, enumerator_type);
5375 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5378 Label call_dispose = ig.DefineLabel ();
5380 enumerator.Emit (ec);
5381 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5382 ig.Emit (OpCodes.Dup);
5383 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5384 ig.Emit (OpCodes.Pop);
5386 Label end_finally = ig.DefineLabel ();
5387 ig.Emit (OpCodes.Br, end_finally);
5389 ig.MarkLabel (call_dispose);
5390 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5391 ig.MarkLabel (end_finally);
5396 protected class CollectionForeachStatement : Statement
5399 Expression variable, current, conv;
5400 Statement statement;
5403 public CollectionForeachStatement (Type type, Expression variable,
5404 Expression current, Statement statement,
5408 this.variable = variable;
5409 this.current = current;
5410 this.statement = statement;
5414 public override bool Resolve (EmitContext ec)
5416 current = current.Resolve (ec);
5417 if (current == null)
5420 conv = Convert.ExplicitConversion (ec, current, type, loc);
5424 assign = new Assign (variable, conv, loc);
5425 if (assign.Resolve (ec) == null)
5428 if (!statement.Resolve (ec))
5434 protected override void DoEmit (EmitContext ec)
5436 assign.EmitStatement (ec);
5437 statement.Emit (ec);
5441 protected override void CloneTo (CloneContext clonectx, Statement t)
5443 Foreach target = (Foreach) t;
5445 target.type = type.Clone (clonectx);
5446 target.variable = variable.Clone (clonectx);
5447 target.expr = expr.Clone (clonectx);
5448 target.statement = statement.Clone (clonectx);