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 {
717 protected Expression Expr;
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;
1093 target.expr = expr.Clone (clonectx);
1097 public class Break : Statement {
1099 public Break (Location l)
1104 bool unwind_protect;
1106 public override bool Resolve (EmitContext ec)
1108 int errors = Report.Errors;
1109 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1110 ec.CurrentBranching.CurrentUsageVector.Goto ();
1111 return errors == Report.Errors;
1114 protected override void DoEmit (EmitContext ec)
1116 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1119 protected override void CloneTo (CloneContext clonectx, Statement t)
1125 public class Continue : Statement {
1127 public Continue (Location l)
1132 bool unwind_protect;
1134 public override bool Resolve (EmitContext ec)
1136 int errors = Report.Errors;
1137 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1138 ec.CurrentBranching.CurrentUsageVector.Goto ();
1139 return errors == Report.Errors;
1142 protected override void DoEmit (EmitContext ec)
1144 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1147 protected override void CloneTo (CloneContext clonectx, Statement t)
1153 public abstract class Variable
1155 public abstract Type Type {
1159 public abstract bool HasInstance {
1163 public abstract bool NeedsTemporary {
1167 public abstract void EmitInstance (EmitContext ec);
1169 public abstract void Emit (EmitContext ec);
1171 public abstract void EmitAssign (EmitContext ec);
1173 public abstract void EmitAddressOf (EmitContext ec);
1176 public interface IKnownVariable {
1177 Block Block { get; }
1178 Location Location { get; }
1182 // The information about a user-perceived local variable
1184 public class LocalInfo : IKnownVariable {
1185 public Expression Type;
1187 public Type VariableType;
1188 public readonly string Name;
1189 public readonly Location Location;
1190 public readonly Block Block;
1192 public VariableInfo VariableInfo;
1195 public Variable Variable {
1207 CompilerGenerated = 64,
1211 public enum ReadOnlyContext: byte {
1218 ReadOnlyContext ro_context;
1219 LocalBuilder builder;
1221 public LocalInfo (Expression type, string name, Block block, Location l)
1229 public LocalInfo (DeclSpace ds, Block block, Location l)
1231 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1236 public void ResolveVariable (EmitContext ec)
1238 Block theblock = Block;
1239 if (theblock.ScopeInfo != null)
1240 var = theblock.ScopeInfo.GetCapturedVariable (this);
1245 // This is needed to compile on both .NET 1.x and .NET 2.x
1246 // the later introduced `DeclareLocal (Type t, bool pinned)'
1248 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1250 builder = ec.ig.DeclareLocal (VariableType);
1252 var = new LocalVariable (this, builder);
1256 public void EmitSymbolInfo (EmitContext ec, string name)
1258 if (builder != null)
1259 ec.DefineLocalVariable (name, builder);
1262 public bool IsThisAssigned (EmitContext ec)
1264 if (VariableInfo == null)
1265 throw new Exception ();
1267 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1270 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1273 public bool IsAssigned (EmitContext ec)
1275 if (VariableInfo == null)
1276 throw new Exception ();
1278 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1281 public bool Resolve (EmitContext ec)
1283 if (VariableType == null) {
1284 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1288 VariableType = texpr.Type;
1291 if (TypeManager.IsGenericParameter (VariableType))
1294 if (VariableType == TypeManager.void_type) {
1295 Expression.Error_VoidInvalidInTheContext (Location);
1299 if (VariableType.IsAbstract && VariableType.IsSealed) {
1300 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1304 if (VariableType.IsPointer && !ec.InUnsafe)
1305 Expression.UnsafeError (Location);
1310 public bool IsCaptured {
1311 get { return (flags & Flags.Captured) != 0; }
1312 set { flags |= Flags.Captured; }
1315 public bool IsConstant {
1316 get { return (flags & Flags.IsConstant) != 0; }
1317 set { flags |= Flags.IsConstant; }
1320 public bool AddressTaken {
1321 get { return (flags & Flags.AddressTaken) != 0; }
1322 set { flags |= Flags.AddressTaken; }
1325 public bool CompilerGenerated {
1326 get { return (flags & Flags.CompilerGenerated) != 0; }
1327 set { flags |= Flags.CompilerGenerated; }
1330 public override string ToString ()
1332 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1333 Name, Type, VariableInfo, Location);
1337 get { return (flags & Flags.Used) != 0; }
1338 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1341 public bool ReadOnly {
1342 get { return (flags & Flags.ReadOnly) != 0; }
1345 public void SetReadOnlyContext (ReadOnlyContext context)
1347 flags |= Flags.ReadOnly;
1348 ro_context = context;
1351 public string GetReadOnlyContext ()
1354 throw new InternalErrorException ("Variable is not readonly");
1356 switch (ro_context) {
1357 case ReadOnlyContext.Fixed:
1358 return "fixed variable";
1359 case ReadOnlyContext.Foreach:
1360 return "foreach iteration variable";
1361 case ReadOnlyContext.Using:
1362 return "using variable";
1364 throw new NotImplementedException ();
1368 // Whether the variable is pinned, if Pinned the variable has been
1369 // allocated in a pinned slot with DeclareLocal.
1371 public bool Pinned {
1372 get { return (flags & Flags.Pinned) != 0; }
1373 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1376 public bool IsThis {
1377 get { return (flags & Flags.IsThis) != 0; }
1378 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1381 Block IKnownVariable.Block {
1382 get { return Block; }
1385 Location IKnownVariable.Location {
1386 get { return Location; }
1389 protected class LocalVariable : Variable
1391 public readonly LocalInfo LocalInfo;
1392 LocalBuilder builder;
1394 public LocalVariable (LocalInfo local, LocalBuilder builder)
1396 this.LocalInfo = local;
1397 this.builder = builder;
1400 public override Type Type {
1401 get { return LocalInfo.VariableType; }
1404 public override bool HasInstance {
1405 get { return false; }
1408 public override bool NeedsTemporary {
1409 get { return false; }
1412 public override void EmitInstance (EmitContext ec)
1417 public override void Emit (EmitContext ec)
1419 ec.ig.Emit (OpCodes.Ldloc, builder);
1422 public override void EmitAssign (EmitContext ec)
1424 ec.ig.Emit (OpCodes.Stloc, builder);
1427 public override void EmitAddressOf (EmitContext ec)
1429 ec.ig.Emit (OpCodes.Ldloca, builder);
1433 public LocalInfo Clone (CloneContext clonectx)
1436 // Variables in anonymous block are not resolved yet
1438 if (VariableType == null)
1439 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1442 // Variables in method block are resolved
1444 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1445 li.VariableType = VariableType;
1451 /// Block represents a C# block.
1455 /// This class is used in a number of places: either to represent
1456 /// explicit blocks that the programmer places or implicit blocks.
1458 /// Implicit blocks are used as labels or to introduce variable
1461 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1462 /// they contain extra information that is not necessary on normal blocks.
1464 public class Block : Statement {
1465 public Block Parent;
1466 public readonly Location StartLocation;
1467 public Location EndLocation = Location.Null;
1469 public ExplicitBlock Explicit;
1470 public ToplevelBlock Toplevel;
1473 public enum Flags : byte {
1476 VariablesInitialized = 4,
1480 HasVarargs = 64, // Used in ToplevelBlock
1483 protected Flags flags;
1485 public bool Unchecked {
1486 get { return (flags & Flags.Unchecked) != 0; }
1487 set { flags |= Flags.Unchecked; }
1490 public bool Unsafe {
1491 get { return (flags & Flags.Unsafe) != 0; }
1492 set { flags |= Flags.Unsafe; }
1496 // The statements in this block
1498 protected ArrayList statements;
1502 // An array of Blocks. We keep track of children just
1503 // to generate the local variable declarations.
1505 // Statements and child statements are handled through the
1511 // Labels. (label, block) pairs.
1516 // Keeps track of (name, type) pairs
1518 IDictionary variables;
1521 // Keeps track of constants
1522 Hashtable constants;
1525 // Temporary variables.
1527 ArrayList temporary_variables;
1530 // If this is a switch section, the enclosing switch block.
1534 ExpressionStatement scope_init;
1536 ArrayList anonymous_children;
1538 protected static int id;
1542 int assignable_slots;
1543 protected ScopeInfo scope_info;
1544 bool unreachable_shown;
1547 public Block (Block parent)
1548 : this (parent, (Flags) 0, Location.Null, Location.Null)
1551 public Block (Block parent, Flags flags)
1552 : this (parent, flags, Location.Null, Location.Null)
1555 public Block (Block parent, Location start, Location end)
1556 : this (parent, (Flags) 0, start, end)
1559 public Block (Block parent, Flags flags, Location start, Location end)
1561 if (parent != null) {
1562 parent.AddChild (this);
1564 // the appropriate constructors will fixup these fields
1565 Toplevel = parent.Toplevel;
1566 Explicit = parent.Explicit;
1569 this.Parent = parent;
1571 this.StartLocation = start;
1572 this.EndLocation = end;
1575 statements = new ArrayList ();
1578 public Block CreateSwitchBlock (Location start)
1580 // FIXME: should this be implicit?
1581 Block new_block = new ExplicitBlock (this, start, start);
1582 new_block.switch_block = this;
1587 get { return this_id; }
1590 public IDictionary Variables {
1592 if (variables == null)
1593 variables = new ListDictionary ();
1598 void AddChild (Block b)
1600 if (children == null)
1601 children = new ArrayList ();
1606 public void SetEndLocation (Location loc)
1611 protected static void Error_158 (string name, Location loc)
1613 Report.Error (158, loc, "The label `{0}' shadows another label " +
1614 "by the same name in a contained scope", name);
1618 /// Adds a label to the current block.
1622 /// false if the name already exists in this block. true
1626 public bool AddLabel (LabeledStatement target)
1628 if (switch_block != null)
1629 return switch_block.AddLabel (target);
1631 string name = target.Name;
1634 while (cur != null) {
1635 LabeledStatement s = cur.DoLookupLabel (name);
1637 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1638 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1642 if (this == Explicit)
1648 while (cur != null) {
1649 if (cur.DoLookupLabel (name) != null) {
1650 Error_158 (name, target.loc);
1654 if (children != null) {
1655 foreach (Block b in children) {
1656 LabeledStatement s = b.DoLookupLabel (name);
1660 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1661 Error_158 (name, target.loc);
1669 Toplevel.CheckError158 (name, target.loc);
1672 labels = new Hashtable ();
1674 labels.Add (name, target);
1678 public LabeledStatement LookupLabel (string name)
1680 LabeledStatement s = DoLookupLabel (name);
1684 if (children == null)
1687 foreach (Block child in children) {
1688 if (Explicit != child.Explicit)
1691 s = child.LookupLabel (name);
1699 LabeledStatement DoLookupLabel (string name)
1701 if (switch_block != null)
1702 return switch_block.LookupLabel (name);
1705 if (labels.Contains (name))
1706 return ((LabeledStatement) labels [name]);
1711 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1714 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1715 while (kvi == null) {
1716 b = b.Explicit.Parent;
1719 kvi = b.Explicit.GetKnownVariable (name);
1725 // Is kvi.Block nested inside 'b'
1726 if (b.Explicit != kvi.Block.Explicit) {
1728 // If a variable by the same name it defined in a nested block of this
1729 // block, we violate the invariant meaning in a block.
1732 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1733 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1738 // It's ok if the definition is in a nested subblock of b, but not
1739 // nested inside this block -- a definition in a sibling block
1740 // should not affect us.
1746 // Block 'b' and kvi.Block are the same textual block.
1747 // However, different variables are extant.
1749 // Check if the variable is in scope in both blocks. We use
1750 // an indirect check that depends on AddVariable doing its
1751 // part in maintaining the invariant-meaning-in-block property.
1753 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1757 // Even though we detected the error when the name is used, we
1758 // treat it as if the variable declaration was in error.
1760 Report.SymbolRelatedToPreviousError (loc, name);
1761 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1765 public LocalInfo AddVariable (Expression type, string name, Location l)
1767 LocalInfo vi = GetLocalInfo (name);
1769 Report.SymbolRelatedToPreviousError (vi.Location, name);
1770 if (Explicit == vi.Block.Explicit)
1771 Error_AlreadyDeclared (l, name, null);
1773 Error_AlreadyDeclared (l, name, "parent");
1777 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1779 Report.SymbolRelatedToPreviousError (pi.Location, name);
1780 Error_AlreadyDeclared (loc, name,
1781 pi.Block == Toplevel ? "method argument" : "parent or current");
1785 if (Toplevel.GenericMethod != null) {
1786 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1787 if (tp.Name == name) {
1788 Report.SymbolRelatedToPreviousError (tp);
1789 Error_AlreadyDeclaredTypeParameter (loc, name);
1795 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1797 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1798 Error_AlreadyDeclared (l, name, "child");
1802 vi = new LocalInfo (type, name, this, l);
1805 if ((flags & Flags.VariablesInitialized) != 0)
1806 throw new InternalErrorException ("block has already been resolved");
1811 protected virtual void AddVariable (LocalInfo li)
1813 Variables.Add (li.Name, li);
1814 Explicit.AddKnownVariable (li.Name, li);
1817 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1819 if (reason == null) {
1820 Error_AlreadyDeclared (loc, var);
1824 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1825 "in this scope because it would give a different meaning " +
1826 "to `{0}', which is already used in a `{1}' scope " +
1827 "to denote something else", var, reason);
1830 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1832 Report.Error (128, loc,
1833 "A local variable named `{0}' is already defined in this scope", name);
1836 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1838 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1841 public bool AddConstant (Expression type, string name, Expression value, Location l)
1843 if (AddVariable (type, name, l) == null)
1846 if (constants == null)
1847 constants = new Hashtable ();
1849 constants.Add (name, value);
1851 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1856 static int next_temp_id = 0;
1858 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1860 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1862 if (temporary_variables == null)
1863 temporary_variables = new ArrayList ();
1865 int id = ++next_temp_id;
1866 string name = "$s_" + id.ToString ();
1868 LocalInfo li = new LocalInfo (te, name, this, loc);
1869 li.CompilerGenerated = true;
1870 temporary_variables.Add (li);
1874 public LocalInfo GetLocalInfo (string name)
1876 for (Block b = this; b != null; b = b.Parent) {
1877 if (b.variables != null) {
1878 LocalInfo ret = b.variables [name] as LocalInfo;
1886 public Expression GetVariableType (string name)
1888 LocalInfo vi = GetLocalInfo (name);
1889 return vi == null ? null : vi.Type;
1892 public Expression GetConstantExpression (string name)
1894 for (Block b = this; b != null; b = b.Parent) {
1895 if (b.constants != null) {
1896 Expression ret = b.constants [name] as Expression;
1904 public void AddStatement (Statement s)
1907 flags |= Flags.BlockUsed;
1911 get { return (flags & Flags.BlockUsed) != 0; }
1916 flags |= Flags.BlockUsed;
1919 public bool HasRet {
1920 get { return (flags & Flags.HasRet) != 0; }
1923 public bool IsDestructor {
1924 get { return (flags & Flags.IsDestructor) != 0; }
1927 public void SetDestructor ()
1929 flags |= Flags.IsDestructor;
1932 public int AssignableSlots {
1934 if ((flags & Flags.VariablesInitialized) == 0)
1935 throw new Exception ("Variables have not been initialized yet");
1936 return assignable_slots;
1940 public ScopeInfo ScopeInfo {
1941 get { return scope_info; }
1944 public ScopeInfo CreateScopeInfo ()
1946 if (scope_info == null)
1947 scope_info = ScopeInfo.CreateScope (this);
1952 public ArrayList AnonymousChildren {
1953 get { return anonymous_children; }
1956 public void AddAnonymousChild (ToplevelBlock b)
1958 if (anonymous_children == null)
1959 anonymous_children = new ArrayList ();
1961 anonymous_children.Add (b);
1964 void DoResolveConstants (EmitContext ec)
1966 if (constants == null)
1969 if (variables == null)
1970 throw new InternalErrorException ("cannot happen");
1972 foreach (DictionaryEntry de in variables) {
1973 string name = (string) de.Key;
1974 LocalInfo vi = (LocalInfo) de.Value;
1975 Type variable_type = vi.VariableType;
1977 if (variable_type == null) {
1978 if (vi.Type is VarExpr)
1979 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1984 Expression cv = (Expression) constants [name];
1988 // Don't let 'const int Foo = Foo;' succeed.
1989 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1990 // which in turn causes the 'must be constant' error to be triggered.
1991 constants.Remove (name);
1993 if (!Const.IsConstantTypeValid (variable_type)) {
1994 Const.Error_InvalidConstantType (variable_type, loc);
1998 ec.CurrentBlock = this;
2000 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2001 e = cv.Resolve (ec);
2006 Constant ce = e as Constant;
2008 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2012 e = ce.ConvertImplicitly (variable_type);
2014 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2015 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2017 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2021 constants.Add (name, e);
2022 vi.IsConstant = true;
2026 protected void ResolveMeta (EmitContext ec, int offset)
2028 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2030 // If some parent block was unsafe, we remain unsafe even if this block
2031 // isn't explicitly marked as such.
2032 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2033 flags |= Flags.VariablesInitialized;
2035 if (variables != null) {
2036 foreach (LocalInfo li in variables.Values) {
2037 if (!li.Resolve (ec))
2039 li.VariableInfo = new VariableInfo (li, offset);
2040 offset += li.VariableInfo.Length;
2043 assignable_slots = offset;
2045 DoResolveConstants (ec);
2047 if (children == null)
2049 foreach (Block b in children)
2050 b.ResolveMeta (ec, offset);
2055 // Emits the local variable declarations for a block
2057 public virtual void EmitMeta (EmitContext ec)
2059 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2060 if (ScopeInfo != null) {
2061 scope_init = ScopeInfo.GetScopeInitializer (ec);
2062 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2066 if (variables != null){
2067 foreach (LocalInfo vi in variables.Values)
2068 vi.ResolveVariable (ec);
2071 if (temporary_variables != null) {
2072 foreach (LocalInfo vi in temporary_variables)
2073 vi.ResolveVariable (ec);
2076 if (children != null){
2077 foreach (Block b in children)
2082 void UsageWarning (FlowBranching.UsageVector vector)
2086 if ((variables != null) && (Report.WarningLevel >= 3)) {
2087 foreach (DictionaryEntry de in variables){
2088 LocalInfo vi = (LocalInfo) de.Value;
2093 name = (string) de.Key;
2095 // vi.VariableInfo can be null for 'catch' variables
2096 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2097 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2099 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2105 private void CheckPossibleMistakenEmptyStatement (Statement s)
2109 // Some statements are wrapped by a Block. Since
2110 // others' internal could be changed, here I treat
2111 // them as possibly wrapped by Block equally.
2112 Block b = s as Block;
2113 if (b != null && b.statements.Count == 1)
2114 s = (Statement) b.statements [0];
2117 body = ((Lock) s).Statement;
2119 body = ((For) s).Statement;
2120 else if (s is Foreach)
2121 body = ((Foreach) s).Statement;
2122 else if (s is While)
2123 body = ((While) s).Statement;
2124 else if (s is Using)
2125 body = ((Using) s).Statement;
2126 else if (s is Fixed)
2127 body = ((Fixed) s).Statement;
2131 if (body == null || body is EmptyStatement)
2132 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2135 public override bool Resolve (EmitContext ec)
2137 Block prev_block = ec.CurrentBlock;
2140 int errors = Report.Errors;
2142 ec.CurrentBlock = this;
2143 ec.StartFlowBranching (this);
2145 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2148 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2149 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2150 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2151 // responsible for handling the situation.
2153 int statement_count = statements.Count;
2154 for (int ix = 0; ix < statement_count; ix++){
2155 Statement s = (Statement) statements [ix];
2156 // Check possible empty statement (CS0642)
2157 if (Report.WarningLevel >= 3 &&
2158 ix + 1 < statement_count &&
2159 statements [ix + 1] is Block)
2160 CheckPossibleMistakenEmptyStatement (s);
2163 // Warn if we detect unreachable code.
2166 if (s is EmptyStatement)
2170 ((Block) s).unreachable = true;
2172 if (!unreachable_shown && !(s is LabeledStatement)) {
2173 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2174 unreachable_shown = true;
2179 // Note that we're not using ResolveUnreachable() for unreachable
2180 // statements here. ResolveUnreachable() creates a temporary
2181 // flow branching and kills it afterwards. This leads to problems
2182 // if you have two unreachable statements where the first one
2183 // assigns a variable and the second one tries to access it.
2186 if (!s.Resolve (ec)) {
2188 if (ec.IsInProbingMode)
2191 statements [ix] = EmptyStatement.Value;
2195 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2196 statements [ix] = EmptyStatement.Value;
2198 num_statements = ix + 1;
2200 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2201 if (unreachable && s is LabeledStatement)
2202 throw new InternalErrorException ("should not happen");
2205 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2206 ec.CurrentBranching, statement_count, num_statements);
2208 while (ec.CurrentBranching is FlowBranchingLabeled)
2209 ec.EndFlowBranching ();
2211 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2213 ec.CurrentBlock = prev_block;
2215 // If we're a non-static `struct' constructor which doesn't have an
2216 // initializer, then we must initialize all of the struct's fields.
2217 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2220 if ((labels != null) && (Report.WarningLevel >= 2)) {
2221 foreach (LabeledStatement label in labels.Values)
2222 if (!label.HasBeenReferenced)
2223 Report.Warning (164, 2, label.loc,
2224 "This label has not been referenced");
2227 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2229 if (vector.IsUnreachable)
2230 flags |= Flags.HasRet;
2232 if (ok && (errors == Report.Errors)) {
2233 UsageWarning (vector);
2239 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2241 unreachable_shown = true;
2245 Report.Warning (162, 2, loc, "Unreachable code detected");
2247 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2248 bool ok = Resolve (ec);
2249 ec.KillFlowBranching ();
2254 protected override void DoEmit (EmitContext ec)
2256 for (int ix = 0; ix < num_statements; ix++){
2257 Statement s = (Statement) statements [ix];
2262 public override void Emit (EmitContext ec)
2264 Block prev_block = ec.CurrentBlock;
2266 ec.CurrentBlock = this;
2268 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2269 bool is_lexical_block = this == Explicit && Parent != null;
2271 if (emit_debug_info) {
2272 if (is_lexical_block)
2275 ec.Mark (StartLocation, true);
2276 if (scope_init != null)
2277 scope_init.EmitStatement (ec);
2279 ec.Mark (EndLocation, true);
2281 if (emit_debug_info) {
2282 if (is_lexical_block)
2285 if (variables != null) {
2286 foreach (DictionaryEntry de in variables) {
2287 string name = (string) de.Key;
2288 LocalInfo vi = (LocalInfo) de.Value;
2290 vi.EmitSymbolInfo (ec, name);
2295 ec.CurrentBlock = prev_block;
2298 public override string ToString ()
2300 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2303 protected override void CloneTo (CloneContext clonectx, Statement t)
2305 Block target = (Block) t;
2307 clonectx.AddBlockMap (this, target);
2309 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2310 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2312 target.Parent = clonectx.RemapBlockCopy (Parent);
2314 if (variables != null){
2315 target.variables = new Hashtable ();
2317 foreach (DictionaryEntry de in variables){
2318 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2319 target.variables [de.Key] = newlocal;
2320 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2324 target.statements = new ArrayList (statements.Count);
2325 foreach (Statement s in statements)
2326 target.statements.Add (s.Clone (clonectx));
2328 if (target.children != null){
2329 target.children = new ArrayList (children.Count);
2330 foreach (Block b in children){
2331 target.children.Add (clonectx.LookupBlock (b));
2336 // TODO: labels, switch_block, constants (?), anonymous_children
2341 public class ExplicitBlock : Block {
2342 public ExplicitBlock (Block parent, Location start, Location end)
2343 : this (parent, (Flags) 0, start, end)
2347 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2348 : base (parent, flags, start, end)
2350 this.Explicit = this;
2353 Hashtable known_variables;
2356 // Marks a variable with name @name as being used in this or a child block.
2357 // If a variable name has been used in a child block, it's illegal to
2358 // declare a variable with the same name in the current block.
2360 internal void AddKnownVariable (string name, IKnownVariable info)
2362 if (known_variables == null)
2363 known_variables = new Hashtable ();
2365 known_variables [name] = info;
2368 Parent.Explicit.AddKnownVariable (name, info);
2371 internal IKnownVariable GetKnownVariable (string name)
2373 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2376 protected override void CloneTo (CloneContext clonectx, Statement t)
2378 ExplicitBlock target = (ExplicitBlock) t;
2379 target.known_variables = null;
2380 base.CloneTo (clonectx, t);
2384 public class ToplevelParameterInfo : IKnownVariable {
2385 public readonly ToplevelBlock Block;
2386 public readonly int Index;
2387 public VariableInfo VariableInfo;
2389 Block IKnownVariable.Block {
2390 get { return Block; }
2392 public Parameter Parameter {
2393 get { return Block.Parameters [Index]; }
2395 public Location Location {
2396 get { return Parameter.Location; }
2399 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2407 // A toplevel block contains extra information, the split is done
2408 // only to separate information that would otherwise bloat the more
2409 // lightweight Block.
2411 // In particular, this was introduced when the support for Anonymous
2412 // Methods was implemented.
2414 public class ToplevelBlock : ExplicitBlock {
2415 GenericMethod generic;
2416 FlowBranchingToplevel top_level_branching;
2417 AnonymousContainer anonymous_container;
2418 RootScopeInfo root_scope;
2419 Parameters parameters;
2420 ToplevelParameterInfo[] parameter_info;
2422 public bool HasVarargs {
2423 get { return (flags & Flags.HasVarargs) != 0; }
2424 set { flags |= Flags.HasVarargs; }
2427 public bool IsIterator {
2428 get { return (flags & Flags.IsIterator) != 0; }
2432 // The parameters for the block.
2434 public Parameters Parameters {
2435 get { return parameters; }
2438 public bool CompleteContexts (EmitContext ec)
2440 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2442 if (root_scope != null)
2443 root_scope.LinkScopes ();
2445 if (Parent == null && root_scope != null) {
2446 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2448 if (root_scope.DefineType () == null)
2450 if (!root_scope.ResolveType ())
2452 if (!root_scope.ResolveMembers ())
2454 if (!root_scope.DefineMembers ())
2461 public GenericMethod GenericMethod {
2462 get { return generic; }
2465 public ToplevelBlock Container {
2466 get { return Parent == null ? null : Parent.Toplevel; }
2469 public AnonymousContainer AnonymousContainer {
2470 get { return anonymous_container; }
2471 set { anonymous_container = value; }
2474 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2475 this (parent, (Flags) 0, parameters, start)
2479 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2480 this (parent, parameters, start)
2482 this.generic = generic;
2485 public ToplevelBlock (Parameters parameters, Location start) :
2486 this (null, (Flags) 0, parameters, start)
2490 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2491 this (null, flags, parameters, start)
2495 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2496 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2497 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2498 base (null, flags, start, Location.Null)
2500 this.Toplevel = this;
2502 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2503 this.Parent = parent;
2505 parent.AddAnonymousChild (this);
2507 if (this.parameters.Count != 0)
2508 ProcessParameters ();
2511 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2515 protected override void CloneTo (CloneContext clonectx, Statement t)
2517 ToplevelBlock target = (ToplevelBlock) t;
2518 base.CloneTo (clonectx, t);
2520 if (parameters.Count != 0)
2521 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2522 for (int i = 0; i < parameters.Count; ++i)
2523 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2526 public bool CheckError158 (string name, Location loc)
2528 if (AnonymousChildren != null) {
2529 foreach (ToplevelBlock child in AnonymousChildren) {
2530 if (!child.CheckError158 (name, loc))
2535 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2536 if (!c.DoCheckError158 (name, loc))
2543 public virtual Expression GetTransparentIdentifier (string name)
2548 void ProcessParameters ()
2550 int n = parameters.Count;
2551 parameter_info = new ToplevelParameterInfo [n];
2552 for (int i = 0; i < n; ++i) {
2553 parameter_info [i] = new ToplevelParameterInfo (this, i);
2555 Parameter p = parameters [i];
2559 string name = p.Name;
2560 LocalInfo vi = GetLocalInfo (name);
2562 Report.SymbolRelatedToPreviousError (vi.Location, name);
2563 Error_AlreadyDeclared (loc, name, "parent or current");
2567 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2569 Report.SymbolRelatedToPreviousError (pi.Location, name);
2570 Error_AlreadyDeclared (loc, name, "parent or current");
2574 AddKnownVariable (name, parameter_info [i]);
2577 // mark this block as "used" so that we create local declarations in a sub-block
2578 // FIXME: This appears to uncover a lot of bugs
2582 bool DoCheckError158 (string name, Location loc)
2584 LabeledStatement s = LookupLabel (name);
2586 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2587 Error_158 (name, loc);
2594 public RootScopeInfo CreateRootScope (TypeContainer host)
2596 if (root_scope != null)
2599 if (Container == null)
2600 root_scope = new RootScopeInfo (
2601 this, host, generic, StartLocation);
2603 if (scope_info != null)
2604 throw new InternalErrorException ();
2606 scope_info = root_scope;
2610 public void CreateIteratorHost (RootScopeInfo root)
2612 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2614 if (Parent != null || root_scope != null)
2615 throw new InternalErrorException ();
2617 scope_info = root_scope = root;
2620 public RootScopeInfo RootScope {
2622 if (root_scope != null)
2624 else if (Container != null)
2625 return Container.RootScope;
2631 public FlowBranchingToplevel TopLevelBranching {
2632 get { return top_level_branching; }
2636 // This is used if anonymous methods are used inside an iterator
2637 // (see 2test-22.cs for an example).
2639 // The AnonymousMethod is created while parsing - at a time when we don't
2640 // know yet that we're inside an iterator, so it's `Container' is initially
2641 // null. Later on, when resolving the iterator, we need to move the
2642 // anonymous method into that iterator.
2644 public void ReParent (ToplevelBlock new_parent)
2646 if ((flags & Flags.VariablesInitialized) != 0)
2647 throw new InternalErrorException ("block has already been resolved");
2649 Parent = new_parent;
2653 // Returns a `ParameterReference' for the given name, or null if there
2654 // is no such parameter
2656 public ParameterReference GetParameterReference (string name, Location loc)
2658 ToplevelParameterInfo p = GetParameterInfo (name);
2659 return p == null ? null : new ParameterReference (this, p, loc);
2662 public ToplevelParameterInfo GetParameterInfo (string name)
2665 for (ToplevelBlock t = this; t != null; t = t.Container) {
2666 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2668 return t.parameter_info [idx];
2673 LocalInfo this_variable = null;
2676 // Returns the "this" instance variable of this block.
2677 // See AddThisVariable() for more information.
2679 public LocalInfo ThisVariable {
2680 get { return this_variable; }
2685 // This is used by non-static `struct' constructors which do not have an
2686 // initializer - in this case, the constructor must initialize all of the
2687 // struct's fields. To do this, we add a "this" variable and use the flow
2688 // analysis code to ensure that it's been fully initialized before control
2689 // leaves the constructor.
2691 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2693 if (this_variable == null) {
2694 this_variable = new LocalInfo (ds, this, l);
2695 this_variable.Used = true;
2696 this_variable.IsThis = true;
2698 Variables.Add ("this", this_variable);
2701 return this_variable;
2704 public bool IsThisAssigned (EmitContext ec)
2706 return this_variable == null || this_variable.IsThisAssigned (ec);
2709 public bool ResolveMeta (EmitContext ec, Parameters ip)
2711 int errors = Report.Errors;
2712 int orig_count = parameters.Count;
2714 if (top_level_branching != null)
2720 // Assert: orig_count != parameter.Count => orig_count == 0
2721 if (orig_count != 0 && orig_count != parameters.Count)
2722 throw new InternalErrorException ("parameter information mismatch");
2724 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2726 for (int i = 0; i < orig_count; ++i) {
2727 Parameter.Modifier mod = parameters.ParameterModifier (i);
2729 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2732 VariableInfo vi = new VariableInfo (ip, i, offset);
2733 parameter_info [i].VariableInfo = vi;
2734 offset += vi.Length;
2737 ResolveMeta (ec, offset);
2739 top_level_branching = ec.StartFlowBranching (this);
2741 return Report.Errors == errors;
2745 // Check whether all `out' parameters have been assigned.
2747 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2749 if (vector.IsUnreachable)
2752 int n = parameter_info == null ? 0 : parameter_info.Length;
2754 for (int i = 0; i < n; i++) {
2755 VariableInfo var = parameter_info [i].VariableInfo;
2760 if (vector.IsAssigned (var, false))
2763 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2768 public override void EmitMeta (EmitContext ec)
2771 parameters.ResolveVariable (this);
2774 public void MakeIterator (Iterator iterator)
2776 flags |= Flags.IsIterator;
2778 Block block = new ExplicitBlock (this, StartLocation, EndLocation);
2779 foreach (Statement stmt in statements)
2780 block.AddStatement (stmt);
2781 statements.Clear ();
2782 statements.Add (new MoveNextStatement (iterator, block));
2785 protected class MoveNextStatement : Statement {
2789 public MoveNextStatement (Iterator iterator, Block block)
2791 this.iterator = iterator;
2793 this.loc = iterator.Location;
2796 public override bool Resolve (EmitContext ec)
2798 return block.Resolve (ec);
2801 protected override void DoEmit (EmitContext ec)
2803 iterator.EmitMoveNext (ec, block);
2807 public override string ToString ()
2809 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2810 root_scope, anonymous_container != null ?
2811 anonymous_container.Scope : null);
2815 public class SwitchLabel {
2822 Label il_label_code;
2823 bool il_label_code_set;
2825 public static readonly object NullStringCase = new object ();
2828 // if expr == null, then it is the default case.
2830 public SwitchLabel (Expression expr, Location l)
2836 public Expression Label {
2842 public object Converted {
2848 public Label GetILLabel (EmitContext ec)
2851 il_label = ec.ig.DefineLabel ();
2852 il_label_set = true;
2857 public Label GetILLabelCode (EmitContext ec)
2859 if (!il_label_code_set){
2860 il_label_code = ec.ig.DefineLabel ();
2861 il_label_code_set = true;
2863 return il_label_code;
2867 // Resolves the expression, reduces it to a literal if possible
2868 // and then converts it to the requested type.
2870 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2872 Expression e = label.Resolve (ec);
2877 Constant c = e as Constant;
2879 Report.Error (150, loc, "A constant value is expected");
2883 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2884 converted = NullStringCase;
2888 if (allow_nullable && c.GetValue () == null) {
2889 converted = NullStringCase;
2893 c = c.ImplicitConversionRequired (required_type, loc);
2897 converted = c.GetValue ();
2901 public void Erorr_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2904 if (converted == null)
2906 else if (converted == NullStringCase)
2908 else if (TypeManager.IsEnumType (switch_type))
2909 label = TypeManager.CSharpEnumValue (switch_type, converted);
2911 label = converted.ToString ();
2913 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2914 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2917 public SwitchLabel Clone (CloneContext clonectx)
2919 return new SwitchLabel (label.Clone (clonectx), loc);
2923 public class SwitchSection {
2924 // An array of SwitchLabels.
2925 public readonly ArrayList Labels;
2926 public readonly Block Block;
2928 public SwitchSection (ArrayList labels, Block block)
2934 public SwitchSection Clone (CloneContext clonectx)
2936 ArrayList cloned_labels = new ArrayList ();
2938 foreach (SwitchLabel sl in cloned_labels)
2939 cloned_labels.Add (sl.Clone (clonectx));
2941 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
2945 public class Switch : Statement {
2946 public ArrayList Sections;
2947 public Expression Expr;
2950 /// Maps constants whose type type SwitchType to their SwitchLabels.
2952 public IDictionary Elements;
2955 /// The governing switch type
2957 public Type SwitchType;
2962 Label default_target;
2964 Expression new_expr;
2966 SwitchSection constant_section;
2967 SwitchSection default_section;
2971 // Nullable Types support for GMCS.
2973 Nullable.Unwrap unwrap;
2975 protected bool HaveUnwrap {
2976 get { return unwrap != null; }
2979 protected bool HaveUnwrap {
2980 get { return false; }
2985 // The types allowed to be implicitly cast from
2986 // on the governing type
2988 static Type [] allowed_types;
2990 public Switch (Expression e, ArrayList sects, Location l)
2997 public bool GotDefault {
2999 return default_section != null;
3003 public Label DefaultTarget {
3005 return default_target;
3010 // Determines the governing type for a switch. The returned
3011 // expression might be the expression from the switch, or an
3012 // expression that includes any potential conversions to the
3013 // integral types or to string.
3015 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3017 Type t = TypeManager.DropGenericTypeArguments (expr.Type);
3019 if (t == TypeManager.byte_type ||
3020 t == TypeManager.sbyte_type ||
3021 t == TypeManager.ushort_type ||
3022 t == TypeManager.short_type ||
3023 t == TypeManager.uint32_type ||
3024 t == TypeManager.int32_type ||
3025 t == TypeManager.uint64_type ||
3026 t == TypeManager.int64_type ||
3027 t == TypeManager.char_type ||
3028 t == TypeManager.string_type ||
3029 t == TypeManager.bool_type ||
3030 t.IsSubclassOf (TypeManager.enum_type))
3033 if (allowed_types == null){
3034 allowed_types = new Type [] {
3035 TypeManager.sbyte_type,
3036 TypeManager.byte_type,
3037 TypeManager.short_type,
3038 TypeManager.ushort_type,
3039 TypeManager.int32_type,
3040 TypeManager.uint32_type,
3041 TypeManager.int64_type,
3042 TypeManager.uint64_type,
3043 TypeManager.char_type,
3044 TypeManager.string_type,
3045 TypeManager.bool_type
3050 // Try to find a *user* defined implicit conversion.
3052 // If there is no implicit conversion, or if there are multiple
3053 // conversions, we have to report an error
3055 Expression converted = null;
3056 foreach (Type tt in allowed_types){
3059 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3064 // Ignore over-worked ImplicitUserConversions that do
3065 // an implicit conversion in addition to the user conversion.
3067 if (!(e is UserCast))
3070 if (converted != null){
3071 Report.ExtraInformation (
3073 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3074 TypeManager.CSharpName (expr.Type)));
3084 // Performs the basic sanity checks on the switch statement
3085 // (looks for duplicate keys and non-constant expressions).
3087 // It also returns a hashtable with the keys that we will later
3088 // use to compute the switch tables
3090 bool CheckSwitch (EmitContext ec)
3093 Elements = Sections.Count > 10 ?
3094 (IDictionary)new Hashtable () :
3095 (IDictionary)new ListDictionary ();
3097 foreach (SwitchSection ss in Sections){
3098 foreach (SwitchLabel sl in ss.Labels){
3099 if (sl.Label == null){
3100 if (default_section != null){
3101 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3104 default_section = ss;
3108 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3113 object key = sl.Converted;
3115 Elements.Add (key, sl);
3116 } catch (ArgumentException) {
3117 sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3125 void EmitObjectInteger (ILGenerator ig, object k)
3128 IntConstant.EmitInt (ig, (int) k);
3129 else if (k is Constant) {
3130 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3133 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3136 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3138 IntConstant.EmitInt (ig, (int) (long) k);
3139 ig.Emit (OpCodes.Conv_I8);
3142 LongConstant.EmitLong (ig, (long) k);
3144 else if (k is ulong)
3146 ulong ul = (ulong) k;
3149 IntConstant.EmitInt (ig, unchecked ((int) ul));
3150 ig.Emit (OpCodes.Conv_U8);
3154 LongConstant.EmitLong (ig, unchecked ((long) ul));
3158 IntConstant.EmitInt (ig, (int) ((char) k));
3159 else if (k is sbyte)
3160 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3162 IntConstant.EmitInt (ig, (int) ((byte) k));
3163 else if (k is short)
3164 IntConstant.EmitInt (ig, (int) ((short) k));
3165 else if (k is ushort)
3166 IntConstant.EmitInt (ig, (int) ((ushort) k));
3168 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3170 throw new Exception ("Unhandled case");
3173 // structure used to hold blocks of keys while calculating table switch
3174 class KeyBlock : IComparable
3176 public KeyBlock (long _first)
3178 first = last = _first;
3182 public ArrayList element_keys = null;
3183 // how many items are in the bucket
3184 public int Size = 1;
3187 get { return (int) (last - first + 1); }
3189 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3191 return kb_last.last - kb_first.first + 1;
3193 public int CompareTo (object obj)
3195 KeyBlock kb = (KeyBlock) obj;
3196 int nLength = Length;
3197 int nLengthOther = kb.Length;
3198 if (nLengthOther == nLength)
3199 return (int) (kb.first - first);
3200 return nLength - nLengthOther;
3205 /// This method emits code for a lookup-based switch statement (non-string)
3206 /// Basically it groups the cases into blocks that are at least half full,
3207 /// and then spits out individual lookup opcodes for each block.
3208 /// It emits the longest blocks first, and short blocks are just
3209 /// handled with direct compares.
3211 /// <param name="ec"></param>
3212 /// <param name="val"></param>
3213 /// <returns></returns>
3214 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3216 int element_count = Elements.Count;
3217 object [] element_keys = new object [element_count];
3218 Elements.Keys.CopyTo (element_keys, 0);
3219 Array.Sort (element_keys);
3221 // initialize the block list with one element per key
3222 ArrayList key_blocks = new ArrayList ();
3223 foreach (object key in element_keys)
3224 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3226 KeyBlock current_kb;
3227 // iteratively merge the blocks while they are at least half full
3228 // there's probably a really cool way to do this with a tree...
3229 while (key_blocks.Count > 1)
3231 ArrayList key_blocks_new = new ArrayList ();
3232 current_kb = (KeyBlock) key_blocks [0];
3233 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3235 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3236 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3239 current_kb.last = kb.last;
3240 current_kb.Size += kb.Size;
3244 // start a new block
3245 key_blocks_new.Add (current_kb);
3249 key_blocks_new.Add (current_kb);
3250 if (key_blocks.Count == key_blocks_new.Count)
3252 key_blocks = key_blocks_new;
3255 // initialize the key lists
3256 foreach (KeyBlock kb in key_blocks)
3257 kb.element_keys = new ArrayList ();
3259 // fill the key lists
3261 if (key_blocks.Count > 0) {
3262 current_kb = (KeyBlock) key_blocks [0];
3263 foreach (object key in element_keys)
3265 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3266 System.Convert.ToInt64 (key) > current_kb.last;
3268 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3269 current_kb.element_keys.Add (key);
3273 // sort the blocks so we can tackle the largest ones first
3276 // okay now we can start...
3277 ILGenerator ig = ec.ig;
3278 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3279 Label lbl_default = ig.DefineLabel ();
3281 Type type_keys = null;
3282 if (element_keys.Length > 0)
3283 type_keys = element_keys [0].GetType (); // used for conversions
3287 if (TypeManager.IsEnumType (SwitchType))
3288 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3290 compare_type = SwitchType;
3292 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3294 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3295 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3298 foreach (object key in kb.element_keys)
3300 ig.Emit (OpCodes.Ldloc, val);
3301 EmitObjectInteger (ig, key);
3302 SwitchLabel sl = (SwitchLabel) Elements [key];
3303 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3308 // TODO: if all the keys in the block are the same and there are
3309 // no gaps/defaults then just use a range-check.
3310 if (compare_type == TypeManager.int64_type ||
3311 compare_type == TypeManager.uint64_type)
3313 // TODO: optimize constant/I4 cases
3315 // check block range (could be > 2^31)
3316 ig.Emit (OpCodes.Ldloc, val);
3317 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3318 ig.Emit (OpCodes.Blt, lbl_default);
3319 ig.Emit (OpCodes.Ldloc, val);
3320 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3321 ig.Emit (OpCodes.Bgt, lbl_default);
3324 ig.Emit (OpCodes.Ldloc, val);
3327 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3328 ig.Emit (OpCodes.Sub);
3330 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3335 ig.Emit (OpCodes.Ldloc, val);
3336 int first = (int) kb.first;
3339 IntConstant.EmitInt (ig, first);
3340 ig.Emit (OpCodes.Sub);
3344 IntConstant.EmitInt (ig, -first);
3345 ig.Emit (OpCodes.Add);
3349 // first, build the list of labels for the switch
3351 int cJumps = kb.Length;
3352 Label [] switch_labels = new Label [cJumps];
3353 for (int iJump = 0; iJump < cJumps; iJump++)
3355 object key = kb.element_keys [iKey];
3356 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3358 SwitchLabel sl = (SwitchLabel) Elements [key];
3359 switch_labels [iJump] = sl.GetILLabel (ec);
3363 switch_labels [iJump] = lbl_default;
3365 // emit the switch opcode
3366 ig.Emit (OpCodes.Switch, switch_labels);
3369 // mark the default for this block
3371 ig.MarkLabel (lbl_default);
3374 // TODO: find the default case and emit it here,
3375 // to prevent having to do the following jump.
3376 // make sure to mark other labels in the default section
3378 // the last default just goes to the end
3379 ig.Emit (OpCodes.Br, lbl_default);
3381 // now emit the code for the sections
3382 bool found_default = false;
3383 bool found_null = false;
3384 foreach (SwitchSection ss in Sections)
3386 foreach (SwitchLabel sl in ss.Labels)
3387 if (sl.Converted == SwitchLabel.NullStringCase)
3391 foreach (SwitchSection ss in Sections)
3393 foreach (SwitchLabel sl in ss.Labels)
3395 ig.MarkLabel (sl.GetILLabel (ec));
3396 ig.MarkLabel (sl.GetILLabelCode (ec));
3397 if (sl.Converted == SwitchLabel.NullStringCase)
3398 ig.MarkLabel (null_target);
3399 else if (sl.Label == null) {
3400 ig.MarkLabel (lbl_default);
3401 found_default = true;
3403 ig.MarkLabel (null_target);
3409 if (!found_default) {
3410 ig.MarkLabel (lbl_default);
3411 if (HaveUnwrap && !found_null) {
3412 ig.MarkLabel (null_target);
3416 ig.MarkLabel (lbl_end);
3419 // This simple emit switch works, but does not take advantage of the
3421 // TODO: remove non-string logic from here
3422 // TODO: binary search strings?
3424 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3426 ILGenerator ig = ec.ig;
3427 Label end_of_switch = ig.DefineLabel ();
3428 Label next_test = ig.DefineLabel ();
3429 bool first_test = true;
3430 bool pending_goto_end = false;
3431 bool null_marked = false;
3433 int section_count = Sections.Count;
3435 // TODO: implement switch optimization for string by using Hashtable
3436 //if (SwitchType == TypeManager.string_type && section_count > 7)
3437 // Console.WriteLine ("Switch optimization possible " + loc);
3439 ig.Emit (OpCodes.Ldloc, val);
3441 if (Elements.Contains (SwitchLabel.NullStringCase)){
3442 ig.Emit (OpCodes.Brfalse, null_target);
3444 ig.Emit (OpCodes.Brfalse, default_target);
3446 ig.Emit (OpCodes.Ldloc, val);
3447 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3448 ig.Emit (OpCodes.Stloc, val);
3450 for (int section = 0; section < section_count; section++){
3451 SwitchSection ss = (SwitchSection) Sections [section];
3453 if (ss == default_section)
3456 Label sec_begin = ig.DefineLabel ();
3458 ig.Emit (OpCodes.Nop);
3460 if (pending_goto_end)
3461 ig.Emit (OpCodes.Br, end_of_switch);
3463 int label_count = ss.Labels.Count;
3465 for (int label = 0; label < label_count; label++){
3466 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3467 ig.MarkLabel (sl.GetILLabel (ec));
3470 ig.MarkLabel (next_test);
3471 next_test = ig.DefineLabel ();
3474 // If we are the default target
3476 if (sl.Label != null){
3477 object lit = sl.Converted;
3479 if (lit == SwitchLabel.NullStringCase){
3481 if (label + 1 == label_count)
3482 ig.Emit (OpCodes.Br, next_test);
3486 ig.Emit (OpCodes.Ldloc, val);
3487 ig.Emit (OpCodes.Ldstr, (string)lit);
3488 if (label_count == 1)
3489 ig.Emit (OpCodes.Bne_Un, next_test);
3491 if (label+1 == label_count)
3492 ig.Emit (OpCodes.Bne_Un, next_test);
3494 ig.Emit (OpCodes.Beq, sec_begin);
3499 ig.MarkLabel (null_target);
3502 ig.MarkLabel (sec_begin);
3503 foreach (SwitchLabel sl in ss.Labels)
3504 ig.MarkLabel (sl.GetILLabelCode (ec));
3507 pending_goto_end = !ss.Block.HasRet;
3510 ig.MarkLabel (next_test);
3511 ig.MarkLabel (default_target);
3513 ig.MarkLabel (null_target);
3514 if (default_section != null)
3515 default_section.Block.Emit (ec);
3516 ig.MarkLabel (end_of_switch);
3519 SwitchSection FindSection (SwitchLabel label)
3521 foreach (SwitchSection ss in Sections){
3522 foreach (SwitchLabel sl in ss.Labels){
3531 public override bool Resolve (EmitContext ec)
3533 Expr = Expr.Resolve (ec);
3537 new_expr = SwitchGoverningType (ec, Expr);
3540 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3541 unwrap = Nullable.Unwrap.Create (Expr, ec);
3545 new_expr = SwitchGoverningType (ec, unwrap);
3549 if (new_expr == null){
3550 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3555 SwitchType = new_expr.Type;
3557 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3558 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3562 if (!CheckSwitch (ec))
3566 Elements.Remove (SwitchLabel.NullStringCase);
3568 Switch old_switch = ec.Switch;
3570 ec.Switch.SwitchType = SwitchType;
3572 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3573 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3575 is_constant = new_expr is Constant;
3577 object key = ((Constant) new_expr).GetValue ();
3578 SwitchLabel label = (SwitchLabel) Elements [key];
3580 constant_section = FindSection (label);
3581 if (constant_section == null)
3582 constant_section = default_section;
3587 foreach (SwitchSection ss in Sections){
3589 ec.CurrentBranching.CreateSibling (
3590 null, FlowBranching.SiblingType.SwitchSection);
3594 if (is_constant && (ss != constant_section)) {
3595 // If we're a constant switch, we're only emitting
3596 // one single section - mark all the others as
3598 ec.CurrentBranching.CurrentUsageVector.Goto ();
3599 if (!ss.Block.ResolveUnreachable (ec, true)) {
3603 if (!ss.Block.Resolve (ec))
3608 if (default_section == null)
3609 ec.CurrentBranching.CreateSibling (
3610 null, FlowBranching.SiblingType.SwitchSection);
3612 ec.EndFlowBranching ();
3613 ec.Switch = old_switch;
3615 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3620 protected override void DoEmit (EmitContext ec)
3622 ILGenerator ig = ec.ig;
3624 default_target = ig.DefineLabel ();
3625 null_target = ig.DefineLabel ();
3627 // Store variable for comparission purposes
3630 value = ig.DeclareLocal (SwitchType);
3632 unwrap.EmitCheck (ec);
3633 ig.Emit (OpCodes.Brfalse, null_target);
3635 ig.Emit (OpCodes.Stloc, value);
3637 } else if (!is_constant) {
3638 value = ig.DeclareLocal (SwitchType);
3640 ig.Emit (OpCodes.Stloc, value);
3645 // Setup the codegen context
3647 Label old_end = ec.LoopEnd;
3648 Switch old_switch = ec.Switch;
3650 ec.LoopEnd = ig.DefineLabel ();
3655 if (constant_section != null)
3656 constant_section.Block.Emit (ec);
3657 } else if (SwitchType == TypeManager.string_type)
3658 SimpleSwitchEmit (ec, value);
3660 TableSwitchEmit (ec, value);
3662 // Restore context state.
3663 ig.MarkLabel (ec.LoopEnd);
3666 // Restore the previous context
3668 ec.LoopEnd = old_end;
3669 ec.Switch = old_switch;
3672 protected override void CloneTo (CloneContext clonectx, Statement t)
3674 Switch target = (Switch) t;
3676 target.Expr = Expr.Clone (clonectx);
3677 target.Sections = new ArrayList ();
3678 foreach (SwitchSection ss in Sections){
3679 target.Sections.Add (ss.Clone (clonectx));
3684 public abstract class ExceptionStatement : Statement
3686 public abstract void EmitFinally (EmitContext ec);
3688 protected bool emit_finally = true;
3689 ArrayList parent_vectors;
3691 protected void DoEmitFinally (EmitContext ec)
3694 ec.ig.BeginFinallyBlock ();
3695 else if (ec.InIterator)
3696 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3700 protected void ResolveFinally (FlowBranchingException branching)
3702 emit_finally = branching.EmitFinally;
3704 branching.Parent.StealFinallyClauses (ref parent_vectors);
3708 public class Lock : ExceptionStatement {
3710 public Statement Statement;
3711 TemporaryVariable temp;
3713 public Lock (Expression expr, Statement stmt, Location l)
3720 public override bool Resolve (EmitContext ec)
3722 expr = expr.Resolve (ec);
3726 if (expr.Type.IsValueType){
3727 Report.Error (185, loc,
3728 "`{0}' is not a reference type as required by the lock statement",
3729 TypeManager.CSharpName (expr.Type));
3733 FlowBranchingException branching = ec.StartFlowBranching (this);
3734 bool ok = Statement.Resolve (ec);
3736 ResolveFinally (branching);
3738 ec.EndFlowBranching ();
3740 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3741 // So, ensure there's some IL code after the finally block.
3742 ec.NeedReturnLabel ();
3744 // Avoid creating libraries that reference the internal
3747 if (t == TypeManager.null_type)
3748 t = TypeManager.object_type;
3750 temp = new TemporaryVariable (t, loc);
3756 protected override void DoEmit (EmitContext ec)
3758 ILGenerator ig = ec.ig;
3760 temp.Store (ec, expr);
3762 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3766 ig.BeginExceptionBlock ();
3767 Statement.Emit (ec);
3772 ig.EndExceptionBlock ();
3775 public override void EmitFinally (EmitContext ec)
3778 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3781 protected override void CloneTo (CloneContext clonectx, Statement t)
3783 Lock target = (Lock) t;
3785 target.expr = expr.Clone (clonectx);
3786 target.Statement = Statement.Clone (clonectx);
3790 public class Unchecked : Statement {
3793 public Unchecked (Block b)
3799 public override bool Resolve (EmitContext ec)
3801 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3802 return Block.Resolve (ec);
3805 protected override void DoEmit (EmitContext ec)
3807 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3811 protected override void CloneTo (CloneContext clonectx, Statement t)
3813 Unchecked target = (Unchecked) t;
3815 target.Block = clonectx.LookupBlock (Block);
3819 public class Checked : Statement {
3822 public Checked (Block b)
3825 b.Unchecked = false;
3828 public override bool Resolve (EmitContext ec)
3830 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3831 return Block.Resolve (ec);
3834 protected override void DoEmit (EmitContext ec)
3836 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3840 protected override void CloneTo (CloneContext clonectx, Statement t)
3842 Checked target = (Checked) t;
3844 target.Block = clonectx.LookupBlock (Block);
3848 public class Unsafe : Statement {
3851 public Unsafe (Block b)
3854 Block.Unsafe = true;
3857 public override bool Resolve (EmitContext ec)
3859 using (ec.With (EmitContext.Flags.InUnsafe, true))
3860 return Block.Resolve (ec);
3863 protected override void DoEmit (EmitContext ec)
3865 using (ec.With (EmitContext.Flags.InUnsafe, true))
3868 protected override void CloneTo (CloneContext clonectx, Statement t)
3870 Unsafe target = (Unsafe) t;
3872 target.Block = clonectx.LookupBlock (Block);
3879 public class Fixed : Statement {
3881 ArrayList declarators;
3882 Statement statement;
3887 abstract class Emitter
3889 protected LocalInfo vi;
3890 protected Expression converted;
3892 protected Emitter (Expression expr, LocalInfo li)
3898 public abstract void Emit (EmitContext ec);
3899 public abstract void EmitExit (EmitContext ec);
3902 class ExpressionEmitter : Emitter {
3903 public ExpressionEmitter (Expression converted, LocalInfo li) :
3904 base (converted, li)
3908 public override void Emit (EmitContext ec) {
3910 // Store pointer in pinned location
3912 converted.Emit (ec);
3913 vi.Variable.EmitAssign (ec);
3916 public override void EmitExit (EmitContext ec)
3918 ec.ig.Emit (OpCodes.Ldc_I4_0);
3919 ec.ig.Emit (OpCodes.Conv_U);
3920 vi.Variable.EmitAssign (ec);
3924 class StringEmitter : Emitter {
3925 LocalBuilder pinned_string;
3928 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3934 public override void Emit (EmitContext ec)
3936 ILGenerator ig = ec.ig;
3937 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3939 converted.Emit (ec);
3940 ig.Emit (OpCodes.Stloc, pinned_string);
3942 Expression sptr = new StringPtr (pinned_string, loc);
3943 converted = Convert.ImplicitConversionRequired (
3944 ec, sptr, vi.VariableType, loc);
3946 if (converted == null)
3949 converted.Emit (ec);
3950 vi.Variable.EmitAssign (ec);
3953 public override void EmitExit (EmitContext ec)
3955 ec.ig.Emit (OpCodes.Ldnull);
3956 ec.ig.Emit (OpCodes.Stloc, pinned_string);
3960 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3963 declarators = decls;
3968 public Statement Statement {
3969 get { return statement; }
3972 public override bool Resolve (EmitContext ec)
3975 Expression.UnsafeError (loc);
3979 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
3980 if (texpr == null) {
3981 if (type is VarExpr)
3982 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
3987 expr_type = texpr.Type;
3989 data = new Emitter [declarators.Count];
3991 if (!expr_type.IsPointer){
3992 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
3997 foreach (Pair p in declarators){
3998 LocalInfo vi = (LocalInfo) p.First;
3999 Expression e = (Expression) p.Second;
4001 vi.VariableInfo.SetAssigned (ec);
4002 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4005 // The rules for the possible declarators are pretty wise,
4006 // but the production on the grammar is more concise.
4008 // So we have to enforce these rules here.
4010 // We do not resolve before doing the case 1 test,
4011 // because the grammar is explicit in that the token &
4012 // is present, so we need to test for this particular case.
4016 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4021 // Case 1: & object.
4023 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4024 Expression child = ((Unary) e).Expr;
4026 if (child is ParameterReference || child is LocalVariableReference){
4029 "No need to use fixed statement for parameters or " +
4030 "local variable declarations (address is already " +
4035 ec.InFixedInitializer = true;
4037 ec.InFixedInitializer = false;
4041 child = ((Unary) e).Expr;
4043 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4046 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4047 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4051 data [i] = new ExpressionEmitter (e, vi);
4057 ec.InFixedInitializer = true;
4059 ec.InFixedInitializer = false;
4066 if (e.Type.IsArray){
4067 Type array_type = TypeManager.GetElementType (e.Type);
4070 // Provided that array_type is unmanaged,
4072 if (!TypeManager.VerifyUnManaged (array_type, loc))
4076 // and T* is implicitly convertible to the
4077 // pointer type given in the fixed statement.
4079 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4081 Expression converted = Convert.ImplicitConversionRequired (
4082 ec, array_ptr, vi.VariableType, loc);
4083 if (converted == null)
4087 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4089 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4090 new Binary (Binary.Operator.Equality, e, new NullConstant (loc)),
4091 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4095 converted = converted.Resolve (ec);
4097 data [i] = new ExpressionEmitter (converted, vi);
4106 if (e.Type == TypeManager.string_type){
4107 data [i] = new StringEmitter (e, vi, loc);
4112 // Case 4: fixed buffer
4113 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4114 if (fixed_buffer_ptr != null) {
4115 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4120 // For other cases, flag a `this is already fixed expression'
4122 if (e is LocalVariableReference || e is ParameterReference ||
4123 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4125 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4129 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4133 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4134 bool ok = statement.Resolve (ec);
4135 bool flow_unreachable = ec.EndFlowBranching ();
4136 has_ret = flow_unreachable;
4141 protected override void DoEmit (EmitContext ec)
4143 for (int i = 0; i < data.Length; i++) {
4147 statement.Emit (ec);
4153 // Clear the pinned variable
4155 for (int i = 0; i < data.Length; i++) {
4156 data [i].EmitExit (ec);
4160 protected override void CloneTo (CloneContext clonectx, Statement t)
4162 Fixed target = (Fixed) t;
4164 target.type = type.Clone (clonectx);
4165 target.declarators = new ArrayList (declarators.Count);
4166 foreach (Pair p in declarators) {
4167 LocalInfo vi = (LocalInfo) p.First;
4168 Expression e = (Expression) p.Second;
4170 target.declarators.Add (
4171 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4174 target.statement = statement.Clone (clonectx);
4178 public class Catch : Statement {
4179 public readonly string Name;
4181 public Block VarBlock;
4183 Expression type_expr;
4186 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4191 VarBlock = var_block;
4195 public Type CatchType {
4201 public bool IsGeneral {
4203 return type_expr == null;
4207 protected override void DoEmit(EmitContext ec)
4209 ILGenerator ig = ec.ig;
4211 if (CatchType != null)
4212 ig.BeginCatchBlock (CatchType);
4214 ig.BeginCatchBlock (TypeManager.object_type);
4216 if (VarBlock != null)
4220 LocalInfo vi = Block.GetLocalInfo (Name);
4222 throw new Exception ("Variable does not exist in this block");
4224 if (vi.Variable.NeedsTemporary) {
4225 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4226 ig.Emit (OpCodes.Stloc, e);
4228 vi.Variable.EmitInstance (ec);
4229 ig.Emit (OpCodes.Ldloc, e);
4230 vi.Variable.EmitAssign (ec);
4232 vi.Variable.EmitAssign (ec);
4234 ig.Emit (OpCodes.Pop);
4239 public override bool Resolve (EmitContext ec)
4241 using (ec.With (EmitContext.Flags.InCatch, true)) {
4242 if (type_expr != null) {
4243 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4249 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4250 Error (155, "The type caught or thrown must be derived from System.Exception");
4256 if (!Block.Resolve (ec))
4259 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4260 // emit the "unused variable" warnings.
4261 if (VarBlock != null)
4262 return VarBlock.Resolve (ec);
4268 protected override void CloneTo (CloneContext clonectx, Statement t)
4270 Catch target = (Catch) t;
4272 if (type_expr != null)
4273 target.type_expr = type_expr.Clone (clonectx);
4274 if (VarBlock != null)
4275 target.VarBlock = clonectx.LookupBlock (VarBlock);
4276 target.Block = clonectx.LookupBlock (Block);
4280 public class Try : ExceptionStatement {
4281 public Block Fini, Block;
4282 public ArrayList Specific;
4283 public Catch General;
4285 bool need_exc_block;
4288 // specific, general and fini might all be null.
4290 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4292 if (specific == null && general == null){
4293 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4297 this.Specific = specific;
4298 this.General = general;
4303 public override bool Resolve (EmitContext ec)
4307 FlowBranchingException branching = ec.StartFlowBranching (this);
4309 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4311 if (!Block.Resolve (ec))
4314 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4316 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4318 Type[] prev_catches = new Type [Specific.Count];
4320 foreach (Catch c in Specific){
4321 ec.CurrentBranching.CreateSibling (
4322 c.Block, FlowBranching.SiblingType.Catch);
4324 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4326 if (c.Name != null) {
4327 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4329 throw new Exception ();
4331 vi.VariableInfo = null;
4334 if (!c.Resolve (ec))
4337 Type resolved_type = c.CatchType;
4338 for (int ii = 0; ii < last_index; ++ii) {
4339 if (resolved_type == prev_catches [ii] || resolved_type.IsSubclassOf (prev_catches [ii])) {
4340 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4345 prev_catches [last_index++] = resolved_type;
4346 need_exc_block = true;
4349 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4351 if (General != null){
4352 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4353 foreach (Catch c in Specific){
4354 if (c.CatchType == TypeManager.exception_type) {
4355 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'");
4360 ec.CurrentBranching.CreateSibling (
4361 General.Block, FlowBranching.SiblingType.Catch);
4363 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4365 if (!General.Resolve (ec))
4368 need_exc_block = true;
4371 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4375 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4377 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4378 using (ec.With (EmitContext.Flags.InFinally, true)) {
4379 if (!Fini.Resolve (ec))
4384 need_exc_block = true;
4387 if (ec.InIterator) {
4388 ResolveFinally (branching);
4389 need_exc_block |= emit_finally;
4391 emit_finally = Fini != null;
4393 ec.EndFlowBranching ();
4395 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4396 // So, ensure there's some IL code after the finally block.
4397 ec.NeedReturnLabel ();
4399 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4401 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4406 protected override void DoEmit (EmitContext ec)
4408 ILGenerator ig = ec.ig;
4411 ig.BeginExceptionBlock ();
4414 foreach (Catch c in Specific)
4417 if (General != null)
4422 ig.EndExceptionBlock ();
4425 public override void EmitFinally (EmitContext ec)
4431 public bool HasCatch
4434 return General != null || Specific.Count > 0;
4438 protected override void CloneTo (CloneContext clonectx, Statement t)
4440 Try target = (Try) t;
4442 target.Block = clonectx.LookupBlock (Block);
4444 target.Fini = clonectx.LookupBlock (Fini);
4445 if (General != null)
4446 target.General = (Catch) General.Clone (clonectx);
4447 if (Specific != null){
4448 target.Specific = new ArrayList ();
4449 foreach (Catch c in Specific)
4450 target.Specific.Add (c.Clone (clonectx));
4455 public class Using : ExceptionStatement {
4456 object expression_or_block;
4457 public Statement Statement;
4461 Expression [] resolved_vars;
4462 Expression [] converted_vars;
4463 Expression [] assign;
4464 TemporaryVariable local_copy;
4466 public Using (object expression_or_block, Statement stmt, Location l)
4468 this.expression_or_block = expression_or_block;
4474 // Resolves for the case of using using a local variable declaration.
4476 bool ResolveLocalVariableDecls (EmitContext ec)
4478 resolved_vars = new Expression[var_list.Count];
4479 assign = new Expression [var_list.Count];
4480 converted_vars = new Expression[var_list.Count];
4482 for (int i = 0; i < assign.Length; ++i) {
4483 DictionaryEntry e = (DictionaryEntry) var_list [i];
4484 Expression var = (Expression) e.Key;
4485 Expression new_expr = (Expression) e.Value;
4487 Expression a = new Assign (var, new_expr, loc);
4492 resolved_vars [i] = var;
4495 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4496 converted_vars [i] = var;
4500 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4502 Error_IsNotConvertibleToIDisposable (var);
4506 converted_vars [i] = a;
4512 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4514 Report.SymbolRelatedToPreviousError (expr.Type);
4515 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4516 expr.GetSignatureForError ());
4519 bool ResolveExpression (EmitContext ec)
4521 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4522 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4523 Error_IsNotConvertibleToIDisposable (expr);
4528 local_copy = new TemporaryVariable (expr_type, loc);
4529 local_copy.Resolve (ec);
4535 // Emits the code for the case of using using a local variable declaration.
4537 void EmitLocalVariableDecls (EmitContext ec)
4539 ILGenerator ig = ec.ig;
4542 for (i = 0; i < assign.Length; i++) {
4543 ExpressionStatement es = assign [i] as ExpressionStatement;
4546 es.EmitStatement (ec);
4548 assign [i].Emit (ec);
4549 ig.Emit (OpCodes.Pop);
4553 ig.BeginExceptionBlock ();
4555 Statement.Emit (ec);
4557 var_list.Reverse ();
4562 void EmitLocalVariableDeclFinally (EmitContext ec)
4564 ILGenerator ig = ec.ig;
4566 int i = assign.Length;
4567 for (int ii = 0; ii < var_list.Count; ++ii){
4568 Expression var = resolved_vars [--i];
4569 Label skip = ig.DefineLabel ();
4572 ig.BeginFinallyBlock ();
4574 if (!var.Type.IsValueType) {
4576 ig.Emit (OpCodes.Brfalse, skip);
4577 converted_vars [i].Emit (ec);
4578 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4580 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4582 if (!(ml is MethodGroupExpr)) {
4584 ig.Emit (OpCodes.Box, var.Type);
4585 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4587 MethodInfo mi = null;
4589 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4590 if (TypeManager.GetParameterData (mk).Count == 0) {
4597 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4601 IMemoryLocation mloc = (IMemoryLocation) var;
4603 mloc.AddressOf (ec, AddressOp.Load);
4604 ig.Emit (OpCodes.Call, mi);
4608 ig.MarkLabel (skip);
4611 ig.EndExceptionBlock ();
4613 ig.BeginFinallyBlock ();
4618 void EmitExpression (EmitContext ec)
4621 // Make a copy of the expression and operate on that.
4623 ILGenerator ig = ec.ig;
4625 local_copy.Store (ec, expr);
4628 ig.BeginExceptionBlock ();
4630 Statement.Emit (ec);
4634 ig.EndExceptionBlock ();
4637 void EmitExpressionFinally (EmitContext ec)
4639 ILGenerator ig = ec.ig;
4640 if (!expr_type.IsValueType) {
4641 Label skip = ig.DefineLabel ();
4642 local_copy.Emit (ec);
4643 ig.Emit (OpCodes.Brfalse, skip);
4644 local_copy.Emit (ec);
4645 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4646 ig.MarkLabel (skip);
4648 Expression ml = Expression.MemberLookup (
4649 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4650 "Dispose", Location.Null);
4652 if (!(ml is MethodGroupExpr)) {
4653 local_copy.Emit (ec);
4654 ig.Emit (OpCodes.Box, expr_type);
4655 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4657 MethodInfo mi = null;
4659 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4660 if (TypeManager.GetParameterData (mk).Count == 0) {
4667 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4671 local_copy.AddressOf (ec, AddressOp.Load);
4672 ig.Emit (OpCodes.Call, mi);
4677 public override bool Resolve (EmitContext ec)
4679 if (expression_or_block is DictionaryEntry){
4680 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4681 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4683 if (!ResolveLocalVariableDecls (ec))
4686 } else if (expression_or_block is Expression){
4687 expr = (Expression) expression_or_block;
4689 expr = expr.Resolve (ec);
4693 expr_type = expr.Type;
4695 if (!ResolveExpression (ec))
4699 FlowBranchingException branching = ec.StartFlowBranching (this);
4701 bool ok = Statement.Resolve (ec);
4703 ResolveFinally (branching);
4705 ec.EndFlowBranching ();
4707 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4708 // So, ensure there's some IL code after the finally block.
4709 ec.NeedReturnLabel ();
4714 protected override void DoEmit (EmitContext ec)
4716 if (expression_or_block is DictionaryEntry)
4717 EmitLocalVariableDecls (ec);
4718 else if (expression_or_block is Expression)
4719 EmitExpression (ec);
4722 public override void EmitFinally (EmitContext ec)
4724 if (expression_or_block is DictionaryEntry)
4725 EmitLocalVariableDeclFinally (ec);
4726 else if (expression_or_block is Expression)
4727 EmitExpressionFinally (ec);
4730 protected override void CloneTo (CloneContext clonectx, Statement t)
4732 Using target = (Using) t;
4734 if (expression_or_block is Expression)
4735 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4737 target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
4739 target.Statement = Statement.Clone (clonectx);
4744 /// Implementation of the foreach C# statement
4746 public class Foreach : Statement {
4748 Expression variable;
4750 Statement statement;
4752 CollectionForeach collection;
4754 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4755 Statement stmt, Location l)
4758 this.variable = var;
4764 public Statement Statement {
4765 get { return statement; }
4768 public override bool Resolve (EmitContext ec)
4770 expr = expr.Resolve (ec);
4775 Report.Error (186, loc, "Use of null is not valid in this context");
4779 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4780 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4781 expr.ExprClassName);
4786 // We need an instance variable. Not sure this is the best
4787 // way of doing this.
4789 // FIXME: When we implement propertyaccess, will those turn
4790 // out to return values in ExprClass? I think they should.
4792 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4793 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4794 collection.Error_Enumerator ();
4798 if (expr.Type.IsArray) {
4799 array = new ArrayForeach (type, variable, expr, statement, loc);
4800 return array.Resolve (ec);
4803 collection = new CollectionForeach (type, variable, expr, statement, loc);
4804 return collection.Resolve (ec);
4807 protected override void DoEmit (EmitContext ec)
4809 ILGenerator ig = ec.ig;
4811 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4812 ec.LoopBegin = ig.DefineLabel ();
4813 ec.LoopEnd = ig.DefineLabel ();
4815 if (collection != null)
4816 collection.Emit (ec);
4820 ec.LoopBegin = old_begin;
4821 ec.LoopEnd = old_end;
4824 protected class ArrayCounter : TemporaryVariable
4826 public ArrayCounter (Location loc)
4827 : base (TypeManager.int32_type, loc)
4830 public void Initialize (EmitContext ec)
4833 ec.ig.Emit (OpCodes.Ldc_I4_0);
4837 public void Increment (EmitContext ec)
4841 ec.ig.Emit (OpCodes.Ldc_I4_1);
4842 ec.ig.Emit (OpCodes.Add);
4847 protected class ArrayForeach : Statement
4849 Expression variable, expr, conv;
4850 Statement statement;
4852 Expression var_type;
4853 TemporaryVariable[] lengths;
4854 ArrayCounter[] counter;
4857 TemporaryVariable copy;
4860 public ArrayForeach (Expression var_type, Expression var,
4861 Expression expr, Statement stmt, Location l)
4863 this.var_type = var_type;
4864 this.variable = var;
4870 public override bool Resolve (EmitContext ec)
4872 array_type = expr.Type;
4873 rank = array_type.GetArrayRank ();
4875 copy = new TemporaryVariable (array_type, loc);
4878 counter = new ArrayCounter [rank];
4879 lengths = new TemporaryVariable [rank];
4881 ArrayList list = new ArrayList ();
4882 for (int i = 0; i < rank; i++) {
4883 counter [i] = new ArrayCounter (loc);
4884 counter [i].Resolve (ec);
4886 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4887 lengths [i].Resolve (ec);
4889 list.Add (counter [i]);
4892 access = new ElementAccess (copy, list).Resolve (ec);
4896 VarExpr ve = var_type as VarExpr;
4898 // Infer implicitly typed local variable from foreach array type
4899 var_type = new TypeExpression (access.Type, ve.Location);
4902 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4903 if (var_type == null)
4906 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4912 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4913 ec.CurrentBranching.CreateSibling ();
4915 variable = variable.ResolveLValue (ec, conv, loc);
4916 if (variable == null)
4919 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4920 if (!statement.Resolve (ec))
4922 ec.EndFlowBranching ();
4924 // There's no direct control flow from the end of the embedded statement to the end of the loop
4925 ec.CurrentBranching.CurrentUsageVector.Goto ();
4927 ec.EndFlowBranching ();
4932 protected override void DoEmit (EmitContext ec)
4934 ILGenerator ig = ec.ig;
4936 copy.Store (ec, expr);
4938 Label[] test = new Label [rank];
4939 Label[] loop = new Label [rank];
4941 for (int i = 0; i < rank; i++) {
4942 test [i] = ig.DefineLabel ();
4943 loop [i] = ig.DefineLabel ();
4945 lengths [i].EmitThis (ec);
4946 ((ArrayAccess) access).EmitGetLength (ec, i);
4947 lengths [i].EmitStore (ec);
4950 for (int i = 0; i < rank; i++) {
4951 counter [i].Initialize (ec);
4953 ig.Emit (OpCodes.Br, test [i]);
4954 ig.MarkLabel (loop [i]);
4957 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4959 statement.Emit (ec);
4961 ig.MarkLabel (ec.LoopBegin);
4963 for (int i = rank - 1; i >= 0; i--){
4964 counter [i].Increment (ec);
4966 ig.MarkLabel (test [i]);
4967 counter [i].Emit (ec);
4968 lengths [i].Emit (ec);
4969 ig.Emit (OpCodes.Blt, loop [i]);
4972 ig.MarkLabel (ec.LoopEnd);
4976 protected class CollectionForeach : ExceptionStatement
4978 Expression variable, expr;
4979 Statement statement;
4981 TemporaryVariable enumerator;
4985 MethodGroupExpr get_enumerator;
4986 PropertyExpr get_current;
4987 MethodInfo move_next;
4988 Expression var_type;
4989 Type enumerator_type;
4991 bool enumerator_found;
4993 public CollectionForeach (Expression var_type, Expression var,
4994 Expression expr, Statement stmt, Location l)
4996 this.var_type = var_type;
4997 this.variable = var;
5003 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5005 Type return_type = mi.ReturnType;
5007 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5009 // Apply the same optimization as MS: skip the GetEnumerator
5010 // returning an IEnumerator, and use the one returning a
5011 // CharEnumerator instead. This allows us to avoid the
5012 // try-finally block and the boxing.
5017 // Ok, we can access it, now make sure that we can do something
5018 // with this `GetEnumerator'
5021 if (return_type == TypeManager.ienumerator_type ||
5022 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5023 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5025 // If it is not an interface, lets try to find the methods ourselves.
5026 // For example, if we have:
5027 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5028 // We can avoid the iface call. This is a runtime perf boost.
5029 // even bigger if we have a ValueType, because we avoid the cost
5032 // We have to make sure that both methods exist for us to take
5033 // this path. If one of the methods does not exist, we will just
5034 // use the interface. Sadly, this complex if statement is the only
5035 // way I could do this without a goto
5040 // Prefer a generic enumerator over a non-generic one.
5042 if (return_type.IsInterface && return_type.IsGenericType) {
5043 enumerator_type = return_type;
5044 if (!FetchGetCurrent (ec, return_type))
5045 get_current = new PropertyExpr (
5046 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5047 if (!FetchMoveNext (return_type))
5048 move_next = TypeManager.bool_movenext_void;
5053 if (return_type.IsInterface ||
5054 !FetchMoveNext (return_type) ||
5055 !FetchGetCurrent (ec, return_type)) {
5056 enumerator_type = return_type;
5057 move_next = TypeManager.bool_movenext_void;
5058 get_current = new PropertyExpr (
5059 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5064 // Ok, so they dont return an IEnumerable, we will have to
5065 // find if they support the GetEnumerator pattern.
5068 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5069 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",
5070 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5075 enumerator_type = return_type;
5076 is_disposable = !enumerator_type.IsSealed ||
5077 TypeManager.ImplementsInterface (
5078 enumerator_type, TypeManager.idisposable_type);
5084 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5086 bool FetchMoveNext (Type t)
5088 MemberList move_next_list;
5090 move_next_list = TypeContainer.FindMembers (
5091 t, MemberTypes.Method,
5092 BindingFlags.Public | BindingFlags.Instance,
5093 Type.FilterName, "MoveNext");
5094 if (move_next_list.Count == 0)
5097 foreach (MemberInfo m in move_next_list){
5098 MethodInfo mi = (MethodInfo) m;
5100 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5101 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5111 // Retrieves a `public T get_Current ()' method from the Type `t'
5113 bool FetchGetCurrent (EmitContext ec, Type t)
5115 PropertyExpr pe = Expression.MemberLookup (
5116 ec.ContainerType, t, "Current", MemberTypes.Property,
5117 Expression.AllBindingFlags, loc) as PropertyExpr;
5126 // Retrieves a `public void Dispose ()' method from the Type `t'
5128 static MethodInfo FetchMethodDispose (Type t)
5130 MemberList dispose_list;
5132 dispose_list = TypeContainer.FindMembers (
5133 t, MemberTypes.Method,
5134 BindingFlags.Public | BindingFlags.Instance,
5135 Type.FilterName, "Dispose");
5136 if (dispose_list.Count == 0)
5139 foreach (MemberInfo m in dispose_list){
5140 MethodInfo mi = (MethodInfo) m;
5142 if (TypeManager.GetParameterData (mi).Count == 0){
5143 if (mi.ReturnType == TypeManager.void_type)
5150 public void Error_Enumerator ()
5152 if (enumerator_found) {
5156 Report.Error (1579, loc,
5157 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5158 TypeManager.CSharpName (expr.Type));
5161 bool IsOverride (MethodInfo m)
5163 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5165 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5167 if (m is MethodBuilder)
5170 MethodInfo base_method = m.GetBaseDefinition ();
5171 return base_method != m;
5174 bool TryType (EmitContext ec, Type t)
5176 MethodGroupExpr mg = Expression.MemberLookup (
5177 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5178 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5182 MethodInfo result = null;
5183 MethodInfo tmp_move_next = null;
5184 PropertyExpr tmp_get_cur = null;
5185 Type tmp_enumerator_type = enumerator_type;
5186 foreach (MethodInfo mi in mg.Methods) {
5187 if (TypeManager.GetParameterData (mi).Count != 0)
5190 // Check whether GetEnumerator is public
5191 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5194 if (IsOverride (mi))
5197 enumerator_found = true;
5199 if (!GetEnumeratorFilter (ec, mi))
5202 if (result != null) {
5203 if (TypeManager.IsGenericType (result.ReturnType)) {
5204 if (!TypeManager.IsGenericType (mi.ReturnType))
5207 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5208 Report.SymbolRelatedToPreviousError (t);
5209 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5210 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5211 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5215 // Always prefer generics enumerators
5216 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5217 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5218 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5221 Report.SymbolRelatedToPreviousError (result);
5222 Report.SymbolRelatedToPreviousError (mi);
5223 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5224 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5229 tmp_move_next = move_next;
5230 tmp_get_cur = get_current;
5231 tmp_enumerator_type = enumerator_type;
5232 if (mi.DeclaringType == t)
5236 if (result != null) {
5237 move_next = tmp_move_next;
5238 get_current = tmp_get_cur;
5239 enumerator_type = tmp_enumerator_type;
5240 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5241 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5243 if (t != expr.Type) {
5244 expr = Convert.ExplicitConversion (
5247 throw new InternalErrorException ();
5250 get_enumerator.InstanceExpression = expr;
5251 get_enumerator.IsBase = t != expr.Type;
5259 bool ProbeCollectionType (EmitContext ec, Type t)
5261 int errors = Report.Errors;
5262 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5263 if (TryType (ec, tt))
5268 if (Report.Errors > errors)
5272 // Now try to find the method in the interfaces
5274 Type [] ifaces = TypeManager.GetInterfaces (t);
5275 foreach (Type i in ifaces){
5276 if (TryType (ec, i))
5283 public override bool Resolve (EmitContext ec)
5285 enumerator_type = TypeManager.ienumerator_type;
5286 is_disposable = true;
5288 if (!ProbeCollectionType (ec, expr.Type)) {
5289 Error_Enumerator ();
5293 VarExpr ve = var_type as VarExpr;
5295 // Infer implicitly typed local variable from foreach enumerable type
5296 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5299 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5300 if (var_type == null)
5303 enumerator = new TemporaryVariable (enumerator_type, loc);
5304 enumerator.Resolve (ec);
5306 init = new Invocation (get_enumerator, null);
5307 init = init.Resolve (ec);
5311 Expression move_next_expr;
5313 MemberInfo[] mi = new MemberInfo[] { move_next };
5314 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5315 mg.InstanceExpression = enumerator;
5317 move_next_expr = new Invocation (mg, null);
5320 get_current.InstanceExpression = enumerator;
5322 Statement block = new CollectionForeachStatement (
5323 var_type.Type, variable, get_current, statement, loc);
5325 loop = new While (move_next_expr, block, loc);
5329 FlowBranchingException branching = null;
5331 branching = ec.StartFlowBranching (this);
5333 if (!loop.Resolve (ec))
5336 if (is_disposable) {
5337 ResolveFinally (branching);
5338 ec.EndFlowBranching ();
5340 emit_finally = true;
5345 protected override void DoEmit (EmitContext ec)
5347 ILGenerator ig = ec.ig;
5349 enumerator.Store (ec, init);
5352 // Protect the code in a try/finalize block, so that
5353 // if the beast implement IDisposable, we get rid of it
5355 if (is_disposable && emit_finally)
5356 ig.BeginExceptionBlock ();
5361 // Now the finally block
5363 if (is_disposable) {
5366 ig.EndExceptionBlock ();
5371 public override void EmitFinally (EmitContext ec)
5373 ILGenerator ig = ec.ig;
5375 if (enumerator_type.IsValueType) {
5376 MethodInfo mi = FetchMethodDispose (enumerator_type);
5378 enumerator.EmitLoadAddress (ec);
5379 ig.Emit (OpCodes.Call, mi);
5381 enumerator.Emit (ec);
5382 ig.Emit (OpCodes.Box, enumerator_type);
5383 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5386 Label call_dispose = ig.DefineLabel ();
5388 enumerator.Emit (ec);
5389 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5390 ig.Emit (OpCodes.Dup);
5391 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5392 ig.Emit (OpCodes.Pop);
5394 Label end_finally = ig.DefineLabel ();
5395 ig.Emit (OpCodes.Br, end_finally);
5397 ig.MarkLabel (call_dispose);
5398 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5399 ig.MarkLabel (end_finally);
5404 protected class CollectionForeachStatement : Statement
5407 Expression variable, current, conv;
5408 Statement statement;
5411 public CollectionForeachStatement (Type type, Expression variable,
5412 Expression current, Statement statement,
5416 this.variable = variable;
5417 this.current = current;
5418 this.statement = statement;
5422 public override bool Resolve (EmitContext ec)
5424 current = current.Resolve (ec);
5425 if (current == null)
5428 conv = Convert.ExplicitConversion (ec, current, type, loc);
5432 assign = new Assign (variable, conv, loc);
5433 if (assign.Resolve (ec) == null)
5436 if (!statement.Resolve (ec))
5442 protected override void DoEmit (EmitContext ec)
5444 assign.EmitStatement (ec);
5445 statement.Emit (ec);
5449 protected override void CloneTo (CloneContext clonectx, Statement t)
5451 Foreach target = (Foreach) t;
5453 target.type = type.Clone (clonectx);
5454 target.variable = variable.Clone (clonectx);
5455 target.expr = expr.Clone (clonectx);
5456 target.statement = statement.Clone (clonectx);