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 virtual Expression CreateExpressionTree (EmitContext ec)
108 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
112 public Statement PerformClone ()
114 CloneContext clonectx = new CloneContext ();
116 return Clone (clonectx);
122 // This class is used during the Statement.Clone operation
123 // to remap objects that have been cloned.
125 // Since blocks are cloned by Block.Clone, we need a way for
126 // expressions that must reference the block to be cloned
127 // pointing to the new cloned block.
129 public class CloneContext {
130 Hashtable block_map = new Hashtable ();
131 Hashtable variable_map;
133 public void AddBlockMap (Block from, Block to)
135 if (block_map.Contains (from))
137 block_map [from] = to;
140 public Block LookupBlock (Block from)
142 Block result = (Block) block_map [from];
145 result = (Block) from.Clone (this);
146 block_map [from] = result;
153 /// Remaps block to cloned copy if one exists.
155 public Block RemapBlockCopy (Block from)
157 Block mapped_to = (Block)block_map[from];
158 if (mapped_to == null)
164 public void AddVariableMap (LocalInfo from, LocalInfo to)
166 if (variable_map == null)
167 variable_map = new Hashtable ();
169 if (variable_map.Contains (from))
171 variable_map [from] = to;
174 public LocalInfo LookupVariable (LocalInfo from)
176 LocalInfo result = (LocalInfo) variable_map [from];
179 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
185 public sealed class EmptyStatement : Statement {
187 private EmptyStatement () {}
189 public static readonly EmptyStatement Value = new EmptyStatement ();
191 public override bool Resolve (EmitContext ec)
196 public override bool ResolveUnreachable (EmitContext ec, bool warn)
201 protected override void DoEmit (EmitContext ec)
205 protected override void CloneTo (CloneContext clonectx, Statement target)
211 public class If : Statement {
213 public Statement TrueStatement;
214 public Statement FalseStatement;
218 public If (Expression expr, Statement true_statement, Location l)
221 TrueStatement = true_statement;
225 public If (Expression expr,
226 Statement true_statement,
227 Statement false_statement,
231 TrueStatement = true_statement;
232 FalseStatement = false_statement;
236 public override bool Resolve (EmitContext ec)
240 Report.Debug (1, "START IF BLOCK", loc);
242 expr = Expression.ResolveBoolean (ec, expr, loc);
248 Assign ass = expr as Assign;
249 if (ass != null && ass.Source is Constant) {
250 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
254 // Dead code elimination
256 if (expr is Constant){
257 bool take = !((Constant) expr).IsDefaultValue;
260 if (!TrueStatement.Resolve (ec))
263 if ((FalseStatement != null) &&
264 !FalseStatement.ResolveUnreachable (ec, true))
266 FalseStatement = null;
268 if (!TrueStatement.ResolveUnreachable (ec, true))
270 TrueStatement = null;
272 if ((FalseStatement != null) &&
273 !FalseStatement.Resolve (ec))
280 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
282 ok &= TrueStatement.Resolve (ec);
284 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
286 ec.CurrentBranching.CreateSibling ();
288 if (FalseStatement != null)
289 ok &= FalseStatement.Resolve (ec);
291 ec.EndFlowBranching ();
293 Report.Debug (1, "END IF BLOCK", loc);
298 protected override void DoEmit (EmitContext ec)
300 ILGenerator ig = ec.ig;
301 Label false_target = ig.DefineLabel ();
305 // If we're a boolean constant, Resolve() already
306 // eliminated dead code for us.
308 if (expr is Constant){
311 // Simple bool constant
313 if (expr is BoolConstant) {
314 bool take = ((BoolConstant) expr).Value;
317 TrueStatement.Emit (ec);
318 else if (FalseStatement != null)
319 FalseStatement.Emit (ec);
325 // Bool constant with side-effects
328 ig.Emit (OpCodes.Pop);
330 if (TrueStatement != null)
331 TrueStatement.Emit (ec);
332 if (FalseStatement != null)
333 FalseStatement.Emit (ec);
338 expr.EmitBranchable (ec, false_target, false);
340 TrueStatement.Emit (ec);
342 if (FalseStatement != null){
343 bool branch_emitted = false;
345 end = ig.DefineLabel ();
347 ig.Emit (OpCodes.Br, end);
348 branch_emitted = true;
351 ig.MarkLabel (false_target);
352 FalseStatement.Emit (ec);
357 ig.MarkLabel (false_target);
361 protected override void CloneTo (CloneContext clonectx, Statement t)
365 target.expr = expr.Clone (clonectx);
366 target.TrueStatement = TrueStatement.Clone (clonectx);
367 if (FalseStatement != null)
368 target.FalseStatement = FalseStatement.Clone (clonectx);
372 public class Do : Statement {
373 public Expression expr;
374 public Statement EmbeddedStatement;
377 public Do (Statement statement, Expression bool_expr, Location l)
380 EmbeddedStatement = statement;
384 public override bool Resolve (EmitContext ec)
388 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
390 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
392 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
393 if (!EmbeddedStatement.Resolve (ec))
395 ec.EndFlowBranching ();
397 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
398 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
400 expr = Expression.ResolveBoolean (ec, expr, loc);
403 else if (expr is BoolConstant){
404 bool res = ((BoolConstant) expr).Value;
410 ec.CurrentBranching.CurrentUsageVector.Goto ();
412 ec.EndFlowBranching ();
417 protected override void DoEmit (EmitContext ec)
419 ILGenerator ig = ec.ig;
420 Label loop = ig.DefineLabel ();
421 Label old_begin = ec.LoopBegin;
422 Label old_end = ec.LoopEnd;
424 ec.LoopBegin = ig.DefineLabel ();
425 ec.LoopEnd = ig.DefineLabel ();
428 EmbeddedStatement.Emit (ec);
429 ig.MarkLabel (ec.LoopBegin);
432 // Dead code elimination
434 if (expr is BoolConstant){
435 bool res = ((BoolConstant) expr).Value;
438 ec.ig.Emit (OpCodes.Br, loop);
440 expr.EmitBranchable (ec, loop, true);
442 ig.MarkLabel (ec.LoopEnd);
444 ec.LoopBegin = old_begin;
445 ec.LoopEnd = old_end;
448 protected override void CloneTo (CloneContext clonectx, Statement t)
452 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
453 target.expr = expr.Clone (clonectx);
457 public class While : Statement {
458 public Expression expr;
459 public Statement Statement;
460 bool infinite, empty;
462 public While (Expression bool_expr, Statement statement, Location l)
464 this.expr = bool_expr;
465 Statement = statement;
469 public override bool Resolve (EmitContext ec)
473 expr = Expression.ResolveBoolean (ec, expr, loc);
478 // Inform whether we are infinite or not
480 if (expr is BoolConstant){
481 BoolConstant bc = (BoolConstant) expr;
483 if (bc.Value == false){
484 if (!Statement.ResolveUnreachable (ec, true))
492 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
494 ec.CurrentBranching.CreateSibling ();
496 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
497 if (!Statement.Resolve (ec))
499 ec.EndFlowBranching ();
501 // There's no direct control flow from the end of the embedded statement to the end of the loop
502 ec.CurrentBranching.CurrentUsageVector.Goto ();
504 ec.EndFlowBranching ();
509 protected override void DoEmit (EmitContext ec)
514 ILGenerator ig = ec.ig;
515 Label old_begin = ec.LoopBegin;
516 Label old_end = ec.LoopEnd;
518 ec.LoopBegin = ig.DefineLabel ();
519 ec.LoopEnd = ig.DefineLabel ();
522 // Inform whether we are infinite or not
524 if (expr is BoolConstant){
525 ig.MarkLabel (ec.LoopBegin);
527 ig.Emit (OpCodes.Br, ec.LoopBegin);
530 // Inform that we are infinite (ie, `we return'), only
531 // if we do not `break' inside the code.
533 ig.MarkLabel (ec.LoopEnd);
535 Label while_loop = ig.DefineLabel ();
537 ig.Emit (OpCodes.Br, ec.LoopBegin);
538 ig.MarkLabel (while_loop);
542 ig.MarkLabel (ec.LoopBegin);
544 expr.EmitBranchable (ec, while_loop, true);
546 ig.MarkLabel (ec.LoopEnd);
549 ec.LoopBegin = old_begin;
550 ec.LoopEnd = old_end;
553 protected override void CloneTo (CloneContext clonectx, Statement t)
555 While target = (While) t;
557 target.expr = expr.Clone (clonectx);
558 target.Statement = Statement.Clone (clonectx);
562 public class For : Statement {
564 Statement InitStatement;
566 public Statement Statement;
567 bool infinite, empty;
569 public For (Statement init_statement,
575 InitStatement = init_statement;
577 Increment = increment;
578 Statement = statement;
582 public override bool Resolve (EmitContext ec)
586 if (InitStatement != null){
587 if (!InitStatement.Resolve (ec))
592 Test = Expression.ResolveBoolean (ec, Test, loc);
595 else if (Test is BoolConstant){
596 BoolConstant bc = (BoolConstant) Test;
598 if (bc.Value == false){
599 if (!Statement.ResolveUnreachable (ec, true))
601 if ((Increment != null) &&
602 !Increment.ResolveUnreachable (ec, false))
612 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
614 ec.CurrentBranching.CreateSibling ();
616 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
618 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
619 if (!Statement.Resolve (ec))
621 ec.EndFlowBranching ();
623 if (Increment != null){
624 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
625 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
628 if (!Increment.Resolve (ec))
633 // There's no direct control flow from the end of the embedded statement to the end of the loop
634 ec.CurrentBranching.CurrentUsageVector.Goto ();
636 ec.EndFlowBranching ();
641 protected override void DoEmit (EmitContext ec)
646 ILGenerator ig = ec.ig;
647 Label old_begin = ec.LoopBegin;
648 Label old_end = ec.LoopEnd;
649 Label loop = ig.DefineLabel ();
650 Label test = ig.DefineLabel ();
652 if (InitStatement != null && InitStatement != EmptyStatement.Value)
653 InitStatement.Emit (ec);
655 ec.LoopBegin = ig.DefineLabel ();
656 ec.LoopEnd = ig.DefineLabel ();
658 ig.Emit (OpCodes.Br, test);
662 ig.MarkLabel (ec.LoopBegin);
663 if (Increment != EmptyStatement.Value)
668 // If test is null, there is no test, and we are just
673 // The Resolve code already catches the case for
674 // Test == BoolConstant (false) so we know that
677 if (Test is BoolConstant)
678 ig.Emit (OpCodes.Br, loop);
680 Test.EmitBranchable (ec, loop, true);
683 ig.Emit (OpCodes.Br, loop);
684 ig.MarkLabel (ec.LoopEnd);
686 ec.LoopBegin = old_begin;
687 ec.LoopEnd = old_end;
690 protected override void CloneTo (CloneContext clonectx, Statement t)
692 For target = (For) t;
694 if (InitStatement != null)
695 target.InitStatement = InitStatement.Clone (clonectx);
697 target.Test = Test.Clone (clonectx);
698 if (Increment != null)
699 target.Increment = Increment.Clone (clonectx);
700 target.Statement = Statement.Clone (clonectx);
704 public class StatementExpression : Statement {
705 ExpressionStatement expr;
707 public StatementExpression (ExpressionStatement expr)
713 public override bool Resolve (EmitContext ec)
716 expr = expr.ResolveStatement (ec);
720 protected override void DoEmit (EmitContext ec)
722 expr.EmitStatement (ec);
725 public override string ToString ()
727 return "StatementExpression (" + expr + ")";
730 protected override void CloneTo (CloneContext clonectx, Statement t)
732 StatementExpression target = (StatementExpression) t;
734 target.expr = (ExpressionStatement) expr.Clone (clonectx);
739 /// Implements the return statement
741 public class Return : Statement {
742 protected Expression Expr;
745 public Return (Expression expr, Location l)
751 bool DoResolve (EmitContext ec)
754 if (ec.ReturnType == TypeManager.void_type)
757 Error (126, "An object of a type convertible to `{0}' is required " +
758 "for the return statement",
759 TypeManager.CSharpName (ec.ReturnType));
763 AnonymousContainer am = ec.CurrentAnonymousMethod;
764 if ((am != null) && am.IsIterator && ec.InIterator) {
765 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
766 "statement to return a value, or yield break to end the iteration");
769 if (am == null && ec.ReturnType == TypeManager.void_type) {
770 MemberCore mc = ec.ResolveContext as MemberCore;
771 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
772 mc.GetSignatureForError ());
775 Expr = Expr.Resolve (ec);
779 if (Expr.Type != ec.ReturnType) {
780 if (ec.InferReturnType) {
781 ec.ReturnType = Expr.Type;
783 Expr = Convert.ImplicitConversionRequired (
784 ec, Expr, ec.ReturnType, loc);
788 Report.Error (1662, loc,
789 "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",
790 am.ContainerType, am.GetSignatureForError ());
800 public override bool Resolve (EmitContext ec)
805 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
807 ec.NeedReturnLabel ();
808 ec.CurrentBranching.CurrentUsageVector.Goto ();
812 protected override void DoEmit (EmitContext ec)
818 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
822 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
824 ec.ig.Emit (OpCodes.Ret);
827 protected override void CloneTo (CloneContext clonectx, Statement t)
829 Return target = (Return) t;
830 // It's null for simple return;
832 target.Expr = Expr.Clone (clonectx);
836 public class Goto : Statement {
838 LabeledStatement label;
841 public override bool Resolve (EmitContext ec)
843 int errors = Report.Errors;
844 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
845 ec.CurrentBranching.CurrentUsageVector.Goto ();
846 return errors == Report.Errors;
849 public Goto (string label, Location l)
855 public string Target {
856 get { return target; }
859 public void SetResolvedTarget (LabeledStatement label)
862 label.AddReference ();
865 protected override void DoEmit (EmitContext ec)
868 throw new InternalErrorException ("goto emitted before target resolved");
869 Label l = label.LabelTarget (ec);
870 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
874 public class LabeledStatement : Statement {
881 FlowBranching.UsageVector vectors;
883 public LabeledStatement (string name, Location l)
889 public Label LabelTarget (EmitContext ec)
894 label = ec.ig.DefineLabel ();
904 public bool IsDefined {
905 get { return defined; }
908 public bool HasBeenReferenced {
909 get { return referenced; }
912 public FlowBranching.UsageVector JumpOrigins {
913 get { return vectors; }
916 public void AddUsageVector (FlowBranching.UsageVector vector)
918 vector = vector.Clone ();
919 vector.Next = vectors;
923 public override bool Resolve (EmitContext ec)
925 // this flow-branching will be terminated when the surrounding block ends
926 ec.StartFlowBranching (this);
930 protected override void DoEmit (EmitContext ec)
932 if (ig != null && ig != ec.ig)
933 throw new InternalErrorException ("cannot happen");
935 ec.ig.MarkLabel (label);
938 public void AddReference ()
946 /// `goto default' statement
948 public class GotoDefault : Statement {
950 public GotoDefault (Location l)
955 public override bool Resolve (EmitContext ec)
957 ec.CurrentBranching.CurrentUsageVector.Goto ();
961 protected override void DoEmit (EmitContext ec)
963 if (ec.Switch == null){
964 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
968 if (!ec.Switch.GotDefault){
969 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
972 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
977 /// `goto case' statement
979 public class GotoCase : Statement {
983 public GotoCase (Expression e, Location l)
989 public override bool Resolve (EmitContext ec)
991 if (ec.Switch == null){
992 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
996 expr = expr.Resolve (ec);
1000 Constant c = expr as Constant;
1002 Error (150, "A constant value is expected");
1006 Type type = ec.Switch.SwitchType;
1007 if (!Convert.ImplicitStandardConversionExists (c, type))
1008 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1009 "convertible to type `{0}'", TypeManager.CSharpName (type));
1012 object val = c.GetValue ();
1013 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1014 val = TypeManager.ChangeType (val, type, out fail);
1017 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1018 c.GetSignatureForError (), TypeManager.CSharpName (type));
1023 val = SwitchLabel.NullStringCase;
1025 sl = (SwitchLabel) ec.Switch.Elements [val];
1028 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1029 (c.GetValue () == null ? "null" : val.ToString ()));
1033 ec.CurrentBranching.CurrentUsageVector.Goto ();
1037 protected override void DoEmit (EmitContext ec)
1039 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1042 protected override void CloneTo (CloneContext clonectx, Statement t)
1044 GotoCase target = (GotoCase) t;
1046 target.expr = expr.Clone (clonectx);
1047 target.sl = sl.Clone (clonectx);
1051 public class Throw : Statement {
1054 public Throw (Expression expr, Location l)
1060 public override bool Resolve (EmitContext ec)
1062 ec.CurrentBranching.CurrentUsageVector.Goto ();
1065 expr = expr.Resolve (ec);
1069 ExprClass eclass = expr.eclass;
1071 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1072 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1073 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1079 if ((t != TypeManager.exception_type) &&
1080 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1081 !(expr is NullLiteral)) {
1083 "The type caught or thrown must be derived " +
1084 "from System.Exception");
1091 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
1096 Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1102 protected override void DoEmit (EmitContext ec)
1105 ec.ig.Emit (OpCodes.Rethrow);
1109 ec.ig.Emit (OpCodes.Throw);
1113 protected override void CloneTo (CloneContext clonectx, Statement t)
1115 Throw target = (Throw) t;
1118 target.expr = expr.Clone (clonectx);
1122 public class Break : Statement {
1124 public Break (Location l)
1129 bool unwind_protect;
1131 public override bool Resolve (EmitContext ec)
1133 int errors = Report.Errors;
1134 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1135 ec.CurrentBranching.CurrentUsageVector.Goto ();
1136 return errors == Report.Errors;
1139 protected override void DoEmit (EmitContext ec)
1141 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1144 protected override void CloneTo (CloneContext clonectx, Statement t)
1150 public class Continue : Statement {
1152 public Continue (Location l)
1157 bool unwind_protect;
1159 public override bool Resolve (EmitContext ec)
1161 int errors = Report.Errors;
1162 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1163 ec.CurrentBranching.CurrentUsageVector.Goto ();
1164 return errors == Report.Errors;
1167 protected override void DoEmit (EmitContext ec)
1169 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1172 protected override void CloneTo (CloneContext clonectx, Statement t)
1178 public abstract class Variable
1180 public abstract Type Type {
1184 public abstract bool HasInstance {
1188 public abstract bool NeedsTemporary {
1192 public abstract void EmitInstance (EmitContext ec);
1194 public abstract void Emit (EmitContext ec);
1196 public abstract void EmitAssign (EmitContext ec);
1198 public abstract void EmitAddressOf (EmitContext ec);
1201 public interface IKnownVariable {
1202 Block Block { get; }
1203 Location Location { get; }
1207 // The information about a user-perceived local variable
1209 public class LocalInfo : IKnownVariable {
1210 public Expression Type;
1212 public Type VariableType;
1213 public readonly string Name;
1214 public readonly Location Location;
1215 public readonly Block Block;
1217 public VariableInfo VariableInfo;
1220 public Variable Variable {
1232 CompilerGenerated = 64,
1236 public enum ReadOnlyContext: byte {
1243 ReadOnlyContext ro_context;
1244 LocalBuilder builder;
1246 public LocalInfo (Expression type, string name, Block block, Location l)
1254 public LocalInfo (DeclSpace ds, Block block, Location l)
1256 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1261 public void ResolveVariable (EmitContext ec)
1263 Block theblock = Block;
1264 if (theblock.ScopeInfo != null)
1265 var = theblock.ScopeInfo.GetCapturedVariable (this);
1270 // This is needed to compile on both .NET 1.x and .NET 2.x
1271 // the later introduced `DeclareLocal (Type t, bool pinned)'
1273 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1275 builder = ec.ig.DeclareLocal (VariableType);
1277 var = new LocalVariable (this, builder);
1281 public void EmitSymbolInfo (EmitContext ec, string name)
1283 if (builder != null)
1284 ec.DefineLocalVariable (name, builder);
1287 public bool IsThisAssigned (EmitContext ec)
1289 if (VariableInfo == null)
1290 throw new Exception ();
1292 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1295 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1298 public bool IsAssigned (EmitContext ec)
1300 if (VariableInfo == null)
1301 throw new Exception ();
1303 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1306 public bool Resolve (EmitContext ec)
1308 if (VariableType == null) {
1309 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1313 VariableType = texpr.Type;
1316 if (TypeManager.IsGenericParameter (VariableType))
1319 if (VariableType == TypeManager.void_type) {
1320 Expression.Error_VoidInvalidInTheContext (Location);
1324 if (VariableType.IsAbstract && VariableType.IsSealed) {
1325 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1329 if (VariableType.IsPointer && !ec.InUnsafe)
1330 Expression.UnsafeError (Location);
1335 public bool IsCaptured {
1336 get { return (flags & Flags.Captured) != 0; }
1337 set { flags |= Flags.Captured; }
1340 public bool IsConstant {
1341 get { return (flags & Flags.IsConstant) != 0; }
1342 set { flags |= Flags.IsConstant; }
1345 public bool AddressTaken {
1346 get { return (flags & Flags.AddressTaken) != 0; }
1347 set { flags |= Flags.AddressTaken; }
1350 public bool CompilerGenerated {
1351 get { return (flags & Flags.CompilerGenerated) != 0; }
1352 set { flags |= Flags.CompilerGenerated; }
1355 public override string ToString ()
1357 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1358 Name, Type, VariableInfo, Location);
1362 get { return (flags & Flags.Used) != 0; }
1363 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1366 public bool ReadOnly {
1367 get { return (flags & Flags.ReadOnly) != 0; }
1370 public void SetReadOnlyContext (ReadOnlyContext context)
1372 flags |= Flags.ReadOnly;
1373 ro_context = context;
1376 public string GetReadOnlyContext ()
1379 throw new InternalErrorException ("Variable is not readonly");
1381 switch (ro_context) {
1382 case ReadOnlyContext.Fixed:
1383 return "fixed variable";
1384 case ReadOnlyContext.Foreach:
1385 return "foreach iteration variable";
1386 case ReadOnlyContext.Using:
1387 return "using variable";
1389 throw new NotImplementedException ();
1393 // Whether the variable is pinned, if Pinned the variable has been
1394 // allocated in a pinned slot with DeclareLocal.
1396 public bool Pinned {
1397 get { return (flags & Flags.Pinned) != 0; }
1398 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1401 public bool IsThis {
1402 get { return (flags & Flags.IsThis) != 0; }
1403 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1406 Block IKnownVariable.Block {
1407 get { return Block; }
1410 Location IKnownVariable.Location {
1411 get { return Location; }
1414 protected class LocalVariable : Variable
1416 public readonly LocalInfo LocalInfo;
1417 LocalBuilder builder;
1419 public LocalVariable (LocalInfo local, LocalBuilder builder)
1421 this.LocalInfo = local;
1422 this.builder = builder;
1425 public override Type Type {
1426 get { return LocalInfo.VariableType; }
1429 public override bool HasInstance {
1430 get { return false; }
1433 public override bool NeedsTemporary {
1434 get { return false; }
1437 public override void EmitInstance (EmitContext ec)
1442 public override void Emit (EmitContext ec)
1444 ec.ig.Emit (OpCodes.Ldloc, builder);
1447 public override void EmitAssign (EmitContext ec)
1449 ec.ig.Emit (OpCodes.Stloc, builder);
1452 public override void EmitAddressOf (EmitContext ec)
1454 ec.ig.Emit (OpCodes.Ldloca, builder);
1458 public LocalInfo Clone (CloneContext clonectx)
1461 // Variables in anonymous block are not resolved yet
1463 if (VariableType == null)
1464 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1467 // Variables in method block are resolved
1469 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1470 li.VariableType = VariableType;
1476 /// Block represents a C# block.
1480 /// This class is used in a number of places: either to represent
1481 /// explicit blocks that the programmer places or implicit blocks.
1483 /// Implicit blocks are used as labels or to introduce variable
1486 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1487 /// they contain extra information that is not necessary on normal blocks.
1489 public class Block : Statement {
1490 public Block Parent;
1491 public readonly Location StartLocation;
1492 public Location EndLocation = Location.Null;
1494 public ExplicitBlock Explicit;
1495 public ToplevelBlock Toplevel;
1498 public enum Flags : byte {
1501 VariablesInitialized = 4,
1505 HasVarargs = 64, // Used in ToplevelBlock
1508 protected Flags flags;
1510 public bool Unchecked {
1511 get { return (flags & Flags.Unchecked) != 0; }
1512 set { flags |= Flags.Unchecked; }
1515 public bool Unsafe {
1516 get { return (flags & Flags.Unsafe) != 0; }
1517 set { flags |= Flags.Unsafe; }
1521 // The statements in this block
1523 protected ArrayList statements;
1527 // An array of Blocks. We keep track of children just
1528 // to generate the local variable declarations.
1530 // Statements and child statements are handled through the
1536 // Labels. (label, block) pairs.
1538 HybridDictionary labels;
1541 // Keeps track of (name, type) pairs
1543 IDictionary variables;
1546 // Keeps track of constants
1547 HybridDictionary constants;
1550 // Temporary variables.
1552 ArrayList temporary_variables;
1555 // If this is a switch section, the enclosing switch block.
1559 // TODO: merge with scope_initializers
1560 ExpressionStatement scope_init;
1561 ArrayList scope_initializers;
1563 ArrayList anonymous_children;
1565 protected static int id;
1569 int assignable_slots;
1570 protected ScopeInfo scope_info;
1571 bool unreachable_shown;
1574 public Block (Block parent)
1575 : this (parent, (Flags) 0, Location.Null, Location.Null)
1578 public Block (Block parent, Flags flags)
1579 : this (parent, flags, Location.Null, Location.Null)
1582 public Block (Block parent, Location start, Location end)
1583 : this (parent, (Flags) 0, start, end)
1586 public Block (Block parent, Flags flags, Location start, Location end)
1588 if (parent != null) {
1589 parent.AddChild (this);
1591 // the appropriate constructors will fixup these fields
1592 Toplevel = parent.Toplevel;
1593 Explicit = parent.Explicit;
1596 this.Parent = parent;
1598 this.StartLocation = start;
1599 this.EndLocation = end;
1602 statements = new ArrayList (4);
1605 public Block CreateSwitchBlock (Location start)
1607 // FIXME: should this be implicit?
1608 Block new_block = new ExplicitBlock (this, start, start);
1609 new_block.switch_block = this;
1614 get { return this_id; }
1617 public IDictionary Variables {
1619 if (variables == null)
1620 variables = new ListDictionary ();
1625 void AddChild (Block b)
1627 if (children == null)
1628 children = new ArrayList (1);
1633 public void SetEndLocation (Location loc)
1638 protected static void Error_158 (string name, Location loc)
1640 Report.Error (158, loc, "The label `{0}' shadows another label " +
1641 "by the same name in a contained scope", name);
1645 /// Adds a label to the current block.
1649 /// false if the name already exists in this block. true
1653 public bool AddLabel (LabeledStatement target)
1655 if (switch_block != null)
1656 return switch_block.AddLabel (target);
1658 string name = target.Name;
1661 while (cur != null) {
1662 LabeledStatement s = cur.DoLookupLabel (name);
1664 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1665 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1669 if (this == Explicit)
1675 while (cur != null) {
1676 if (cur.DoLookupLabel (name) != null) {
1677 Error_158 (name, target.loc);
1681 if (children != null) {
1682 foreach (Block b in children) {
1683 LabeledStatement s = b.DoLookupLabel (name);
1687 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1688 Error_158 (name, target.loc);
1696 Toplevel.CheckError158 (name, target.loc);
1699 labels = new HybridDictionary();
1701 labels.Add (name, target);
1705 public LabeledStatement LookupLabel (string name)
1707 LabeledStatement s = DoLookupLabel (name);
1711 if (children == null)
1714 foreach (Block child in children) {
1715 if (Explicit != child.Explicit)
1718 s = child.LookupLabel (name);
1726 LabeledStatement DoLookupLabel (string name)
1728 if (switch_block != null)
1729 return switch_block.LookupLabel (name);
1732 if (labels.Contains (name))
1733 return ((LabeledStatement) labels [name]);
1738 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1741 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1742 while (kvi == null) {
1743 b = b.Explicit.Parent;
1746 kvi = b.Explicit.GetKnownVariable (name);
1752 // Is kvi.Block nested inside 'b'
1753 if (b.Explicit != kvi.Block.Explicit) {
1755 // If a variable by the same name it defined in a nested block of this
1756 // block, we violate the invariant meaning in a block.
1759 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1760 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1765 // It's ok if the definition is in a nested subblock of b, but not
1766 // nested inside this block -- a definition in a sibling block
1767 // should not affect us.
1773 // Block 'b' and kvi.Block are the same textual block.
1774 // However, different variables are extant.
1776 // Check if the variable is in scope in both blocks. We use
1777 // an indirect check that depends on AddVariable doing its
1778 // part in maintaining the invariant-meaning-in-block property.
1780 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1784 // Even though we detected the error when the name is used, we
1785 // treat it as if the variable declaration was in error.
1787 Report.SymbolRelatedToPreviousError (loc, name);
1788 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1792 public LocalInfo AddVariable (Expression type, string name, Location l)
1794 LocalInfo vi = GetLocalInfo (name);
1796 Report.SymbolRelatedToPreviousError (vi.Location, name);
1797 if (Explicit == vi.Block.Explicit)
1798 Error_AlreadyDeclared (l, name, null);
1800 Error_AlreadyDeclared (l, name, "parent");
1804 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1806 Report.SymbolRelatedToPreviousError (pi.Location, name);
1807 Error_AlreadyDeclared (loc, name,
1808 pi.Block == Toplevel ? "method argument" : "parent or current");
1812 if (Toplevel.GenericMethod != null) {
1813 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1814 if (tp.Name == name) {
1815 Report.SymbolRelatedToPreviousError (tp);
1816 Error_AlreadyDeclaredTypeParameter (loc, name);
1822 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1824 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1825 Error_AlreadyDeclared (l, name, "child");
1829 vi = new LocalInfo (type, name, this, l);
1832 if ((flags & Flags.VariablesInitialized) != 0)
1833 throw new InternalErrorException ("block has already been resolved");
1838 protected virtual void AddVariable (LocalInfo li)
1840 Variables.Add (li.Name, li);
1841 Explicit.AddKnownVariable (li.Name, li);
1844 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1846 if (reason == null) {
1847 Error_AlreadyDeclared (loc, var);
1851 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1852 "in this scope because it would give a different meaning " +
1853 "to `{0}', which is already used in a `{1}' scope " +
1854 "to denote something else", var, reason);
1857 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1859 Report.Error (128, loc,
1860 "A local variable named `{0}' is already defined in this scope", name);
1863 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1865 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1868 public bool AddConstant (Expression type, string name, Expression value, Location l)
1870 if (AddVariable (type, name, l) == null)
1873 if (constants == null)
1874 constants = new HybridDictionary();
1876 constants.Add (name, value);
1878 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1883 static int next_temp_id = 0;
1885 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1887 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1889 if (temporary_variables == null)
1890 temporary_variables = new ArrayList ();
1892 int id = ++next_temp_id;
1893 string name = "$s_" + id.ToString ();
1895 LocalInfo li = new LocalInfo (te, name, this, loc);
1896 li.CompilerGenerated = true;
1897 temporary_variables.Add (li);
1901 public LocalInfo GetLocalInfo (string name)
1903 for (Block b = this; b != null; b = b.Parent) {
1904 if (b.variables != null) {
1905 LocalInfo ret = b.variables [name] as LocalInfo;
1913 public Expression GetVariableType (string name)
1915 LocalInfo vi = GetLocalInfo (name);
1916 return vi == null ? null : vi.Type;
1919 public Expression GetConstantExpression (string name)
1921 for (Block b = this; b != null; b = b.Parent) {
1922 if (b.constants != null) {
1923 Expression ret = b.constants [name] as Expression;
1932 // It should be used by expressions which require to
1933 // register a statement during resolve process.
1935 public void AddScopeStatement (StatementExpression s)
1937 if (scope_initializers == null)
1938 scope_initializers = new ArrayList ();
1940 scope_initializers.Add (s);
1943 public void AddStatement (Statement s)
1946 flags |= Flags.BlockUsed;
1950 get { return (flags & Flags.BlockUsed) != 0; }
1955 flags |= Flags.BlockUsed;
1958 public bool HasRet {
1959 get { return (flags & Flags.HasRet) != 0; }
1962 public bool IsDestructor {
1963 get { return (flags & Flags.IsDestructor) != 0; }
1966 public void SetDestructor ()
1968 flags |= Flags.IsDestructor;
1971 public int AssignableSlots {
1973 if ((flags & Flags.VariablesInitialized) == 0)
1974 throw new Exception ("Variables have not been initialized yet");
1975 return assignable_slots;
1979 public ScopeInfo ScopeInfo {
1980 get { return scope_info; }
1983 public ScopeInfo CreateScopeInfo ()
1985 if (scope_info == null)
1986 scope_info = ScopeInfo.CreateScope (this);
1991 public ArrayList AnonymousChildren {
1992 get { return anonymous_children; }
1995 public void AddAnonymousChild (ToplevelBlock b)
1997 if (anonymous_children == null)
1998 anonymous_children = new ArrayList ();
2000 anonymous_children.Add (b);
2003 void DoResolveConstants (EmitContext ec)
2005 if (constants == null)
2008 if (variables == null)
2009 throw new InternalErrorException ("cannot happen");
2011 foreach (DictionaryEntry de in variables) {
2012 string name = (string) de.Key;
2013 LocalInfo vi = (LocalInfo) de.Value;
2014 Type variable_type = vi.VariableType;
2016 if (variable_type == null) {
2017 if (vi.Type is VarExpr)
2018 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2023 Expression cv = (Expression) constants [name];
2027 // Don't let 'const int Foo = Foo;' succeed.
2028 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2029 // which in turn causes the 'must be constant' error to be triggered.
2030 constants.Remove (name);
2032 if (!Const.IsConstantTypeValid (variable_type)) {
2033 Const.Error_InvalidConstantType (variable_type, loc);
2037 ec.CurrentBlock = this;
2039 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2040 e = cv.Resolve (ec);
2045 Constant ce = e as Constant;
2047 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2051 e = ce.ConvertImplicitly (variable_type);
2053 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2054 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2056 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2060 constants.Add (name, e);
2061 vi.IsConstant = true;
2065 protected void ResolveMeta (EmitContext ec, int offset)
2067 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2069 // If some parent block was unsafe, we remain unsafe even if this block
2070 // isn't explicitly marked as such.
2071 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2072 flags |= Flags.VariablesInitialized;
2074 if (variables != null) {
2075 foreach (LocalInfo li in variables.Values) {
2076 if (!li.Resolve (ec))
2078 li.VariableInfo = new VariableInfo (li, offset);
2079 offset += li.VariableInfo.Length;
2082 assignable_slots = offset;
2084 DoResolveConstants (ec);
2086 if (children == null)
2088 foreach (Block b in children)
2089 b.ResolveMeta (ec, offset);
2094 // Emits the local variable declarations for a block
2096 public virtual void EmitMeta (EmitContext ec)
2098 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2099 if (ScopeInfo != null) {
2100 scope_init = ScopeInfo.GetScopeInitializer (ec);
2101 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2105 if (variables != null){
2106 foreach (LocalInfo vi in variables.Values)
2107 vi.ResolveVariable (ec);
2110 if (temporary_variables != null) {
2111 for (int i = 0; i < temporary_variables.Count; i++)
2112 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2115 if (children != null){
2116 for (int i = 0; i < children.Count; i++)
2117 ((Block)children[i]).EmitMeta(ec);
2121 void UsageWarning (FlowBranching.UsageVector vector)
2125 if ((variables != null) && (Report.WarningLevel >= 3)) {
2126 foreach (DictionaryEntry de in variables){
2127 LocalInfo vi = (LocalInfo) de.Value;
2132 name = (string) de.Key;
2134 // vi.VariableInfo can be null for 'catch' variables
2135 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2136 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2138 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2144 private void CheckPossibleMistakenEmptyStatement (Statement s)
2148 // Some statements are wrapped by a Block. Since
2149 // others' internal could be changed, here I treat
2150 // them as possibly wrapped by Block equally.
2151 Block b = s as Block;
2152 if (b != null && b.statements.Count == 1)
2153 s = (Statement) b.statements [0];
2156 body = ((Lock) s).Statement;
2158 body = ((For) s).Statement;
2159 else if (s is Foreach)
2160 body = ((Foreach) s).Statement;
2161 else if (s is While)
2162 body = ((While) s).Statement;
2163 else if (s is Using)
2164 body = ((Using) s).Statement;
2165 else if (s is Fixed)
2166 body = ((Fixed) s).Statement;
2170 if (body == null || body is EmptyStatement)
2171 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2174 public override bool Resolve (EmitContext ec)
2176 Block prev_block = ec.CurrentBlock;
2179 int errors = Report.Errors;
2181 ec.CurrentBlock = this;
2182 ec.StartFlowBranching (this);
2184 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2187 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2188 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2189 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2190 // responsible for handling the situation.
2192 int statement_count = statements.Count;
2193 for (int ix = 0; ix < statement_count; ix++){
2194 Statement s = (Statement) statements [ix];
2195 // Check possible empty statement (CS0642)
2196 if (Report.WarningLevel >= 3 &&
2197 ix + 1 < statement_count &&
2198 statements [ix + 1] is Block)
2199 CheckPossibleMistakenEmptyStatement (s);
2202 // Warn if we detect unreachable code.
2205 if (s is EmptyStatement)
2209 ((Block) s).unreachable = true;
2211 if (!unreachable_shown && !(s is LabeledStatement)) {
2212 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2213 unreachable_shown = true;
2218 // Note that we're not using ResolveUnreachable() for unreachable
2219 // statements here. ResolveUnreachable() creates a temporary
2220 // flow branching and kills it afterwards. This leads to problems
2221 // if you have two unreachable statements where the first one
2222 // assigns a variable and the second one tries to access it.
2225 if (!s.Resolve (ec)) {
2227 if (ec.IsInProbingMode)
2230 statements [ix] = EmptyStatement.Value;
2234 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2235 statements [ix] = EmptyStatement.Value;
2237 num_statements = ix + 1;
2239 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2240 if (unreachable && s is LabeledStatement)
2241 throw new InternalErrorException ("should not happen");
2244 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2245 ec.CurrentBranching, statement_count, num_statements);
2247 while (ec.CurrentBranching is FlowBranchingLabeled)
2248 ec.EndFlowBranching ();
2250 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2252 ec.CurrentBlock = prev_block;
2254 // If we're a non-static `struct' constructor which doesn't have an
2255 // initializer, then we must initialize all of the struct's fields.
2256 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2259 if ((labels != null) && (Report.WarningLevel >= 2)) {
2260 foreach (LabeledStatement label in labels.Values)
2261 if (!label.HasBeenReferenced)
2262 Report.Warning (164, 2, label.loc,
2263 "This label has not been referenced");
2266 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2268 if (vector.IsUnreachable)
2269 flags |= Flags.HasRet;
2271 if (ok && (errors == Report.Errors)) {
2272 UsageWarning (vector);
2278 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2280 unreachable_shown = true;
2284 Report.Warning (162, 2, loc, "Unreachable code detected");
2286 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2287 bool ok = Resolve (ec);
2288 ec.KillFlowBranching ();
2293 protected override void DoEmit (EmitContext ec)
2295 for (int ix = 0; ix < num_statements; ix++){
2296 Statement s = (Statement) statements [ix];
2301 public override void Emit (EmitContext ec)
2303 Block prev_block = ec.CurrentBlock;
2305 ec.CurrentBlock = this;
2307 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2308 bool is_lexical_block = this == Explicit && Parent != null;
2310 if (emit_debug_info) {
2311 if (is_lexical_block)
2314 ec.Mark (StartLocation, true);
2315 if (scope_init != null)
2316 scope_init.EmitStatement (ec);
2317 if (scope_initializers != null) {
2318 foreach (StatementExpression s in scope_initializers)
2323 ec.Mark (EndLocation, true);
2325 if (emit_debug_info) {
2326 if (is_lexical_block)
2329 if (variables != null) {
2330 foreach (DictionaryEntry de in variables) {
2331 string name = (string) de.Key;
2332 LocalInfo vi = (LocalInfo) de.Value;
2334 vi.EmitSymbolInfo (ec, name);
2339 ec.CurrentBlock = prev_block;
2342 public override string ToString ()
2344 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2347 protected override void CloneTo (CloneContext clonectx, Statement t)
2349 Block target = (Block) t;
2351 clonectx.AddBlockMap (this, target);
2353 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2354 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2356 target.Parent = clonectx.RemapBlockCopy (Parent);
2358 if (variables != null){
2359 target.variables = new Hashtable ();
2361 foreach (DictionaryEntry de in variables){
2362 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2363 target.variables [de.Key] = newlocal;
2364 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2368 target.statements = new ArrayList (statements.Count);
2369 foreach (Statement s in statements)
2370 target.statements.Add (s.Clone (clonectx));
2372 if (target.children != null){
2373 target.children = new ArrayList (children.Count);
2374 foreach (Block b in children){
2375 target.children.Add (clonectx.LookupBlock (b));
2380 // TODO: labels, switch_block, constants (?), anonymous_children
2385 public class ExplicitBlock : Block {
2386 public ExplicitBlock (Block parent, Location start, Location end)
2387 : this (parent, (Flags) 0, start, end)
2391 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2392 : base (parent, flags, start, end)
2394 this.Explicit = this;
2397 HybridDictionary known_variables;
2400 // Marks a variable with name @name as being used in this or a child block.
2401 // If a variable name has been used in a child block, it's illegal to
2402 // declare a variable with the same name in the current block.
2404 internal void AddKnownVariable (string name, IKnownVariable info)
2406 if (known_variables == null)
2407 known_variables = new HybridDictionary();
2409 known_variables [name] = info;
2412 Parent.Explicit.AddKnownVariable (name, info);
2415 internal IKnownVariable GetKnownVariable (string name)
2417 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2420 protected override void CloneTo (CloneContext clonectx, Statement t)
2422 ExplicitBlock target = (ExplicitBlock) t;
2423 target.known_variables = null;
2424 base.CloneTo (clonectx, t);
2428 public class ToplevelParameterInfo : IKnownVariable {
2429 public readonly ToplevelBlock Block;
2430 public readonly int Index;
2431 public VariableInfo VariableInfo;
2433 Block IKnownVariable.Block {
2434 get { return Block; }
2436 public Parameter Parameter {
2437 get { return Block.Parameters [Index]; }
2439 public Location Location {
2440 get { return Parameter.Location; }
2443 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2451 // A toplevel block contains extra information, the split is done
2452 // only to separate information that would otherwise bloat the more
2453 // lightweight Block.
2455 // In particular, this was introduced when the support for Anonymous
2456 // Methods was implemented.
2458 public class ToplevelBlock : ExplicitBlock {
2459 GenericMethod generic;
2460 FlowBranchingToplevel top_level_branching;
2461 AnonymousContainer anonymous_container;
2462 RootScopeInfo root_scope;
2463 Parameters parameters;
2464 ToplevelParameterInfo[] parameter_info;
2466 public bool HasVarargs {
2467 get { return (flags & Flags.HasVarargs) != 0; }
2468 set { flags |= Flags.HasVarargs; }
2471 public bool IsIterator {
2472 get { return (flags & Flags.IsIterator) != 0; }
2476 // The parameters for the block.
2478 public Parameters Parameters {
2479 get { return parameters; }
2482 public bool CompleteContexts (EmitContext ec)
2484 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2486 if (root_scope != null)
2487 root_scope.LinkScopes ();
2489 if (Parent == null && root_scope != null) {
2490 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2492 if (root_scope.DefineType () == null)
2494 if (!root_scope.ResolveType ())
2496 if (!root_scope.ResolveMembers ())
2498 if (!root_scope.DefineMembers ())
2505 public GenericMethod GenericMethod {
2506 get { return generic; }
2509 public ToplevelBlock Container {
2510 get { return Parent == null ? null : Parent.Toplevel; }
2513 public AnonymousContainer AnonymousContainer {
2514 get { return anonymous_container; }
2515 set { anonymous_container = value; }
2518 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2519 this (parent, (Flags) 0, parameters, start)
2523 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2524 this (parent, parameters, start)
2526 this.generic = generic;
2529 public ToplevelBlock (Parameters parameters, Location start) :
2530 this (null, (Flags) 0, parameters, start)
2534 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2535 this (null, flags, parameters, start)
2539 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2540 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2541 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2542 base (null, flags, start, Location.Null)
2544 this.Toplevel = this;
2546 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2547 this.Parent = parent;
2549 parent.AddAnonymousChild (this);
2551 if (this.parameters.Count != 0)
2552 ProcessParameters ();
2555 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2559 protected override void CloneTo (CloneContext clonectx, Statement t)
2561 ToplevelBlock target = (ToplevelBlock) t;
2562 base.CloneTo (clonectx, t);
2564 if (parameters.Count != 0)
2565 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2566 for (int i = 0; i < parameters.Count; ++i)
2567 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2570 public bool CheckError158 (string name, Location loc)
2572 if (AnonymousChildren != null) {
2573 foreach (ToplevelBlock child in AnonymousChildren) {
2574 if (!child.CheckError158 (name, loc))
2579 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2580 if (!c.DoCheckError158 (name, loc))
2587 public virtual Expression GetTransparentIdentifier (string name)
2592 void ProcessParameters ()
2594 int n = parameters.Count;
2595 parameter_info = new ToplevelParameterInfo [n];
2596 for (int i = 0; i < n; ++i) {
2597 parameter_info [i] = new ToplevelParameterInfo (this, i);
2599 Parameter p = parameters [i];
2603 string name = p.Name;
2604 LocalInfo vi = GetLocalInfo (name);
2606 Report.SymbolRelatedToPreviousError (vi.Location, name);
2607 Error_AlreadyDeclared (loc, name, "parent or current");
2611 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2613 Report.SymbolRelatedToPreviousError (pi.Location, name);
2614 Error_AlreadyDeclared (loc, name, "parent or current");
2618 AddKnownVariable (name, parameter_info [i]);
2621 // mark this block as "used" so that we create local declarations in a sub-block
2622 // FIXME: This appears to uncover a lot of bugs
2626 bool DoCheckError158 (string name, Location loc)
2628 LabeledStatement s = LookupLabel (name);
2630 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2631 Error_158 (name, loc);
2638 public RootScopeInfo CreateRootScope (TypeContainer host)
2640 if (root_scope != null)
2643 if (Container == null)
2644 root_scope = new RootScopeInfo (
2645 this, host, generic, StartLocation);
2647 if (scope_info != null)
2648 throw new InternalErrorException ();
2650 scope_info = root_scope;
2654 public override Expression CreateExpressionTree (EmitContext ec)
2656 return ((Statement) statements [0]).CreateExpressionTree (ec);
2659 public void CreateIteratorHost (RootScopeInfo root)
2661 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2663 if (Parent != null || root_scope != null)
2664 throw new InternalErrorException ();
2666 scope_info = root_scope = root;
2669 public RootScopeInfo RootScope {
2671 if (root_scope != null)
2673 else if (Container != null)
2674 return Container.RootScope;
2680 public FlowBranchingToplevel TopLevelBranching {
2681 get { return top_level_branching; }
2685 // This is used if anonymous methods are used inside an iterator
2686 // (see 2test-22.cs for an example).
2688 // The AnonymousMethod is created while parsing - at a time when we don't
2689 // know yet that we're inside an iterator, so it's `Container' is initially
2690 // null. Later on, when resolving the iterator, we need to move the
2691 // anonymous method into that iterator.
2693 public void ReParent (ToplevelBlock new_parent)
2695 if ((flags & Flags.VariablesInitialized) != 0)
2696 throw new InternalErrorException ("block has already been resolved");
2698 Parent = new_parent;
2702 // Returns a `ParameterReference' for the given name, or null if there
2703 // is no such parameter
2705 public ParameterReference GetParameterReference (string name, Location loc)
2707 ToplevelParameterInfo p = GetParameterInfo (name);
2708 return p == null ? null : new ParameterReference (this, p, loc);
2711 public ToplevelParameterInfo GetParameterInfo (string name)
2714 for (ToplevelBlock t = this; t != null; t = t.Container) {
2715 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2717 return t.parameter_info [idx];
2722 LocalInfo this_variable = null;
2725 // Returns the "this" instance variable of this block.
2726 // See AddThisVariable() for more information.
2728 public LocalInfo ThisVariable {
2729 get { return this_variable; }
2734 // This is used by non-static `struct' constructors which do not have an
2735 // initializer - in this case, the constructor must initialize all of the
2736 // struct's fields. To do this, we add a "this" variable and use the flow
2737 // analysis code to ensure that it's been fully initialized before control
2738 // leaves the constructor.
2740 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2742 if (this_variable == null) {
2743 this_variable = new LocalInfo (ds, this, l);
2744 this_variable.Used = true;
2745 this_variable.IsThis = true;
2747 Variables.Add ("this", this_variable);
2750 return this_variable;
2753 public bool IsThisAssigned (EmitContext ec)
2755 return this_variable == null || this_variable.IsThisAssigned (ec);
2758 public bool ResolveMeta (EmitContext ec, Parameters ip)
2760 int errors = Report.Errors;
2761 int orig_count = parameters.Count;
2763 if (top_level_branching != null)
2769 // Assert: orig_count != parameter.Count => orig_count == 0
2770 if (orig_count != 0 && orig_count != parameters.Count)
2771 throw new InternalErrorException ("parameter information mismatch");
2773 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2775 for (int i = 0; i < orig_count; ++i) {
2776 Parameter.Modifier mod = parameters.ParameterModifier (i);
2778 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2781 VariableInfo vi = new VariableInfo (ip, i, offset);
2782 parameter_info [i].VariableInfo = vi;
2783 offset += vi.Length;
2786 ResolveMeta (ec, offset);
2788 top_level_branching = ec.StartFlowBranching (this);
2790 return Report.Errors == errors;
2794 // Check whether all `out' parameters have been assigned.
2796 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2798 if (vector.IsUnreachable)
2801 int n = parameter_info == null ? 0 : parameter_info.Length;
2803 for (int i = 0; i < n; i++) {
2804 VariableInfo var = parameter_info [i].VariableInfo;
2809 if (vector.IsAssigned (var, false))
2812 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2817 public override void EmitMeta (EmitContext ec)
2819 // Avoid declaring an IL variable for this_variable since it is not accessed
2820 // from the generated IL
2821 if (this_variable != null)
2822 Variables.Remove ("this");
2824 parameters.ResolveVariable (this);
2827 public void MakeIterator (Iterator iterator)
2829 flags |= Flags.IsIterator;
2831 Block block = new ExplicitBlock (this, StartLocation, EndLocation);
2832 foreach (Statement stmt in statements)
2833 block.AddStatement (stmt);
2834 statements.Clear ();
2835 statements.Add (new MoveNextStatement (iterator, block));
2838 protected class MoveNextStatement : Statement {
2842 public MoveNextStatement (Iterator iterator, Block block)
2844 this.iterator = iterator;
2846 this.loc = iterator.Location;
2849 public override bool Resolve (EmitContext ec)
2851 return block.Resolve (ec);
2854 protected override void DoEmit (EmitContext ec)
2856 iterator.EmitMoveNext (ec, block);
2860 public override string ToString ()
2862 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2863 root_scope, anonymous_container != null ?
2864 anonymous_container.Scope : null);
2868 public class SwitchLabel {
2875 Label il_label_code;
2876 bool il_label_code_set;
2878 public static readonly object NullStringCase = new object ();
2881 // if expr == null, then it is the default case.
2883 public SwitchLabel (Expression expr, Location l)
2889 public Expression Label {
2895 public object Converted {
2901 public Label GetILLabel (EmitContext ec)
2904 il_label = ec.ig.DefineLabel ();
2905 il_label_set = true;
2910 public Label GetILLabelCode (EmitContext ec)
2912 if (!il_label_code_set){
2913 il_label_code = ec.ig.DefineLabel ();
2914 il_label_code_set = true;
2916 return il_label_code;
2920 // Resolves the expression, reduces it to a literal if possible
2921 // and then converts it to the requested type.
2923 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2925 Expression e = label.Resolve (ec);
2930 Constant c = e as Constant;
2932 Report.Error (150, loc, "A constant value is expected");
2936 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2937 converted = NullStringCase;
2941 if (allow_nullable && c.GetValue () == null) {
2942 converted = NullStringCase;
2946 c = c.ImplicitConversionRequired (required_type, loc);
2950 converted = c.GetValue ();
2954 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2957 if (converted == null)
2959 else if (converted == NullStringCase)
2961 else if (TypeManager.IsEnumType (switch_type))
2962 label = TypeManager.CSharpEnumValue (switch_type, converted);
2964 label = converted.ToString ();
2966 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2967 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2970 public SwitchLabel Clone (CloneContext clonectx)
2972 return new SwitchLabel (label.Clone (clonectx), loc);
2976 public class SwitchSection {
2977 // An array of SwitchLabels.
2978 public readonly ArrayList Labels;
2979 public readonly Block Block;
2981 public SwitchSection (ArrayList labels, Block block)
2987 public SwitchSection Clone (CloneContext clonectx)
2989 ArrayList cloned_labels = new ArrayList ();
2991 foreach (SwitchLabel sl in cloned_labels)
2992 cloned_labels.Add (sl.Clone (clonectx));
2994 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
2998 public class Switch : Statement {
2999 public ArrayList Sections;
3000 public Expression Expr;
3003 /// Maps constants whose type type SwitchType to their SwitchLabels.
3005 public IDictionary Elements;
3008 /// The governing switch type
3010 public Type SwitchType;
3015 Label default_target;
3017 Expression new_expr;
3019 SwitchSection constant_section;
3020 SwitchSection default_section;
3024 // Nullable Types support for GMCS.
3026 Nullable.Unwrap unwrap;
3028 protected bool HaveUnwrap {
3029 get { return unwrap != null; }
3032 protected bool HaveUnwrap {
3033 get { return false; }
3038 // The types allowed to be implicitly cast from
3039 // on the governing type
3041 static Type [] allowed_types;
3043 public Switch (Expression e, ArrayList sects, Location l)
3050 public bool GotDefault {
3052 return default_section != null;
3056 public Label DefaultTarget {
3058 return default_target;
3063 // Determines the governing type for a switch. The returned
3064 // expression might be the expression from the switch, or an
3065 // expression that includes any potential conversions to the
3066 // integral types or to string.
3068 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3070 Type t = TypeManager.DropGenericTypeArguments (expr.Type);
3072 if (t == TypeManager.byte_type ||
3073 t == TypeManager.sbyte_type ||
3074 t == TypeManager.ushort_type ||
3075 t == TypeManager.short_type ||
3076 t == TypeManager.uint32_type ||
3077 t == TypeManager.int32_type ||
3078 t == TypeManager.uint64_type ||
3079 t == TypeManager.int64_type ||
3080 t == TypeManager.char_type ||
3081 t == TypeManager.string_type ||
3082 t == TypeManager.bool_type ||
3083 TypeManager.IsSubclassOf (t, TypeManager.enum_type))
3086 if (allowed_types == null){
3087 allowed_types = new Type [] {
3088 TypeManager.sbyte_type,
3089 TypeManager.byte_type,
3090 TypeManager.short_type,
3091 TypeManager.ushort_type,
3092 TypeManager.int32_type,
3093 TypeManager.uint32_type,
3094 TypeManager.int64_type,
3095 TypeManager.uint64_type,
3096 TypeManager.char_type,
3097 TypeManager.string_type,
3098 TypeManager.bool_type
3103 // Try to find a *user* defined implicit conversion.
3105 // If there is no implicit conversion, or if there are multiple
3106 // conversions, we have to report an error
3108 Expression converted = null;
3109 foreach (Type tt in allowed_types){
3112 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3117 // Ignore over-worked ImplicitUserConversions that do
3118 // an implicit conversion in addition to the user conversion.
3120 if (!(e is UserCast))
3123 if (converted != null){
3124 Report.ExtraInformation (
3126 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3127 TypeManager.CSharpName (expr.Type)));
3137 // Performs the basic sanity checks on the switch statement
3138 // (looks for duplicate keys and non-constant expressions).
3140 // It also returns a hashtable with the keys that we will later
3141 // use to compute the switch tables
3143 bool CheckSwitch (EmitContext ec)
3146 Elements = Sections.Count > 10 ?
3147 (IDictionary)new Hashtable () :
3148 (IDictionary)new ListDictionary ();
3150 foreach (SwitchSection ss in Sections){
3151 foreach (SwitchLabel sl in ss.Labels){
3152 if (sl.Label == null){
3153 if (default_section != null){
3154 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3157 default_section = ss;
3161 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3166 object key = sl.Converted;
3168 Elements.Add (key, sl);
3169 } catch (ArgumentException) {
3170 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3178 void EmitObjectInteger (ILGenerator ig, object k)
3181 IntConstant.EmitInt (ig, (int) k);
3182 else if (k is Constant) {
3183 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3186 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3189 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3191 IntConstant.EmitInt (ig, (int) (long) k);
3192 ig.Emit (OpCodes.Conv_I8);
3195 LongConstant.EmitLong (ig, (long) k);
3197 else if (k is ulong)
3199 ulong ul = (ulong) k;
3202 IntConstant.EmitInt (ig, unchecked ((int) ul));
3203 ig.Emit (OpCodes.Conv_U8);
3207 LongConstant.EmitLong (ig, unchecked ((long) ul));
3211 IntConstant.EmitInt (ig, (int) ((char) k));
3212 else if (k is sbyte)
3213 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3215 IntConstant.EmitInt (ig, (int) ((byte) k));
3216 else if (k is short)
3217 IntConstant.EmitInt (ig, (int) ((short) k));
3218 else if (k is ushort)
3219 IntConstant.EmitInt (ig, (int) ((ushort) k));
3221 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3223 throw new Exception ("Unhandled case");
3226 // structure used to hold blocks of keys while calculating table switch
3227 class KeyBlock : IComparable
3229 public KeyBlock (long _first)
3231 first = last = _first;
3235 public ArrayList element_keys = null;
3236 // how many items are in the bucket
3237 public int Size = 1;
3240 get { return (int) (last - first + 1); }
3242 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3244 return kb_last.last - kb_first.first + 1;
3246 public int CompareTo (object obj)
3248 KeyBlock kb = (KeyBlock) obj;
3249 int nLength = Length;
3250 int nLengthOther = kb.Length;
3251 if (nLengthOther == nLength)
3252 return (int) (kb.first - first);
3253 return nLength - nLengthOther;
3258 /// This method emits code for a lookup-based switch statement (non-string)
3259 /// Basically it groups the cases into blocks that are at least half full,
3260 /// and then spits out individual lookup opcodes for each block.
3261 /// It emits the longest blocks first, and short blocks are just
3262 /// handled with direct compares.
3264 /// <param name="ec"></param>
3265 /// <param name="val"></param>
3266 /// <returns></returns>
3267 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3269 int element_count = Elements.Count;
3270 object [] element_keys = new object [element_count];
3271 Elements.Keys.CopyTo (element_keys, 0);
3272 Array.Sort (element_keys);
3274 // initialize the block list with one element per key
3275 ArrayList key_blocks = new ArrayList ();
3276 foreach (object key in element_keys)
3277 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3279 KeyBlock current_kb;
3280 // iteratively merge the blocks while they are at least half full
3281 // there's probably a really cool way to do this with a tree...
3282 while (key_blocks.Count > 1)
3284 ArrayList key_blocks_new = new ArrayList ();
3285 current_kb = (KeyBlock) key_blocks [0];
3286 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3288 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3289 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3292 current_kb.last = kb.last;
3293 current_kb.Size += kb.Size;
3297 // start a new block
3298 key_blocks_new.Add (current_kb);
3302 key_blocks_new.Add (current_kb);
3303 if (key_blocks.Count == key_blocks_new.Count)
3305 key_blocks = key_blocks_new;
3308 // initialize the key lists
3309 foreach (KeyBlock kb in key_blocks)
3310 kb.element_keys = new ArrayList ();
3312 // fill the key lists
3314 if (key_blocks.Count > 0) {
3315 current_kb = (KeyBlock) key_blocks [0];
3316 foreach (object key in element_keys)
3318 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3319 System.Convert.ToInt64 (key) > current_kb.last;
3321 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3322 current_kb.element_keys.Add (key);
3326 // sort the blocks so we can tackle the largest ones first
3329 // okay now we can start...
3330 ILGenerator ig = ec.ig;
3331 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3332 Label lbl_default = ig.DefineLabel ();
3334 Type type_keys = null;
3335 if (element_keys.Length > 0)
3336 type_keys = element_keys [0].GetType (); // used for conversions
3340 if (TypeManager.IsEnumType (SwitchType))
3341 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3343 compare_type = SwitchType;
3345 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3347 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3348 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3351 foreach (object key in kb.element_keys)
3353 ig.Emit (OpCodes.Ldloc, val);
3354 EmitObjectInteger (ig, key);
3355 SwitchLabel sl = (SwitchLabel) Elements [key];
3356 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3361 // TODO: if all the keys in the block are the same and there are
3362 // no gaps/defaults then just use a range-check.
3363 if (compare_type == TypeManager.int64_type ||
3364 compare_type == TypeManager.uint64_type)
3366 // TODO: optimize constant/I4 cases
3368 // check block range (could be > 2^31)
3369 ig.Emit (OpCodes.Ldloc, val);
3370 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3371 ig.Emit (OpCodes.Blt, lbl_default);
3372 ig.Emit (OpCodes.Ldloc, val);
3373 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3374 ig.Emit (OpCodes.Bgt, lbl_default);
3377 ig.Emit (OpCodes.Ldloc, val);
3380 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3381 ig.Emit (OpCodes.Sub);
3383 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3388 ig.Emit (OpCodes.Ldloc, val);
3389 int first = (int) kb.first;
3392 IntConstant.EmitInt (ig, first);
3393 ig.Emit (OpCodes.Sub);
3397 IntConstant.EmitInt (ig, -first);
3398 ig.Emit (OpCodes.Add);
3402 // first, build the list of labels for the switch
3404 int cJumps = kb.Length;
3405 Label [] switch_labels = new Label [cJumps];
3406 for (int iJump = 0; iJump < cJumps; iJump++)
3408 object key = kb.element_keys [iKey];
3409 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3411 SwitchLabel sl = (SwitchLabel) Elements [key];
3412 switch_labels [iJump] = sl.GetILLabel (ec);
3416 switch_labels [iJump] = lbl_default;
3418 // emit the switch opcode
3419 ig.Emit (OpCodes.Switch, switch_labels);
3422 // mark the default for this block
3424 ig.MarkLabel (lbl_default);
3427 // TODO: find the default case and emit it here,
3428 // to prevent having to do the following jump.
3429 // make sure to mark other labels in the default section
3431 // the last default just goes to the end
3432 ig.Emit (OpCodes.Br, lbl_default);
3434 // now emit the code for the sections
3435 bool found_default = false;
3436 bool found_null = false;
3437 foreach (SwitchSection ss in Sections)
3439 foreach (SwitchLabel sl in ss.Labels)
3440 if (sl.Converted == SwitchLabel.NullStringCase)
3444 foreach (SwitchSection ss in Sections)
3446 foreach (SwitchLabel sl in ss.Labels)
3448 ig.MarkLabel (sl.GetILLabel (ec));
3449 ig.MarkLabel (sl.GetILLabelCode (ec));
3450 if (sl.Converted == SwitchLabel.NullStringCase)
3451 ig.MarkLabel (null_target);
3452 else if (sl.Label == null) {
3453 ig.MarkLabel (lbl_default);
3454 found_default = true;
3456 ig.MarkLabel (null_target);
3462 if (!found_default) {
3463 ig.MarkLabel (lbl_default);
3464 if (HaveUnwrap && !found_null) {
3465 ig.MarkLabel (null_target);
3469 ig.MarkLabel (lbl_end);
3472 // This simple emit switch works, but does not take advantage of the
3474 // TODO: remove non-string logic from here
3475 // TODO: binary search strings?
3477 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3479 ILGenerator ig = ec.ig;
3480 Label end_of_switch = ig.DefineLabel ();
3481 Label next_test = ig.DefineLabel ();
3482 bool first_test = true;
3483 bool pending_goto_end = false;
3484 bool null_marked = false;
3486 int section_count = Sections.Count;
3488 // TODO: implement switch optimization for string by using Hashtable
3489 //if (SwitchType == TypeManager.string_type && section_count > 7)
3490 // Console.WriteLine ("Switch optimization possible " + loc);
3492 ig.Emit (OpCodes.Ldloc, val);
3494 if (Elements.Contains (SwitchLabel.NullStringCase)){
3495 ig.Emit (OpCodes.Brfalse, null_target);
3497 ig.Emit (OpCodes.Brfalse, default_target);
3499 ig.Emit (OpCodes.Ldloc, val);
3500 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3501 ig.Emit (OpCodes.Stloc, val);
3503 for (int section = 0; section < section_count; section++){
3504 SwitchSection ss = (SwitchSection) Sections [section];
3506 if (ss == default_section)
3509 Label sec_begin = ig.DefineLabel ();
3511 ig.Emit (OpCodes.Nop);
3513 if (pending_goto_end)
3514 ig.Emit (OpCodes.Br, end_of_switch);
3516 int label_count = ss.Labels.Count;
3518 for (int label = 0; label < label_count; label++){
3519 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3520 ig.MarkLabel (sl.GetILLabel (ec));
3523 ig.MarkLabel (next_test);
3524 next_test = ig.DefineLabel ();
3527 // If we are the default target
3529 if (sl.Label != null){
3530 object lit = sl.Converted;
3532 if (lit == SwitchLabel.NullStringCase){
3534 if (label + 1 == label_count)
3535 ig.Emit (OpCodes.Br, next_test);
3539 ig.Emit (OpCodes.Ldloc, val);
3540 ig.Emit (OpCodes.Ldstr, (string)lit);
3541 if (label_count == 1)
3542 ig.Emit (OpCodes.Bne_Un, next_test);
3544 if (label+1 == label_count)
3545 ig.Emit (OpCodes.Bne_Un, next_test);
3547 ig.Emit (OpCodes.Beq, sec_begin);
3552 ig.MarkLabel (null_target);
3555 ig.MarkLabel (sec_begin);
3556 foreach (SwitchLabel sl in ss.Labels)
3557 ig.MarkLabel (sl.GetILLabelCode (ec));
3560 pending_goto_end = !ss.Block.HasRet;
3563 ig.MarkLabel (next_test);
3564 ig.MarkLabel (default_target);
3566 ig.MarkLabel (null_target);
3567 if (default_section != null)
3568 default_section.Block.Emit (ec);
3569 ig.MarkLabel (end_of_switch);
3572 SwitchSection FindSection (SwitchLabel label)
3574 foreach (SwitchSection ss in Sections){
3575 foreach (SwitchLabel sl in ss.Labels){
3584 public override bool Resolve (EmitContext ec)
3586 Expr = Expr.Resolve (ec);
3590 new_expr = SwitchGoverningType (ec, Expr);
3593 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3594 unwrap = Nullable.Unwrap.Create (Expr, ec);
3598 new_expr = SwitchGoverningType (ec, unwrap);
3602 if (new_expr == null){
3603 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3608 SwitchType = new_expr.Type;
3610 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3611 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3615 if (!CheckSwitch (ec))
3619 Elements.Remove (SwitchLabel.NullStringCase);
3621 Switch old_switch = ec.Switch;
3623 ec.Switch.SwitchType = SwitchType;
3625 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3626 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3628 is_constant = new_expr is Constant;
3630 object key = ((Constant) new_expr).GetValue ();
3631 SwitchLabel label = (SwitchLabel) Elements [key];
3633 constant_section = FindSection (label);
3634 if (constant_section == null)
3635 constant_section = default_section;
3640 foreach (SwitchSection ss in Sections){
3642 ec.CurrentBranching.CreateSibling (
3643 null, FlowBranching.SiblingType.SwitchSection);
3647 if (is_constant && (ss != constant_section)) {
3648 // If we're a constant switch, we're only emitting
3649 // one single section - mark all the others as
3651 ec.CurrentBranching.CurrentUsageVector.Goto ();
3652 if (!ss.Block.ResolveUnreachable (ec, true)) {
3656 if (!ss.Block.Resolve (ec))
3661 if (default_section == null)
3662 ec.CurrentBranching.CreateSibling (
3663 null, FlowBranching.SiblingType.SwitchSection);
3665 ec.EndFlowBranching ();
3666 ec.Switch = old_switch;
3668 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3670 if (TypeManager.string_isinterned_string == null) {
3671 TypeManager.string_isinterned_string = TypeManager.GetPredefinedMethod (TypeManager.string_type,
3672 "IsInterned", loc, TypeManager.string_type);
3678 protected override void DoEmit (EmitContext ec)
3680 ILGenerator ig = ec.ig;
3682 default_target = ig.DefineLabel ();
3683 null_target = ig.DefineLabel ();
3685 // Store variable for comparission purposes
3688 value = ig.DeclareLocal (SwitchType);
3690 unwrap.EmitCheck (ec);
3691 ig.Emit (OpCodes.Brfalse, null_target);
3693 ig.Emit (OpCodes.Stloc, value);
3695 } else if (!is_constant) {
3696 value = ig.DeclareLocal (SwitchType);
3698 ig.Emit (OpCodes.Stloc, value);
3703 // Setup the codegen context
3705 Label old_end = ec.LoopEnd;
3706 Switch old_switch = ec.Switch;
3708 ec.LoopEnd = ig.DefineLabel ();
3713 if (constant_section != null)
3714 constant_section.Block.Emit (ec);
3715 } else if (SwitchType == TypeManager.string_type)
3716 SimpleSwitchEmit (ec, value);
3718 TableSwitchEmit (ec, value);
3720 // Restore context state.
3721 ig.MarkLabel (ec.LoopEnd);
3724 // Restore the previous context
3726 ec.LoopEnd = old_end;
3727 ec.Switch = old_switch;
3730 protected override void CloneTo (CloneContext clonectx, Statement t)
3732 Switch target = (Switch) t;
3734 target.Expr = Expr.Clone (clonectx);
3735 target.Sections = new ArrayList ();
3736 foreach (SwitchSection ss in Sections){
3737 target.Sections.Add (ss.Clone (clonectx));
3742 public abstract class ExceptionStatement : Statement
3744 public abstract void EmitFinally (EmitContext ec);
3746 protected bool emit_finally = true;
3747 ArrayList parent_vectors;
3749 protected void DoEmitFinally (EmitContext ec)
3752 ec.ig.BeginFinallyBlock ();
3753 else if (ec.InIterator)
3754 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3758 protected void ResolveFinally (FlowBranchingException branching)
3760 emit_finally = branching.EmitFinally;
3762 branching.Parent.StealFinallyClauses (ref parent_vectors);
3766 public class Lock : ExceptionStatement {
3768 public Statement Statement;
3769 TemporaryVariable temp;
3771 public Lock (Expression expr, Statement stmt, Location l)
3778 public override bool Resolve (EmitContext ec)
3780 expr = expr.Resolve (ec);
3784 if (expr.Type.IsValueType){
3785 Report.Error (185, loc,
3786 "`{0}' is not a reference type as required by the lock statement",
3787 TypeManager.CSharpName (expr.Type));
3791 FlowBranchingException branching = ec.StartFlowBranching (this);
3792 bool ok = Statement.Resolve (ec);
3794 ResolveFinally (branching);
3796 ec.EndFlowBranching ();
3798 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3799 // So, ensure there's some IL code after the finally block.
3800 ec.NeedReturnLabel ();
3802 // Avoid creating libraries that reference the internal
3805 if (t == TypeManager.null_type)
3806 t = TypeManager.object_type;
3808 temp = new TemporaryVariable (t, loc);
3811 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
3812 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
3813 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
3814 monitor_type, "Enter", loc, TypeManager.object_type);
3815 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
3816 monitor_type, "Exit", loc, TypeManager.object_type);
3822 protected override void DoEmit (EmitContext ec)
3824 ILGenerator ig = ec.ig;
3826 temp.Store (ec, expr);
3828 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3832 ig.BeginExceptionBlock ();
3833 Statement.Emit (ec);
3838 ig.EndExceptionBlock ();
3841 public override void EmitFinally (EmitContext ec)
3844 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3847 protected override void CloneTo (CloneContext clonectx, Statement t)
3849 Lock target = (Lock) t;
3851 target.expr = expr.Clone (clonectx);
3852 target.Statement = Statement.Clone (clonectx);
3856 public class Unchecked : Statement {
3859 public Unchecked (Block b)
3865 public override bool Resolve (EmitContext ec)
3867 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3868 return Block.Resolve (ec);
3871 protected override void DoEmit (EmitContext ec)
3873 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3877 protected override void CloneTo (CloneContext clonectx, Statement t)
3879 Unchecked target = (Unchecked) t;
3881 target.Block = clonectx.LookupBlock (Block);
3885 public class Checked : Statement {
3888 public Checked (Block b)
3891 b.Unchecked = false;
3894 public override bool Resolve (EmitContext ec)
3896 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3897 return Block.Resolve (ec);
3900 protected override void DoEmit (EmitContext ec)
3902 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3906 protected override void CloneTo (CloneContext clonectx, Statement t)
3908 Checked target = (Checked) t;
3910 target.Block = clonectx.LookupBlock (Block);
3914 public class Unsafe : Statement {
3917 public Unsafe (Block b)
3920 Block.Unsafe = true;
3923 public override bool Resolve (EmitContext ec)
3925 using (ec.With (EmitContext.Flags.InUnsafe, true))
3926 return Block.Resolve (ec);
3929 protected override void DoEmit (EmitContext ec)
3931 using (ec.With (EmitContext.Flags.InUnsafe, true))
3934 protected override void CloneTo (CloneContext clonectx, Statement t)
3936 Unsafe target = (Unsafe) t;
3938 target.Block = clonectx.LookupBlock (Block);
3945 public class Fixed : Statement {
3947 ArrayList declarators;
3948 Statement statement;
3953 abstract class Emitter
3955 protected LocalInfo vi;
3956 protected Expression converted;
3958 protected Emitter (Expression expr, LocalInfo li)
3964 public abstract void Emit (EmitContext ec);
3965 public abstract void EmitExit (EmitContext ec);
3968 class ExpressionEmitter : Emitter {
3969 public ExpressionEmitter (Expression converted, LocalInfo li) :
3970 base (converted, li)
3974 public override void Emit (EmitContext ec) {
3976 // Store pointer in pinned location
3978 converted.Emit (ec);
3979 vi.Variable.EmitAssign (ec);
3982 public override void EmitExit (EmitContext ec)
3984 ec.ig.Emit (OpCodes.Ldc_I4_0);
3985 ec.ig.Emit (OpCodes.Conv_U);
3986 vi.Variable.EmitAssign (ec);
3990 class StringEmitter : Emitter {
3991 LocalBuilder pinned_string;
3994 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4000 public override void Emit (EmitContext ec)
4002 ILGenerator ig = ec.ig;
4003 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4005 converted.Emit (ec);
4006 ig.Emit (OpCodes.Stloc, pinned_string);
4008 Expression sptr = new StringPtr (pinned_string, loc);
4009 converted = Convert.ImplicitConversionRequired (
4010 ec, sptr, vi.VariableType, loc);
4012 if (converted == null)
4015 converted.Emit (ec);
4016 vi.Variable.EmitAssign (ec);
4019 public override void EmitExit (EmitContext ec)
4021 ec.ig.Emit (OpCodes.Ldnull);
4022 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4026 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4029 declarators = decls;
4034 public Statement Statement {
4035 get { return statement; }
4038 public override bool Resolve (EmitContext ec)
4041 Expression.UnsafeError (loc);
4045 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4046 if (texpr == null) {
4047 if (type is VarExpr)
4048 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4053 expr_type = texpr.Type;
4055 data = new Emitter [declarators.Count];
4057 if (!expr_type.IsPointer){
4058 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4063 foreach (Pair p in declarators){
4064 LocalInfo vi = (LocalInfo) p.First;
4065 Expression e = (Expression) p.Second;
4067 vi.VariableInfo.SetAssigned (ec);
4068 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4071 // The rules for the possible declarators are pretty wise,
4072 // but the production on the grammar is more concise.
4074 // So we have to enforce these rules here.
4076 // We do not resolve before doing the case 1 test,
4077 // because the grammar is explicit in that the token &
4078 // is present, so we need to test for this particular case.
4082 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4087 // Case 1: & object.
4089 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4090 Expression child = ((Unary) e).Expr;
4092 if (child is ParameterReference || child is LocalVariableReference){
4095 "No need to use fixed statement for parameters or " +
4096 "local variable declarations (address is already " +
4101 ec.InFixedInitializer = true;
4103 ec.InFixedInitializer = false;
4107 child = ((Unary) e).Expr;
4109 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4112 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4113 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4117 data [i] = new ExpressionEmitter (e, vi);
4123 ec.InFixedInitializer = true;
4125 ec.InFixedInitializer = false;
4132 if (e.Type.IsArray){
4133 Type array_type = TypeManager.GetElementType (e.Type);
4136 // Provided that array_type is unmanaged,
4138 if (!TypeManager.VerifyUnManaged (array_type, loc))
4142 // and T* is implicitly convertible to the
4143 // pointer type given in the fixed statement.
4145 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4147 Expression converted = Convert.ImplicitConversionRequired (
4148 ec, array_ptr, vi.VariableType, loc);
4149 if (converted == null)
4153 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4155 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4156 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4157 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4161 converted = converted.Resolve (ec);
4163 data [i] = new ExpressionEmitter (converted, vi);
4172 if (e.Type == TypeManager.string_type){
4173 data [i] = new StringEmitter (e, vi, loc);
4178 // Case 4: fixed buffer
4179 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4180 if (fixed_buffer_ptr != null) {
4181 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4186 // For other cases, flag a `this is already fixed expression'
4188 if (e is LocalVariableReference || e is ParameterReference ||
4189 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4191 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4195 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4199 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4200 bool ok = statement.Resolve (ec);
4201 bool flow_unreachable = ec.EndFlowBranching ();
4202 has_ret = flow_unreachable;
4207 protected override void DoEmit (EmitContext ec)
4209 for (int i = 0; i < data.Length; i++) {
4213 statement.Emit (ec);
4219 // Clear the pinned variable
4221 for (int i = 0; i < data.Length; i++) {
4222 data [i].EmitExit (ec);
4226 protected override void CloneTo (CloneContext clonectx, Statement t)
4228 Fixed target = (Fixed) t;
4230 target.type = type.Clone (clonectx);
4231 target.declarators = new ArrayList (declarators.Count);
4232 foreach (Pair p in declarators) {
4233 LocalInfo vi = (LocalInfo) p.First;
4234 Expression e = (Expression) p.Second;
4236 target.declarators.Add (
4237 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4240 target.statement = statement.Clone (clonectx);
4244 public class Catch : Statement {
4245 public readonly string Name;
4247 public Block VarBlock;
4249 Expression type_expr;
4252 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4257 VarBlock = var_block;
4261 public Type CatchType {
4267 public bool IsGeneral {
4269 return type_expr == null;
4273 protected override void DoEmit(EmitContext ec)
4275 ILGenerator ig = ec.ig;
4277 if (CatchType != null)
4278 ig.BeginCatchBlock (CatchType);
4280 ig.BeginCatchBlock (TypeManager.object_type);
4282 if (VarBlock != null)
4286 LocalInfo vi = Block.GetLocalInfo (Name);
4288 throw new Exception ("Variable does not exist in this block");
4290 if (vi.Variable.NeedsTemporary) {
4291 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4292 ig.Emit (OpCodes.Stloc, e);
4294 vi.Variable.EmitInstance (ec);
4295 ig.Emit (OpCodes.Ldloc, e);
4296 vi.Variable.EmitAssign (ec);
4298 vi.Variable.EmitAssign (ec);
4300 ig.Emit (OpCodes.Pop);
4305 public override bool Resolve (EmitContext ec)
4307 using (ec.With (EmitContext.Flags.InCatch, true)) {
4308 if (type_expr != null) {
4309 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4315 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4316 Error (155, "The type caught or thrown must be derived from System.Exception");
4322 if (!Block.Resolve (ec))
4325 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4326 // emit the "unused variable" warnings.
4327 if (VarBlock != null)
4328 return VarBlock.Resolve (ec);
4334 protected override void CloneTo (CloneContext clonectx, Statement t)
4336 Catch target = (Catch) t;
4338 if (type_expr != null)
4339 target.type_expr = type_expr.Clone (clonectx);
4340 if (VarBlock != null)
4341 target.VarBlock = clonectx.LookupBlock (VarBlock);
4342 target.Block = clonectx.LookupBlock (Block);
4346 public class Try : ExceptionStatement {
4347 public Block Fini, Block;
4348 public ArrayList Specific;
4349 public Catch General;
4351 bool need_exc_block;
4354 // specific, general and fini might all be null.
4356 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4358 if (specific == null && general == null){
4359 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4363 this.Specific = specific;
4364 this.General = general;
4369 public override bool Resolve (EmitContext ec)
4373 FlowBranchingException branching = ec.StartFlowBranching (this);
4375 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4377 if (!Block.Resolve (ec))
4380 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4382 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4384 Type[] prev_catches = new Type [Specific.Count];
4386 foreach (Catch c in Specific){
4387 ec.CurrentBranching.CreateSibling (
4388 c.Block, FlowBranching.SiblingType.Catch);
4390 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4392 if (c.Name != null) {
4393 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4395 throw new Exception ();
4397 vi.VariableInfo = null;
4400 if (!c.Resolve (ec))
4403 Type resolved_type = c.CatchType;
4404 for (int ii = 0; ii < last_index; ++ii) {
4405 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4406 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4411 prev_catches [last_index++] = resolved_type;
4412 need_exc_block = true;
4415 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4417 if (General != null){
4418 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4419 foreach (Catch c in Specific){
4420 if (c.CatchType == TypeManager.exception_type) {
4421 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'");
4426 ec.CurrentBranching.CreateSibling (
4427 General.Block, FlowBranching.SiblingType.Catch);
4429 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4431 if (!General.Resolve (ec))
4434 need_exc_block = true;
4437 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4441 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4443 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4444 using (ec.With (EmitContext.Flags.InFinally, true)) {
4445 if (!Fini.Resolve (ec))
4450 need_exc_block = true;
4453 if (ec.InIterator) {
4454 ResolveFinally (branching);
4455 need_exc_block |= emit_finally;
4457 emit_finally = Fini != null;
4459 ec.EndFlowBranching ();
4461 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4462 // So, ensure there's some IL code after the finally block.
4463 ec.NeedReturnLabel ();
4465 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4467 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4472 protected override void DoEmit (EmitContext ec)
4474 ILGenerator ig = ec.ig;
4477 ig.BeginExceptionBlock ();
4480 foreach (Catch c in Specific)
4483 if (General != null)
4488 ig.EndExceptionBlock ();
4491 public override void EmitFinally (EmitContext ec)
4497 public bool HasCatch
4500 return General != null || Specific.Count > 0;
4504 protected override void CloneTo (CloneContext clonectx, Statement t)
4506 Try target = (Try) t;
4508 target.Block = clonectx.LookupBlock (Block);
4510 target.Fini = clonectx.LookupBlock (Fini);
4511 if (General != null)
4512 target.General = (Catch) General.Clone (clonectx);
4513 if (Specific != null){
4514 target.Specific = new ArrayList ();
4515 foreach (Catch c in Specific)
4516 target.Specific.Add (c.Clone (clonectx));
4521 public class Using : ExceptionStatement {
4522 object expression_or_block;
4523 public Statement Statement;
4527 Expression [] resolved_vars;
4528 Expression [] converted_vars;
4529 Expression [] assign;
4530 TemporaryVariable local_copy;
4532 public Using (object expression_or_block, Statement stmt, Location l)
4534 this.expression_or_block = expression_or_block;
4540 // Resolves for the case of using using a local variable declaration.
4542 bool ResolveLocalVariableDecls (EmitContext ec)
4544 resolved_vars = new Expression[var_list.Count];
4545 assign = new Expression [var_list.Count];
4546 converted_vars = new Expression[var_list.Count];
4548 for (int i = 0; i < assign.Length; ++i) {
4549 DictionaryEntry e = (DictionaryEntry) var_list [i];
4550 Expression var = (Expression) e.Key;
4551 Expression new_expr = (Expression) e.Value;
4553 Expression a = new Assign (var, new_expr, loc);
4558 resolved_vars [i] = var;
4561 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4562 converted_vars [i] = var;
4566 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4568 Error_IsNotConvertibleToIDisposable (var);
4572 converted_vars [i] = a;
4578 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4580 Report.SymbolRelatedToPreviousError (expr.Type);
4581 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4582 expr.GetSignatureForError ());
4585 bool ResolveExpression (EmitContext ec)
4587 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4588 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4589 Error_IsNotConvertibleToIDisposable (expr);
4594 local_copy = new TemporaryVariable (expr_type, loc);
4595 local_copy.Resolve (ec);
4601 // Emits the code for the case of using using a local variable declaration.
4603 void EmitLocalVariableDecls (EmitContext ec)
4605 ILGenerator ig = ec.ig;
4608 for (i = 0; i < assign.Length; i++) {
4609 ExpressionStatement es = assign [i] as ExpressionStatement;
4612 es.EmitStatement (ec);
4614 assign [i].Emit (ec);
4615 ig.Emit (OpCodes.Pop);
4619 ig.BeginExceptionBlock ();
4621 Statement.Emit (ec);
4623 var_list.Reverse ();
4628 void EmitLocalVariableDeclFinally (EmitContext ec)
4630 ILGenerator ig = ec.ig;
4632 int i = assign.Length;
4633 for (int ii = 0; ii < var_list.Count; ++ii){
4634 Expression var = resolved_vars [--i];
4635 Label skip = ig.DefineLabel ();
4638 ig.BeginFinallyBlock ();
4640 if (!var.Type.IsValueType) {
4642 ig.Emit (OpCodes.Brfalse, skip);
4643 converted_vars [i].Emit (ec);
4644 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4646 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4648 if (!(ml is MethodGroupExpr)) {
4650 ig.Emit (OpCodes.Box, var.Type);
4651 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4653 MethodInfo mi = null;
4655 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4656 if (TypeManager.GetParameterData (mk).Count == 0) {
4663 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4667 IMemoryLocation mloc = (IMemoryLocation) var;
4669 mloc.AddressOf (ec, AddressOp.Load);
4670 ig.Emit (OpCodes.Call, mi);
4674 ig.MarkLabel (skip);
4677 ig.EndExceptionBlock ();
4679 ig.BeginFinallyBlock ();
4684 void EmitExpression (EmitContext ec)
4687 // Make a copy of the expression and operate on that.
4689 ILGenerator ig = ec.ig;
4691 local_copy.Store (ec, expr);
4694 ig.BeginExceptionBlock ();
4696 Statement.Emit (ec);
4700 ig.EndExceptionBlock ();
4703 void EmitExpressionFinally (EmitContext ec)
4705 ILGenerator ig = ec.ig;
4706 if (!expr_type.IsValueType) {
4707 Label skip = ig.DefineLabel ();
4708 local_copy.Emit (ec);
4709 ig.Emit (OpCodes.Brfalse, skip);
4710 local_copy.Emit (ec);
4711 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4712 ig.MarkLabel (skip);
4714 Expression ml = Expression.MemberLookup (
4715 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4716 "Dispose", Location.Null);
4718 if (!(ml is MethodGroupExpr)) {
4719 local_copy.Emit (ec);
4720 ig.Emit (OpCodes.Box, expr_type);
4721 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4723 MethodInfo mi = null;
4725 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4726 if (TypeManager.GetParameterData (mk).Count == 0) {
4733 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4737 local_copy.AddressOf (ec, AddressOp.Load);
4738 ig.Emit (OpCodes.Call, mi);
4743 public override bool Resolve (EmitContext ec)
4745 if (expression_or_block is DictionaryEntry){
4746 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4747 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4749 if (!ResolveLocalVariableDecls (ec))
4752 } else if (expression_or_block is Expression){
4753 expr = (Expression) expression_or_block;
4755 expr = expr.Resolve (ec);
4759 expr_type = expr.Type;
4761 if (!ResolveExpression (ec))
4765 FlowBranchingException branching = ec.StartFlowBranching (this);
4767 bool ok = Statement.Resolve (ec);
4769 ResolveFinally (branching);
4771 ec.EndFlowBranching ();
4773 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4774 // So, ensure there's some IL code after the finally block.
4775 ec.NeedReturnLabel ();
4777 if (TypeManager.void_dispose_void == null) {
4778 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4779 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4785 protected override void DoEmit (EmitContext ec)
4787 if (expression_or_block is DictionaryEntry)
4788 EmitLocalVariableDecls (ec);
4789 else if (expression_or_block is Expression)
4790 EmitExpression (ec);
4793 public override void EmitFinally (EmitContext ec)
4795 if (expression_or_block is DictionaryEntry)
4796 EmitLocalVariableDeclFinally (ec);
4797 else if (expression_or_block is Expression)
4798 EmitExpressionFinally (ec);
4801 protected override void CloneTo (CloneContext clonectx, Statement t)
4803 Using target = (Using) t;
4805 if (expression_or_block is Expression) {
4806 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4808 DictionaryEntry de = (DictionaryEntry) expression_or_block;
4809 ArrayList var_list = (ArrayList) de.Value;
4810 ArrayList target_var_list = new ArrayList (var_list.Count);
4812 foreach (DictionaryEntry de_variable in var_list)
4813 target_var_list.Add (new DictionaryEntry (
4814 ((Expression) de_variable.Key).Clone (clonectx),
4815 ((Expression) de_variable.Value).Clone (clonectx)));
4817 target.expression_or_block = new DictionaryEntry (
4818 ((Expression) de.Key).Clone (clonectx),
4822 target.Statement = Statement.Clone (clonectx);
4827 /// Implementation of the foreach C# statement
4829 public class Foreach : Statement {
4831 Expression variable;
4833 Statement statement;
4835 CollectionForeach collection;
4837 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4838 Statement stmt, Location l)
4841 this.variable = var;
4847 public Statement Statement {
4848 get { return statement; }
4851 public override bool Resolve (EmitContext ec)
4853 expr = expr.Resolve (ec);
4858 Report.Error (186, loc, "Use of null is not valid in this context");
4862 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4863 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4864 expr.ExprClassName);
4869 // We need an instance variable. Not sure this is the best
4870 // way of doing this.
4872 // FIXME: When we implement propertyaccess, will those turn
4873 // out to return values in ExprClass? I think they should.
4875 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4876 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4877 collection.Error_Enumerator ();
4881 if (expr.Type.IsArray) {
4882 array = new ArrayForeach (type, variable, expr, statement, loc);
4883 return array.Resolve (ec);
4886 collection = new CollectionForeach (type, variable, expr, statement, loc);
4887 return collection.Resolve (ec);
4890 protected override void DoEmit (EmitContext ec)
4892 ILGenerator ig = ec.ig;
4894 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4895 ec.LoopBegin = ig.DefineLabel ();
4896 ec.LoopEnd = ig.DefineLabel ();
4898 if (collection != null)
4899 collection.Emit (ec);
4903 ec.LoopBegin = old_begin;
4904 ec.LoopEnd = old_end;
4907 protected class ArrayCounter : TemporaryVariable
4909 public ArrayCounter (Location loc)
4910 : base (TypeManager.int32_type, loc)
4913 public void Initialize (EmitContext ec)
4916 ec.ig.Emit (OpCodes.Ldc_I4_0);
4920 public void Increment (EmitContext ec)
4924 ec.ig.Emit (OpCodes.Ldc_I4_1);
4925 ec.ig.Emit (OpCodes.Add);
4930 protected class ArrayForeach : Statement
4932 Expression variable, expr, conv;
4933 Statement statement;
4935 Expression var_type;
4936 TemporaryVariable[] lengths;
4937 ArrayCounter[] counter;
4940 TemporaryVariable copy;
4943 public ArrayForeach (Expression var_type, Expression var,
4944 Expression expr, Statement stmt, Location l)
4946 this.var_type = var_type;
4947 this.variable = var;
4953 public override bool Resolve (EmitContext ec)
4955 array_type = expr.Type;
4956 rank = array_type.GetArrayRank ();
4958 copy = new TemporaryVariable (array_type, loc);
4961 counter = new ArrayCounter [rank];
4962 lengths = new TemporaryVariable [rank];
4964 ArrayList list = new ArrayList ();
4965 for (int i = 0; i < rank; i++) {
4966 counter [i] = new ArrayCounter (loc);
4967 counter [i].Resolve (ec);
4969 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4970 lengths [i].Resolve (ec);
4972 list.Add (counter [i]);
4975 access = new ElementAccess (copy, list).Resolve (ec);
4979 VarExpr ve = var_type as VarExpr;
4981 // Infer implicitly typed local variable from foreach array type
4982 var_type = new TypeExpression (access.Type, ve.Location);
4985 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4986 if (var_type == null)
4989 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4995 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4996 ec.CurrentBranching.CreateSibling ();
4998 variable = variable.ResolveLValue (ec, conv, loc);
4999 if (variable == null)
5002 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5003 if (!statement.Resolve (ec))
5005 ec.EndFlowBranching ();
5007 // There's no direct control flow from the end of the embedded statement to the end of the loop
5008 ec.CurrentBranching.CurrentUsageVector.Goto ();
5010 ec.EndFlowBranching ();
5015 protected override void DoEmit (EmitContext ec)
5017 ILGenerator ig = ec.ig;
5019 copy.Store (ec, expr);
5021 Label[] test = new Label [rank];
5022 Label[] loop = new Label [rank];
5024 for (int i = 0; i < rank; i++) {
5025 test [i] = ig.DefineLabel ();
5026 loop [i] = ig.DefineLabel ();
5028 lengths [i].EmitThis (ec);
5029 ((ArrayAccess) access).EmitGetLength (ec, i);
5030 lengths [i].EmitStore (ec);
5033 for (int i = 0; i < rank; i++) {
5034 counter [i].Initialize (ec);
5036 ig.Emit (OpCodes.Br, test [i]);
5037 ig.MarkLabel (loop [i]);
5040 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5042 statement.Emit (ec);
5044 ig.MarkLabel (ec.LoopBegin);
5046 for (int i = rank - 1; i >= 0; i--){
5047 counter [i].Increment (ec);
5049 ig.MarkLabel (test [i]);
5050 counter [i].Emit (ec);
5051 lengths [i].Emit (ec);
5052 ig.Emit (OpCodes.Blt, loop [i]);
5055 ig.MarkLabel (ec.LoopEnd);
5059 protected class CollectionForeach : ExceptionStatement
5061 Expression variable, expr;
5062 Statement statement;
5064 TemporaryVariable enumerator;
5068 MethodGroupExpr get_enumerator;
5069 PropertyExpr get_current;
5070 MethodInfo move_next;
5071 Expression var_type;
5072 Type enumerator_type;
5074 bool enumerator_found;
5076 public CollectionForeach (Expression var_type, Expression var,
5077 Expression expr, Statement stmt, Location l)
5079 this.var_type = var_type;
5080 this.variable = var;
5086 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5088 Type return_type = mi.ReturnType;
5090 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5092 // Apply the same optimization as MS: skip the GetEnumerator
5093 // returning an IEnumerator, and use the one returning a
5094 // CharEnumerator instead. This allows us to avoid the
5095 // try-finally block and the boxing.
5100 // Ok, we can access it, now make sure that we can do something
5101 // with this `GetEnumerator'
5104 if (return_type == TypeManager.ienumerator_type ||
5105 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5106 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5108 // If it is not an interface, lets try to find the methods ourselves.
5109 // For example, if we have:
5110 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5111 // We can avoid the iface call. This is a runtime perf boost.
5112 // even bigger if we have a ValueType, because we avoid the cost
5115 // We have to make sure that both methods exist for us to take
5116 // this path. If one of the methods does not exist, we will just
5117 // use the interface. Sadly, this complex if statement is the only
5118 // way I could do this without a goto
5121 if (TypeManager.bool_movenext_void == null) {
5122 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5123 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5126 if (TypeManager.ienumerator_getcurrent == null) {
5127 TypeManager.ienumerator_getcurrent = TypeManager.GetCoreProperty (
5128 TypeManager.ienumerator_type, "Current");
5133 // Prefer a generic enumerator over a non-generic one.
5135 if (return_type.IsInterface && return_type.IsGenericType) {
5136 enumerator_type = return_type;
5137 if (!FetchGetCurrent (ec, return_type))
5138 get_current = new PropertyExpr (
5139 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5140 if (!FetchMoveNext (return_type))
5141 move_next = TypeManager.bool_movenext_void;
5146 if (return_type.IsInterface ||
5147 !FetchMoveNext (return_type) ||
5148 !FetchGetCurrent (ec, return_type)) {
5149 enumerator_type = return_type;
5150 move_next = TypeManager.bool_movenext_void;
5151 get_current = new PropertyExpr (
5152 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5157 // Ok, so they dont return an IEnumerable, we will have to
5158 // find if they support the GetEnumerator pattern.
5161 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5162 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",
5163 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5168 enumerator_type = return_type;
5169 is_disposable = !enumerator_type.IsSealed ||
5170 TypeManager.ImplementsInterface (
5171 enumerator_type, TypeManager.idisposable_type);
5177 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5179 bool FetchMoveNext (Type t)
5181 MemberList move_next_list;
5183 move_next_list = TypeContainer.FindMembers (
5184 t, MemberTypes.Method,
5185 BindingFlags.Public | BindingFlags.Instance,
5186 Type.FilterName, "MoveNext");
5187 if (move_next_list.Count == 0)
5190 foreach (MemberInfo m in move_next_list){
5191 MethodInfo mi = (MethodInfo) m;
5193 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5194 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5204 // Retrieves a `public T get_Current ()' method from the Type `t'
5206 bool FetchGetCurrent (EmitContext ec, Type t)
5208 PropertyExpr pe = Expression.MemberLookup (
5209 ec.ContainerType, t, "Current", MemberTypes.Property,
5210 Expression.AllBindingFlags, loc) as PropertyExpr;
5219 // Retrieves a `public void Dispose ()' method from the Type `t'
5221 static MethodInfo FetchMethodDispose (Type t)
5223 MemberList dispose_list;
5225 dispose_list = TypeContainer.FindMembers (
5226 t, MemberTypes.Method,
5227 BindingFlags.Public | BindingFlags.Instance,
5228 Type.FilterName, "Dispose");
5229 if (dispose_list.Count == 0)
5232 foreach (MemberInfo m in dispose_list){
5233 MethodInfo mi = (MethodInfo) m;
5235 if (TypeManager.GetParameterData (mi).Count == 0){
5236 if (mi.ReturnType == TypeManager.void_type)
5243 public void Error_Enumerator ()
5245 if (enumerator_found) {
5249 Report.Error (1579, loc,
5250 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5251 TypeManager.CSharpName (expr.Type));
5254 bool IsOverride (MethodInfo m)
5256 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5258 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5260 if (m is MethodBuilder)
5263 MethodInfo base_method = m.GetBaseDefinition ();
5264 return base_method != m;
5267 bool TryType (EmitContext ec, Type t)
5269 MethodGroupExpr mg = Expression.MemberLookup (
5270 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5271 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5275 MethodInfo result = null;
5276 MethodInfo tmp_move_next = null;
5277 PropertyExpr tmp_get_cur = null;
5278 Type tmp_enumerator_type = enumerator_type;
5279 foreach (MethodInfo mi in mg.Methods) {
5280 if (TypeManager.GetParameterData (mi).Count != 0)
5283 // Check whether GetEnumerator is public
5284 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5287 if (IsOverride (mi))
5290 enumerator_found = true;
5292 if (!GetEnumeratorFilter (ec, mi))
5295 if (result != null) {
5296 if (TypeManager.IsGenericType (result.ReturnType)) {
5297 if (!TypeManager.IsGenericType (mi.ReturnType))
5300 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5301 Report.SymbolRelatedToPreviousError (t);
5302 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5303 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5304 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5308 // Always prefer generics enumerators
5309 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5310 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5311 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5314 Report.SymbolRelatedToPreviousError (result);
5315 Report.SymbolRelatedToPreviousError (mi);
5316 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5317 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5322 tmp_move_next = move_next;
5323 tmp_get_cur = get_current;
5324 tmp_enumerator_type = enumerator_type;
5325 if (mi.DeclaringType == t)
5329 if (result != null) {
5330 move_next = tmp_move_next;
5331 get_current = tmp_get_cur;
5332 enumerator_type = tmp_enumerator_type;
5333 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5334 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5336 if (t != expr.Type) {
5337 expr = Convert.ExplicitConversion (
5340 throw new InternalErrorException ();
5343 get_enumerator.InstanceExpression = expr;
5344 get_enumerator.IsBase = t != expr.Type;
5352 bool ProbeCollectionType (EmitContext ec, Type t)
5354 int errors = Report.Errors;
5355 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5356 if (TryType (ec, tt))
5361 if (Report.Errors > errors)
5365 // Now try to find the method in the interfaces
5367 Type [] ifaces = TypeManager.GetInterfaces (t);
5368 foreach (Type i in ifaces){
5369 if (TryType (ec, i))
5376 public override bool Resolve (EmitContext ec)
5378 enumerator_type = TypeManager.ienumerator_type;
5379 is_disposable = true;
5381 if (!ProbeCollectionType (ec, expr.Type)) {
5382 Error_Enumerator ();
5386 VarExpr ve = var_type as VarExpr;
5388 // Infer implicitly typed local variable from foreach enumerable type
5389 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5392 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5393 if (var_type == null)
5396 enumerator = new TemporaryVariable (enumerator_type, loc);
5397 enumerator.Resolve (ec);
5399 init = new Invocation (get_enumerator, null);
5400 init = init.Resolve (ec);
5404 Expression move_next_expr;
5406 MemberInfo[] mi = new MemberInfo[] { move_next };
5407 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5408 mg.InstanceExpression = enumerator;
5410 move_next_expr = new Invocation (mg, null);
5413 get_current.InstanceExpression = enumerator;
5415 Statement block = new CollectionForeachStatement (
5416 var_type.Type, variable, get_current, statement, loc);
5418 loop = new While (move_next_expr, block, loc);
5422 FlowBranchingException branching = null;
5424 branching = ec.StartFlowBranching (this);
5426 if (!loop.Resolve (ec))
5429 if (is_disposable) {
5430 ResolveFinally (branching);
5431 ec.EndFlowBranching ();
5433 if (TypeManager.void_dispose_void == null) {
5434 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5435 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5438 emit_finally = true;
5443 protected override void DoEmit (EmitContext ec)
5445 ILGenerator ig = ec.ig;
5447 enumerator.Store (ec, init);
5450 // Protect the code in a try/finalize block, so that
5451 // if the beast implement IDisposable, we get rid of it
5453 if (is_disposable && emit_finally)
5454 ig.BeginExceptionBlock ();
5459 // Now the finally block
5461 if (is_disposable) {
5464 ig.EndExceptionBlock ();
5469 public override void EmitFinally (EmitContext ec)
5471 ILGenerator ig = ec.ig;
5473 if (enumerator_type.IsValueType) {
5474 MethodInfo mi = FetchMethodDispose (enumerator_type);
5476 enumerator.EmitLoadAddress (ec);
5477 ig.Emit (OpCodes.Call, mi);
5479 enumerator.Emit (ec);
5480 ig.Emit (OpCodes.Box, enumerator_type);
5481 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5484 Label call_dispose = ig.DefineLabel ();
5486 enumerator.Emit (ec);
5487 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5488 ig.Emit (OpCodes.Dup);
5489 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5490 ig.Emit (OpCodes.Pop);
5492 Label end_finally = ig.DefineLabel ();
5493 ig.Emit (OpCodes.Br, end_finally);
5495 ig.MarkLabel (call_dispose);
5496 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5497 ig.MarkLabel (end_finally);
5502 protected class CollectionForeachStatement : Statement
5505 Expression variable, current, conv;
5506 Statement statement;
5509 public CollectionForeachStatement (Type type, Expression variable,
5510 Expression current, Statement statement,
5514 this.variable = variable;
5515 this.current = current;
5516 this.statement = statement;
5520 public override bool Resolve (EmitContext ec)
5522 current = current.Resolve (ec);
5523 if (current == null)
5526 conv = Convert.ExplicitConversion (ec, current, type, loc);
5530 assign = new Assign (variable, conv, loc);
5531 if (assign.Resolve (ec) == null)
5534 if (!statement.Resolve (ec))
5540 protected override void DoEmit (EmitContext ec)
5542 assign.EmitStatement (ec);
5543 statement.Emit (ec);
5547 protected override void CloneTo (CloneContext clonectx, Statement t)
5549 Foreach target = (Foreach) t;
5551 target.type = type.Clone (clonectx);
5552 target.variable = variable.Clone (clonectx);
5553 target.expr = expr.Clone (clonectx);
5554 target.statement = statement.Clone (clonectx);