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);
3673 protected override void DoEmit (EmitContext ec)
3675 ILGenerator ig = ec.ig;
3677 default_target = ig.DefineLabel ();
3678 null_target = ig.DefineLabel ();
3680 // Store variable for comparission purposes
3683 value = ig.DeclareLocal (SwitchType);
3685 unwrap.EmitCheck (ec);
3686 ig.Emit (OpCodes.Brfalse, null_target);
3688 ig.Emit (OpCodes.Stloc, value);
3690 } else if (!is_constant) {
3691 value = ig.DeclareLocal (SwitchType);
3693 ig.Emit (OpCodes.Stloc, value);
3698 // Setup the codegen context
3700 Label old_end = ec.LoopEnd;
3701 Switch old_switch = ec.Switch;
3703 ec.LoopEnd = ig.DefineLabel ();
3708 if (constant_section != null)
3709 constant_section.Block.Emit (ec);
3710 } else if (SwitchType == TypeManager.string_type)
3711 SimpleSwitchEmit (ec, value);
3713 TableSwitchEmit (ec, value);
3715 // Restore context state.
3716 ig.MarkLabel (ec.LoopEnd);
3719 // Restore the previous context
3721 ec.LoopEnd = old_end;
3722 ec.Switch = old_switch;
3725 protected override void CloneTo (CloneContext clonectx, Statement t)
3727 Switch target = (Switch) t;
3729 target.Expr = Expr.Clone (clonectx);
3730 target.Sections = new ArrayList ();
3731 foreach (SwitchSection ss in Sections){
3732 target.Sections.Add (ss.Clone (clonectx));
3737 public abstract class ExceptionStatement : Statement
3739 public abstract void EmitFinally (EmitContext ec);
3741 protected bool emit_finally = true;
3742 ArrayList parent_vectors;
3744 protected void DoEmitFinally (EmitContext ec)
3747 ec.ig.BeginFinallyBlock ();
3748 else if (ec.InIterator)
3749 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3753 protected void ResolveFinally (FlowBranchingException branching)
3755 emit_finally = branching.EmitFinally;
3757 branching.Parent.StealFinallyClauses (ref parent_vectors);
3761 public class Lock : ExceptionStatement {
3763 public Statement Statement;
3764 TemporaryVariable temp;
3766 public Lock (Expression expr, Statement stmt, Location l)
3773 public override bool Resolve (EmitContext ec)
3775 expr = expr.Resolve (ec);
3779 if (expr.Type.IsValueType){
3780 Report.Error (185, loc,
3781 "`{0}' is not a reference type as required by the lock statement",
3782 TypeManager.CSharpName (expr.Type));
3786 FlowBranchingException branching = ec.StartFlowBranching (this);
3787 bool ok = Statement.Resolve (ec);
3789 ResolveFinally (branching);
3791 ec.EndFlowBranching ();
3793 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3794 // So, ensure there's some IL code after the finally block.
3795 ec.NeedReturnLabel ();
3797 // Avoid creating libraries that reference the internal
3800 if (t == TypeManager.null_type)
3801 t = TypeManager.object_type;
3803 temp = new TemporaryVariable (t, loc);
3809 protected override void DoEmit (EmitContext ec)
3811 ILGenerator ig = ec.ig;
3813 temp.Store (ec, expr);
3815 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3819 ig.BeginExceptionBlock ();
3820 Statement.Emit (ec);
3825 ig.EndExceptionBlock ();
3828 public override void EmitFinally (EmitContext ec)
3831 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3834 protected override void CloneTo (CloneContext clonectx, Statement t)
3836 Lock target = (Lock) t;
3838 target.expr = expr.Clone (clonectx);
3839 target.Statement = Statement.Clone (clonectx);
3843 public class Unchecked : Statement {
3846 public Unchecked (Block b)
3852 public override bool Resolve (EmitContext ec)
3854 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3855 return Block.Resolve (ec);
3858 protected override void DoEmit (EmitContext ec)
3860 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3864 protected override void CloneTo (CloneContext clonectx, Statement t)
3866 Unchecked target = (Unchecked) t;
3868 target.Block = clonectx.LookupBlock (Block);
3872 public class Checked : Statement {
3875 public Checked (Block b)
3878 b.Unchecked = false;
3881 public override bool Resolve (EmitContext ec)
3883 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3884 return Block.Resolve (ec);
3887 protected override void DoEmit (EmitContext ec)
3889 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3893 protected override void CloneTo (CloneContext clonectx, Statement t)
3895 Checked target = (Checked) t;
3897 target.Block = clonectx.LookupBlock (Block);
3901 public class Unsafe : Statement {
3904 public Unsafe (Block b)
3907 Block.Unsafe = true;
3910 public override bool Resolve (EmitContext ec)
3912 using (ec.With (EmitContext.Flags.InUnsafe, true))
3913 return Block.Resolve (ec);
3916 protected override void DoEmit (EmitContext ec)
3918 using (ec.With (EmitContext.Flags.InUnsafe, true))
3921 protected override void CloneTo (CloneContext clonectx, Statement t)
3923 Unsafe target = (Unsafe) t;
3925 target.Block = clonectx.LookupBlock (Block);
3932 public class Fixed : Statement {
3934 ArrayList declarators;
3935 Statement statement;
3940 abstract class Emitter
3942 protected LocalInfo vi;
3943 protected Expression converted;
3945 protected Emitter (Expression expr, LocalInfo li)
3951 public abstract void Emit (EmitContext ec);
3952 public abstract void EmitExit (EmitContext ec);
3955 class ExpressionEmitter : Emitter {
3956 public ExpressionEmitter (Expression converted, LocalInfo li) :
3957 base (converted, li)
3961 public override void Emit (EmitContext ec) {
3963 // Store pointer in pinned location
3965 converted.Emit (ec);
3966 vi.Variable.EmitAssign (ec);
3969 public override void EmitExit (EmitContext ec)
3971 ec.ig.Emit (OpCodes.Ldc_I4_0);
3972 ec.ig.Emit (OpCodes.Conv_U);
3973 vi.Variable.EmitAssign (ec);
3977 class StringEmitter : Emitter {
3978 LocalBuilder pinned_string;
3981 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3987 public override void Emit (EmitContext ec)
3989 ILGenerator ig = ec.ig;
3990 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3992 converted.Emit (ec);
3993 ig.Emit (OpCodes.Stloc, pinned_string);
3995 Expression sptr = new StringPtr (pinned_string, loc);
3996 converted = Convert.ImplicitConversionRequired (
3997 ec, sptr, vi.VariableType, loc);
3999 if (converted == null)
4002 converted.Emit (ec);
4003 vi.Variable.EmitAssign (ec);
4006 public override void EmitExit (EmitContext ec)
4008 ec.ig.Emit (OpCodes.Ldnull);
4009 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4013 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4016 declarators = decls;
4021 public Statement Statement {
4022 get { return statement; }
4025 public override bool Resolve (EmitContext ec)
4028 Expression.UnsafeError (loc);
4032 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4033 if (texpr == null) {
4034 if (type is VarExpr)
4035 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4040 expr_type = texpr.Type;
4042 data = new Emitter [declarators.Count];
4044 if (!expr_type.IsPointer){
4045 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4050 foreach (Pair p in declarators){
4051 LocalInfo vi = (LocalInfo) p.First;
4052 Expression e = (Expression) p.Second;
4054 vi.VariableInfo.SetAssigned (ec);
4055 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4058 // The rules for the possible declarators are pretty wise,
4059 // but the production on the grammar is more concise.
4061 // So we have to enforce these rules here.
4063 // We do not resolve before doing the case 1 test,
4064 // because the grammar is explicit in that the token &
4065 // is present, so we need to test for this particular case.
4069 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4074 // Case 1: & object.
4076 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4077 Expression child = ((Unary) e).Expr;
4079 if (child is ParameterReference || child is LocalVariableReference){
4082 "No need to use fixed statement for parameters or " +
4083 "local variable declarations (address is already " +
4088 ec.InFixedInitializer = true;
4090 ec.InFixedInitializer = false;
4094 child = ((Unary) e).Expr;
4096 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4099 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4100 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4104 data [i] = new ExpressionEmitter (e, vi);
4110 ec.InFixedInitializer = true;
4112 ec.InFixedInitializer = false;
4119 if (e.Type.IsArray){
4120 Type array_type = TypeManager.GetElementType (e.Type);
4123 // Provided that array_type is unmanaged,
4125 if (!TypeManager.VerifyUnManaged (array_type, loc))
4129 // and T* is implicitly convertible to the
4130 // pointer type given in the fixed statement.
4132 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4134 Expression converted = Convert.ImplicitConversionRequired (
4135 ec, array_ptr, vi.VariableType, loc);
4136 if (converted == null)
4140 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4142 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4143 new Binary (Binary.Operator.Equality, e, new NullConstant (loc)),
4144 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4148 converted = converted.Resolve (ec);
4150 data [i] = new ExpressionEmitter (converted, vi);
4159 if (e.Type == TypeManager.string_type){
4160 data [i] = new StringEmitter (e, vi, loc);
4165 // Case 4: fixed buffer
4166 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4167 if (fixed_buffer_ptr != null) {
4168 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4173 // For other cases, flag a `this is already fixed expression'
4175 if (e is LocalVariableReference || e is ParameterReference ||
4176 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4178 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4182 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4186 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4187 bool ok = statement.Resolve (ec);
4188 bool flow_unreachable = ec.EndFlowBranching ();
4189 has_ret = flow_unreachable;
4194 protected override void DoEmit (EmitContext ec)
4196 for (int i = 0; i < data.Length; i++) {
4200 statement.Emit (ec);
4206 // Clear the pinned variable
4208 for (int i = 0; i < data.Length; i++) {
4209 data [i].EmitExit (ec);
4213 protected override void CloneTo (CloneContext clonectx, Statement t)
4215 Fixed target = (Fixed) t;
4217 target.type = type.Clone (clonectx);
4218 target.declarators = new ArrayList (declarators.Count);
4219 foreach (Pair p in declarators) {
4220 LocalInfo vi = (LocalInfo) p.First;
4221 Expression e = (Expression) p.Second;
4223 target.declarators.Add (
4224 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4227 target.statement = statement.Clone (clonectx);
4231 public class Catch : Statement {
4232 public readonly string Name;
4234 public Block VarBlock;
4236 Expression type_expr;
4239 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4244 VarBlock = var_block;
4248 public Type CatchType {
4254 public bool IsGeneral {
4256 return type_expr == null;
4260 protected override void DoEmit(EmitContext ec)
4262 ILGenerator ig = ec.ig;
4264 if (CatchType != null)
4265 ig.BeginCatchBlock (CatchType);
4267 ig.BeginCatchBlock (TypeManager.object_type);
4269 if (VarBlock != null)
4273 LocalInfo vi = Block.GetLocalInfo (Name);
4275 throw new Exception ("Variable does not exist in this block");
4277 if (vi.Variable.NeedsTemporary) {
4278 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4279 ig.Emit (OpCodes.Stloc, e);
4281 vi.Variable.EmitInstance (ec);
4282 ig.Emit (OpCodes.Ldloc, e);
4283 vi.Variable.EmitAssign (ec);
4285 vi.Variable.EmitAssign (ec);
4287 ig.Emit (OpCodes.Pop);
4292 public override bool Resolve (EmitContext ec)
4294 using (ec.With (EmitContext.Flags.InCatch, true)) {
4295 if (type_expr != null) {
4296 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4302 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4303 Error (155, "The type caught or thrown must be derived from System.Exception");
4309 if (!Block.Resolve (ec))
4312 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4313 // emit the "unused variable" warnings.
4314 if (VarBlock != null)
4315 return VarBlock.Resolve (ec);
4321 protected override void CloneTo (CloneContext clonectx, Statement t)
4323 Catch target = (Catch) t;
4325 if (type_expr != null)
4326 target.type_expr = type_expr.Clone (clonectx);
4327 if (VarBlock != null)
4328 target.VarBlock = clonectx.LookupBlock (VarBlock);
4329 target.Block = clonectx.LookupBlock (Block);
4333 public class Try : ExceptionStatement {
4334 public Block Fini, Block;
4335 public ArrayList Specific;
4336 public Catch General;
4338 bool need_exc_block;
4341 // specific, general and fini might all be null.
4343 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4345 if (specific == null && general == null){
4346 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4350 this.Specific = specific;
4351 this.General = general;
4356 public override bool Resolve (EmitContext ec)
4360 FlowBranchingException branching = ec.StartFlowBranching (this);
4362 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4364 if (!Block.Resolve (ec))
4367 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4369 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4371 Type[] prev_catches = new Type [Specific.Count];
4373 foreach (Catch c in Specific){
4374 ec.CurrentBranching.CreateSibling (
4375 c.Block, FlowBranching.SiblingType.Catch);
4377 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4379 if (c.Name != null) {
4380 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4382 throw new Exception ();
4384 vi.VariableInfo = null;
4387 if (!c.Resolve (ec))
4390 Type resolved_type = c.CatchType;
4391 for (int ii = 0; ii < last_index; ++ii) {
4392 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4393 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4398 prev_catches [last_index++] = resolved_type;
4399 need_exc_block = true;
4402 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4404 if (General != null){
4405 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4406 foreach (Catch c in Specific){
4407 if (c.CatchType == TypeManager.exception_type) {
4408 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'");
4413 ec.CurrentBranching.CreateSibling (
4414 General.Block, FlowBranching.SiblingType.Catch);
4416 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4418 if (!General.Resolve (ec))
4421 need_exc_block = true;
4424 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4428 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4430 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4431 using (ec.With (EmitContext.Flags.InFinally, true)) {
4432 if (!Fini.Resolve (ec))
4437 need_exc_block = true;
4440 if (ec.InIterator) {
4441 ResolveFinally (branching);
4442 need_exc_block |= emit_finally;
4444 emit_finally = Fini != null;
4446 ec.EndFlowBranching ();
4448 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4449 // So, ensure there's some IL code after the finally block.
4450 ec.NeedReturnLabel ();
4452 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4454 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4459 protected override void DoEmit (EmitContext ec)
4461 ILGenerator ig = ec.ig;
4464 ig.BeginExceptionBlock ();
4467 foreach (Catch c in Specific)
4470 if (General != null)
4475 ig.EndExceptionBlock ();
4478 public override void EmitFinally (EmitContext ec)
4484 public bool HasCatch
4487 return General != null || Specific.Count > 0;
4491 protected override void CloneTo (CloneContext clonectx, Statement t)
4493 Try target = (Try) t;
4495 target.Block = clonectx.LookupBlock (Block);
4497 target.Fini = clonectx.LookupBlock (Fini);
4498 if (General != null)
4499 target.General = (Catch) General.Clone (clonectx);
4500 if (Specific != null){
4501 target.Specific = new ArrayList ();
4502 foreach (Catch c in Specific)
4503 target.Specific.Add (c.Clone (clonectx));
4508 public class Using : ExceptionStatement {
4509 object expression_or_block;
4510 public Statement Statement;
4514 Expression [] resolved_vars;
4515 Expression [] converted_vars;
4516 Expression [] assign;
4517 TemporaryVariable local_copy;
4519 public Using (object expression_or_block, Statement stmt, Location l)
4521 this.expression_or_block = expression_or_block;
4527 // Resolves for the case of using using a local variable declaration.
4529 bool ResolveLocalVariableDecls (EmitContext ec)
4531 resolved_vars = new Expression[var_list.Count];
4532 assign = new Expression [var_list.Count];
4533 converted_vars = new Expression[var_list.Count];
4535 for (int i = 0; i < assign.Length; ++i) {
4536 DictionaryEntry e = (DictionaryEntry) var_list [i];
4537 Expression var = (Expression) e.Key;
4538 Expression new_expr = (Expression) e.Value;
4540 Expression a = new Assign (var, new_expr, loc);
4545 resolved_vars [i] = var;
4548 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4549 converted_vars [i] = var;
4553 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4555 Error_IsNotConvertibleToIDisposable (var);
4559 converted_vars [i] = a;
4565 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4567 Report.SymbolRelatedToPreviousError (expr.Type);
4568 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4569 expr.GetSignatureForError ());
4572 bool ResolveExpression (EmitContext ec)
4574 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4575 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4576 Error_IsNotConvertibleToIDisposable (expr);
4581 local_copy = new TemporaryVariable (expr_type, loc);
4582 local_copy.Resolve (ec);
4588 // Emits the code for the case of using using a local variable declaration.
4590 void EmitLocalVariableDecls (EmitContext ec)
4592 ILGenerator ig = ec.ig;
4595 for (i = 0; i < assign.Length; i++) {
4596 ExpressionStatement es = assign [i] as ExpressionStatement;
4599 es.EmitStatement (ec);
4601 assign [i].Emit (ec);
4602 ig.Emit (OpCodes.Pop);
4606 ig.BeginExceptionBlock ();
4608 Statement.Emit (ec);
4610 var_list.Reverse ();
4615 void EmitLocalVariableDeclFinally (EmitContext ec)
4617 ILGenerator ig = ec.ig;
4619 int i = assign.Length;
4620 for (int ii = 0; ii < var_list.Count; ++ii){
4621 Expression var = resolved_vars [--i];
4622 Label skip = ig.DefineLabel ();
4625 ig.BeginFinallyBlock ();
4627 if (!var.Type.IsValueType) {
4629 ig.Emit (OpCodes.Brfalse, skip);
4630 converted_vars [i].Emit (ec);
4631 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4633 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4635 if (!(ml is MethodGroupExpr)) {
4637 ig.Emit (OpCodes.Box, var.Type);
4638 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4640 MethodInfo mi = null;
4642 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4643 if (TypeManager.GetParameterData (mk).Count == 0) {
4650 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4654 IMemoryLocation mloc = (IMemoryLocation) var;
4656 mloc.AddressOf (ec, AddressOp.Load);
4657 ig.Emit (OpCodes.Call, mi);
4661 ig.MarkLabel (skip);
4664 ig.EndExceptionBlock ();
4666 ig.BeginFinallyBlock ();
4671 void EmitExpression (EmitContext ec)
4674 // Make a copy of the expression and operate on that.
4676 ILGenerator ig = ec.ig;
4678 local_copy.Store (ec, expr);
4681 ig.BeginExceptionBlock ();
4683 Statement.Emit (ec);
4687 ig.EndExceptionBlock ();
4690 void EmitExpressionFinally (EmitContext ec)
4692 ILGenerator ig = ec.ig;
4693 if (!expr_type.IsValueType) {
4694 Label skip = ig.DefineLabel ();
4695 local_copy.Emit (ec);
4696 ig.Emit (OpCodes.Brfalse, skip);
4697 local_copy.Emit (ec);
4698 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4699 ig.MarkLabel (skip);
4701 Expression ml = Expression.MemberLookup (
4702 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4703 "Dispose", Location.Null);
4705 if (!(ml is MethodGroupExpr)) {
4706 local_copy.Emit (ec);
4707 ig.Emit (OpCodes.Box, expr_type);
4708 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4710 MethodInfo mi = null;
4712 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4713 if (TypeManager.GetParameterData (mk).Count == 0) {
4720 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4724 local_copy.AddressOf (ec, AddressOp.Load);
4725 ig.Emit (OpCodes.Call, mi);
4730 public override bool Resolve (EmitContext ec)
4732 if (expression_or_block is DictionaryEntry){
4733 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4734 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4736 if (!ResolveLocalVariableDecls (ec))
4739 } else if (expression_or_block is Expression){
4740 expr = (Expression) expression_or_block;
4742 expr = expr.Resolve (ec);
4746 expr_type = expr.Type;
4748 if (!ResolveExpression (ec))
4752 FlowBranchingException branching = ec.StartFlowBranching (this);
4754 bool ok = Statement.Resolve (ec);
4756 ResolveFinally (branching);
4758 ec.EndFlowBranching ();
4760 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4761 // So, ensure there's some IL code after the finally block.
4762 ec.NeedReturnLabel ();
4767 protected override void DoEmit (EmitContext ec)
4769 if (expression_or_block is DictionaryEntry)
4770 EmitLocalVariableDecls (ec);
4771 else if (expression_or_block is Expression)
4772 EmitExpression (ec);
4775 public override void EmitFinally (EmitContext ec)
4777 if (expression_or_block is DictionaryEntry)
4778 EmitLocalVariableDeclFinally (ec);
4779 else if (expression_or_block is Expression)
4780 EmitExpressionFinally (ec);
4783 protected override void CloneTo (CloneContext clonectx, Statement t)
4785 Using target = (Using) t;
4787 if (expression_or_block is Expression) {
4788 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4790 DictionaryEntry de = (DictionaryEntry) expression_or_block;
4791 ArrayList var_list = (ArrayList) de.Value;
4792 ArrayList target_var_list = new ArrayList (var_list.Count);
4794 foreach (DictionaryEntry de_variable in var_list)
4795 target_var_list.Add (new DictionaryEntry (
4796 ((Expression) de_variable.Key).Clone (clonectx),
4797 ((Expression) de_variable.Value).Clone (clonectx)));
4799 target.expression_or_block = new DictionaryEntry (
4800 ((Expression) de.Key).Clone (clonectx),
4804 target.Statement = Statement.Clone (clonectx);
4809 /// Implementation of the foreach C# statement
4811 public class Foreach : Statement {
4813 Expression variable;
4815 Statement statement;
4817 CollectionForeach collection;
4819 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4820 Statement stmt, Location l)
4823 this.variable = var;
4829 public Statement Statement {
4830 get { return statement; }
4833 public override bool Resolve (EmitContext ec)
4835 expr = expr.Resolve (ec);
4840 Report.Error (186, loc, "Use of null is not valid in this context");
4844 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4845 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4846 expr.ExprClassName);
4851 // We need an instance variable. Not sure this is the best
4852 // way of doing this.
4854 // FIXME: When we implement propertyaccess, will those turn
4855 // out to return values in ExprClass? I think they should.
4857 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4858 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4859 collection.Error_Enumerator ();
4863 if (expr.Type.IsArray) {
4864 array = new ArrayForeach (type, variable, expr, statement, loc);
4865 return array.Resolve (ec);
4868 collection = new CollectionForeach (type, variable, expr, statement, loc);
4869 return collection.Resolve (ec);
4872 protected override void DoEmit (EmitContext ec)
4874 ILGenerator ig = ec.ig;
4876 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4877 ec.LoopBegin = ig.DefineLabel ();
4878 ec.LoopEnd = ig.DefineLabel ();
4880 if (collection != null)
4881 collection.Emit (ec);
4885 ec.LoopBegin = old_begin;
4886 ec.LoopEnd = old_end;
4889 protected class ArrayCounter : TemporaryVariable
4891 public ArrayCounter (Location loc)
4892 : base (TypeManager.int32_type, loc)
4895 public void Initialize (EmitContext ec)
4898 ec.ig.Emit (OpCodes.Ldc_I4_0);
4902 public void Increment (EmitContext ec)
4906 ec.ig.Emit (OpCodes.Ldc_I4_1);
4907 ec.ig.Emit (OpCodes.Add);
4912 protected class ArrayForeach : Statement
4914 Expression variable, expr, conv;
4915 Statement statement;
4917 Expression var_type;
4918 TemporaryVariable[] lengths;
4919 ArrayCounter[] counter;
4922 TemporaryVariable copy;
4925 public ArrayForeach (Expression var_type, Expression var,
4926 Expression expr, Statement stmt, Location l)
4928 this.var_type = var_type;
4929 this.variable = var;
4935 public override bool Resolve (EmitContext ec)
4937 array_type = expr.Type;
4938 rank = array_type.GetArrayRank ();
4940 copy = new TemporaryVariable (array_type, loc);
4943 counter = new ArrayCounter [rank];
4944 lengths = new TemporaryVariable [rank];
4946 ArrayList list = new ArrayList ();
4947 for (int i = 0; i < rank; i++) {
4948 counter [i] = new ArrayCounter (loc);
4949 counter [i].Resolve (ec);
4951 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4952 lengths [i].Resolve (ec);
4954 list.Add (counter [i]);
4957 access = new ElementAccess (copy, list).Resolve (ec);
4961 VarExpr ve = var_type as VarExpr;
4963 // Infer implicitly typed local variable from foreach array type
4964 var_type = new TypeExpression (access.Type, ve.Location);
4967 var_type = var_type.ResolveAsTypeTerminal (ec, false);
4968 if (var_type == null)
4971 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
4977 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4978 ec.CurrentBranching.CreateSibling ();
4980 variable = variable.ResolveLValue (ec, conv, loc);
4981 if (variable == null)
4984 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4985 if (!statement.Resolve (ec))
4987 ec.EndFlowBranching ();
4989 // There's no direct control flow from the end of the embedded statement to the end of the loop
4990 ec.CurrentBranching.CurrentUsageVector.Goto ();
4992 ec.EndFlowBranching ();
4997 protected override void DoEmit (EmitContext ec)
4999 ILGenerator ig = ec.ig;
5001 copy.Store (ec, expr);
5003 Label[] test = new Label [rank];
5004 Label[] loop = new Label [rank];
5006 for (int i = 0; i < rank; i++) {
5007 test [i] = ig.DefineLabel ();
5008 loop [i] = ig.DefineLabel ();
5010 lengths [i].EmitThis (ec);
5011 ((ArrayAccess) access).EmitGetLength (ec, i);
5012 lengths [i].EmitStore (ec);
5015 for (int i = 0; i < rank; i++) {
5016 counter [i].Initialize (ec);
5018 ig.Emit (OpCodes.Br, test [i]);
5019 ig.MarkLabel (loop [i]);
5022 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5024 statement.Emit (ec);
5026 ig.MarkLabel (ec.LoopBegin);
5028 for (int i = rank - 1; i >= 0; i--){
5029 counter [i].Increment (ec);
5031 ig.MarkLabel (test [i]);
5032 counter [i].Emit (ec);
5033 lengths [i].Emit (ec);
5034 ig.Emit (OpCodes.Blt, loop [i]);
5037 ig.MarkLabel (ec.LoopEnd);
5041 protected class CollectionForeach : ExceptionStatement
5043 Expression variable, expr;
5044 Statement statement;
5046 TemporaryVariable enumerator;
5050 MethodGroupExpr get_enumerator;
5051 PropertyExpr get_current;
5052 MethodInfo move_next;
5053 Expression var_type;
5054 Type enumerator_type;
5056 bool enumerator_found;
5058 public CollectionForeach (Expression var_type, Expression var,
5059 Expression expr, Statement stmt, Location l)
5061 this.var_type = var_type;
5062 this.variable = var;
5068 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5070 Type return_type = mi.ReturnType;
5072 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5074 // Apply the same optimization as MS: skip the GetEnumerator
5075 // returning an IEnumerator, and use the one returning a
5076 // CharEnumerator instead. This allows us to avoid the
5077 // try-finally block and the boxing.
5082 // Ok, we can access it, now make sure that we can do something
5083 // with this `GetEnumerator'
5086 if (return_type == TypeManager.ienumerator_type ||
5087 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5088 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5090 // If it is not an interface, lets try to find the methods ourselves.
5091 // For example, if we have:
5092 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5093 // We can avoid the iface call. This is a runtime perf boost.
5094 // even bigger if we have a ValueType, because we avoid the cost
5097 // We have to make sure that both methods exist for us to take
5098 // this path. If one of the methods does not exist, we will just
5099 // use the interface. Sadly, this complex if statement is the only
5100 // way I could do this without a goto
5105 // Prefer a generic enumerator over a non-generic one.
5107 if (return_type.IsInterface && return_type.IsGenericType) {
5108 enumerator_type = return_type;
5109 if (!FetchGetCurrent (ec, return_type))
5110 get_current = new PropertyExpr (
5111 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5112 if (!FetchMoveNext (return_type))
5113 move_next = TypeManager.bool_movenext_void;
5118 if (return_type.IsInterface ||
5119 !FetchMoveNext (return_type) ||
5120 !FetchGetCurrent (ec, return_type)) {
5121 enumerator_type = return_type;
5122 move_next = TypeManager.bool_movenext_void;
5123 get_current = new PropertyExpr (
5124 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5129 // Ok, so they dont return an IEnumerable, we will have to
5130 // find if they support the GetEnumerator pattern.
5133 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5134 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",
5135 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5140 enumerator_type = return_type;
5141 is_disposable = !enumerator_type.IsSealed ||
5142 TypeManager.ImplementsInterface (
5143 enumerator_type, TypeManager.idisposable_type);
5149 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5151 bool FetchMoveNext (Type t)
5153 MemberList move_next_list;
5155 move_next_list = TypeContainer.FindMembers (
5156 t, MemberTypes.Method,
5157 BindingFlags.Public | BindingFlags.Instance,
5158 Type.FilterName, "MoveNext");
5159 if (move_next_list.Count == 0)
5162 foreach (MemberInfo m in move_next_list){
5163 MethodInfo mi = (MethodInfo) m;
5165 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5166 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5176 // Retrieves a `public T get_Current ()' method from the Type `t'
5178 bool FetchGetCurrent (EmitContext ec, Type t)
5180 PropertyExpr pe = Expression.MemberLookup (
5181 ec.ContainerType, t, "Current", MemberTypes.Property,
5182 Expression.AllBindingFlags, loc) as PropertyExpr;
5191 // Retrieves a `public void Dispose ()' method from the Type `t'
5193 static MethodInfo FetchMethodDispose (Type t)
5195 MemberList dispose_list;
5197 dispose_list = TypeContainer.FindMembers (
5198 t, MemberTypes.Method,
5199 BindingFlags.Public | BindingFlags.Instance,
5200 Type.FilterName, "Dispose");
5201 if (dispose_list.Count == 0)
5204 foreach (MemberInfo m in dispose_list){
5205 MethodInfo mi = (MethodInfo) m;
5207 if (TypeManager.GetParameterData (mi).Count == 0){
5208 if (mi.ReturnType == TypeManager.void_type)
5215 public void Error_Enumerator ()
5217 if (enumerator_found) {
5221 Report.Error (1579, loc,
5222 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5223 TypeManager.CSharpName (expr.Type));
5226 bool IsOverride (MethodInfo m)
5228 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5230 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5232 if (m is MethodBuilder)
5235 MethodInfo base_method = m.GetBaseDefinition ();
5236 return base_method != m;
5239 bool TryType (EmitContext ec, Type t)
5241 MethodGroupExpr mg = Expression.MemberLookup (
5242 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5243 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5247 MethodInfo result = null;
5248 MethodInfo tmp_move_next = null;
5249 PropertyExpr tmp_get_cur = null;
5250 Type tmp_enumerator_type = enumerator_type;
5251 foreach (MethodInfo mi in mg.Methods) {
5252 if (TypeManager.GetParameterData (mi).Count != 0)
5255 // Check whether GetEnumerator is public
5256 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5259 if (IsOverride (mi))
5262 enumerator_found = true;
5264 if (!GetEnumeratorFilter (ec, mi))
5267 if (result != null) {
5268 if (TypeManager.IsGenericType (result.ReturnType)) {
5269 if (!TypeManager.IsGenericType (mi.ReturnType))
5272 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5273 Report.SymbolRelatedToPreviousError (t);
5274 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5275 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5276 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5280 // Always prefer generics enumerators
5281 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5282 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5283 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5286 Report.SymbolRelatedToPreviousError (result);
5287 Report.SymbolRelatedToPreviousError (mi);
5288 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5289 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5294 tmp_move_next = move_next;
5295 tmp_get_cur = get_current;
5296 tmp_enumerator_type = enumerator_type;
5297 if (mi.DeclaringType == t)
5301 if (result != null) {
5302 move_next = tmp_move_next;
5303 get_current = tmp_get_cur;
5304 enumerator_type = tmp_enumerator_type;
5305 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5306 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5308 if (t != expr.Type) {
5309 expr = Convert.ExplicitConversion (
5312 throw new InternalErrorException ();
5315 get_enumerator.InstanceExpression = expr;
5316 get_enumerator.IsBase = t != expr.Type;
5324 bool ProbeCollectionType (EmitContext ec, Type t)
5326 int errors = Report.Errors;
5327 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5328 if (TryType (ec, tt))
5333 if (Report.Errors > errors)
5337 // Now try to find the method in the interfaces
5339 Type [] ifaces = TypeManager.GetInterfaces (t);
5340 foreach (Type i in ifaces){
5341 if (TryType (ec, i))
5348 public override bool Resolve (EmitContext ec)
5350 enumerator_type = TypeManager.ienumerator_type;
5351 is_disposable = true;
5353 if (!ProbeCollectionType (ec, expr.Type)) {
5354 Error_Enumerator ();
5358 VarExpr ve = var_type as VarExpr;
5360 // Infer implicitly typed local variable from foreach enumerable type
5361 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5364 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5365 if (var_type == null)
5368 enumerator = new TemporaryVariable (enumerator_type, loc);
5369 enumerator.Resolve (ec);
5371 init = new Invocation (get_enumerator, null);
5372 init = init.Resolve (ec);
5376 Expression move_next_expr;
5378 MemberInfo[] mi = new MemberInfo[] { move_next };
5379 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5380 mg.InstanceExpression = enumerator;
5382 move_next_expr = new Invocation (mg, null);
5385 get_current.InstanceExpression = enumerator;
5387 Statement block = new CollectionForeachStatement (
5388 var_type.Type, variable, get_current, statement, loc);
5390 loop = new While (move_next_expr, block, loc);
5394 FlowBranchingException branching = null;
5396 branching = ec.StartFlowBranching (this);
5398 if (!loop.Resolve (ec))
5401 if (is_disposable) {
5402 ResolveFinally (branching);
5403 ec.EndFlowBranching ();
5405 emit_finally = true;
5410 protected override void DoEmit (EmitContext ec)
5412 ILGenerator ig = ec.ig;
5414 enumerator.Store (ec, init);
5417 // Protect the code in a try/finalize block, so that
5418 // if the beast implement IDisposable, we get rid of it
5420 if (is_disposable && emit_finally)
5421 ig.BeginExceptionBlock ();
5426 // Now the finally block
5428 if (is_disposable) {
5431 ig.EndExceptionBlock ();
5436 public override void EmitFinally (EmitContext ec)
5438 ILGenerator ig = ec.ig;
5440 if (enumerator_type.IsValueType) {
5441 MethodInfo mi = FetchMethodDispose (enumerator_type);
5443 enumerator.EmitLoadAddress (ec);
5444 ig.Emit (OpCodes.Call, mi);
5446 enumerator.Emit (ec);
5447 ig.Emit (OpCodes.Box, enumerator_type);
5448 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5451 Label call_dispose = ig.DefineLabel ();
5453 enumerator.Emit (ec);
5454 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5455 ig.Emit (OpCodes.Dup);
5456 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5457 ig.Emit (OpCodes.Pop);
5459 Label end_finally = ig.DefineLabel ();
5460 ig.Emit (OpCodes.Br, end_finally);
5462 ig.MarkLabel (call_dispose);
5463 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5464 ig.MarkLabel (end_finally);
5469 protected class CollectionForeachStatement : Statement
5472 Expression variable, current, conv;
5473 Statement statement;
5476 public CollectionForeachStatement (Type type, Expression variable,
5477 Expression current, Statement statement,
5481 this.variable = variable;
5482 this.current = current;
5483 this.statement = statement;
5487 public override bool Resolve (EmitContext ec)
5489 current = current.Resolve (ec);
5490 if (current == null)
5493 conv = Convert.ExplicitConversion (ec, current, type, loc);
5497 assign = new Assign (variable, conv, loc);
5498 if (assign.Resolve (ec) == null)
5501 if (!statement.Resolve (ec))
5507 protected override void DoEmit (EmitContext ec)
5509 assign.EmitStatement (ec);
5510 statement.Emit (ec);
5514 protected override void CloneTo (CloneContext clonectx, Statement t)
5516 Foreach target = (Foreach) t;
5518 target.type = type.Clone (clonectx);
5519 target.variable = variable.Clone (clonectx);
5520 target.expr = expr.Clone (clonectx);
5521 target.statement = statement.Clone (clonectx);