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);
545 expr.EmitBranchable (ec, while_loop, true);
547 ig.MarkLabel (ec.LoopEnd);
550 ec.LoopBegin = old_begin;
551 ec.LoopEnd = old_end;
554 public override void Emit (EmitContext ec)
559 protected override void CloneTo (CloneContext clonectx, Statement t)
561 While target = (While) t;
563 target.expr = expr.Clone (clonectx);
564 target.Statement = Statement.Clone (clonectx);
568 public class For : Statement {
570 Statement InitStatement;
572 public Statement Statement;
573 bool infinite, empty;
575 public For (Statement init_statement,
581 InitStatement = init_statement;
583 Increment = increment;
584 Statement = statement;
588 public override bool Resolve (EmitContext ec)
592 if (InitStatement != null){
593 if (!InitStatement.Resolve (ec))
598 Test = Expression.ResolveBoolean (ec, Test, loc);
601 else if (Test is BoolConstant){
602 BoolConstant bc = (BoolConstant) Test;
604 if (bc.Value == false){
605 if (!Statement.ResolveUnreachable (ec, true))
607 if ((Increment != null) &&
608 !Increment.ResolveUnreachable (ec, false))
618 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
620 ec.CurrentBranching.CreateSibling ();
622 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
624 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
625 if (!Statement.Resolve (ec))
627 ec.EndFlowBranching ();
629 if (Increment != null){
630 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
631 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
634 if (!Increment.Resolve (ec))
639 // There's no direct control flow from the end of the embedded statement to the end of the loop
640 ec.CurrentBranching.CurrentUsageVector.Goto ();
642 ec.EndFlowBranching ();
647 protected override void DoEmit (EmitContext ec)
652 ILGenerator ig = ec.ig;
653 Label old_begin = ec.LoopBegin;
654 Label old_end = ec.LoopEnd;
655 Label loop = ig.DefineLabel ();
656 Label test = ig.DefineLabel ();
658 if (InitStatement != null && InitStatement != EmptyStatement.Value)
659 InitStatement.Emit (ec);
661 ec.LoopBegin = ig.DefineLabel ();
662 ec.LoopEnd = ig.DefineLabel ();
664 ig.Emit (OpCodes.Br, test);
668 ig.MarkLabel (ec.LoopBegin);
669 if (Increment != EmptyStatement.Value)
674 // If test is null, there is no test, and we are just
679 // The Resolve code already catches the case for
680 // Test == BoolConstant (false) so we know that
683 if (Test is BoolConstant)
684 ig.Emit (OpCodes.Br, loop);
686 Test.EmitBranchable (ec, loop, true);
689 ig.Emit (OpCodes.Br, loop);
690 ig.MarkLabel (ec.LoopEnd);
692 ec.LoopBegin = old_begin;
693 ec.LoopEnd = old_end;
696 protected override void CloneTo (CloneContext clonectx, Statement t)
698 For target = (For) t;
700 if (InitStatement != null)
701 target.InitStatement = InitStatement.Clone (clonectx);
703 target.Test = Test.Clone (clonectx);
704 if (Increment != null)
705 target.Increment = Increment.Clone (clonectx);
706 target.Statement = Statement.Clone (clonectx);
710 public class StatementExpression : Statement {
711 ExpressionStatement expr;
713 public StatementExpression (ExpressionStatement expr)
719 public override bool Resolve (EmitContext ec)
722 expr = expr.ResolveStatement (ec);
726 protected override void DoEmit (EmitContext ec)
728 expr.EmitStatement (ec);
731 public override string ToString ()
733 return "StatementExpression (" + expr + ")";
736 protected override void CloneTo (CloneContext clonectx, Statement t)
738 StatementExpression target = (StatementExpression) t;
740 target.expr = (ExpressionStatement) expr.Clone (clonectx);
745 /// Implements the return statement
747 public class Return : Statement {
748 protected Expression Expr;
751 public Return (Expression expr, Location l)
757 bool DoResolve (EmitContext ec)
760 if (ec.ReturnType == TypeManager.void_type)
763 Error (126, "An object of a type convertible to `{0}' is required " +
764 "for the return statement",
765 TypeManager.CSharpName (ec.ReturnType));
769 AnonymousContainer am = ec.CurrentAnonymousMethod;
770 if ((am != null) && am.IsIterator && ec.InIterator) {
771 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
772 "statement to return a value, or yield break to end the iteration");
775 if (am == null && ec.ReturnType == TypeManager.void_type) {
776 MemberCore mc = ec.ResolveContext as MemberCore;
777 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
778 mc.GetSignatureForError ());
781 Expr = Expr.Resolve (ec);
785 if (Expr.Type != ec.ReturnType) {
786 if (ec.InferReturnType) {
788 // void cannot be used in contextual return
790 if (Expr.Type == TypeManager.void_type)
793 ec.ReturnType = Expr.Type;
795 Expr = Convert.ImplicitConversionRequired (
796 ec, Expr, ec.ReturnType, loc);
800 Report.Error (1662, loc,
801 "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",
802 am.ContainerType, am.GetSignatureForError ());
812 public override bool Resolve (EmitContext ec)
817 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
819 ec.NeedReturnLabel ();
820 ec.CurrentBranching.CurrentUsageVector.Goto ();
824 protected override void DoEmit (EmitContext ec)
830 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
834 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
836 ec.ig.Emit (OpCodes.Ret);
839 protected override void CloneTo (CloneContext clonectx, Statement t)
841 Return target = (Return) t;
842 // It's null for simple return;
844 target.Expr = Expr.Clone (clonectx);
848 public class Goto : Statement {
850 LabeledStatement label;
853 public override bool Resolve (EmitContext ec)
855 int errors = Report.Errors;
856 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
857 ec.CurrentBranching.CurrentUsageVector.Goto ();
858 return errors == Report.Errors;
861 public Goto (string label, Location l)
867 public string Target {
868 get { return target; }
871 public void SetResolvedTarget (LabeledStatement label)
874 label.AddReference ();
877 protected override void DoEmit (EmitContext ec)
880 throw new InternalErrorException ("goto emitted before target resolved");
881 Label l = label.LabelTarget (ec);
882 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
886 public class LabeledStatement : Statement {
893 FlowBranching.UsageVector vectors;
895 public LabeledStatement (string name, Location l)
901 public Label LabelTarget (EmitContext ec)
906 label = ec.ig.DefineLabel ();
916 public bool IsDefined {
917 get { return defined; }
920 public bool HasBeenReferenced {
921 get { return referenced; }
924 public FlowBranching.UsageVector JumpOrigins {
925 get { return vectors; }
928 public void AddUsageVector (FlowBranching.UsageVector vector)
930 vector = vector.Clone ();
931 vector.Next = vectors;
935 public override bool Resolve (EmitContext ec)
937 // this flow-branching will be terminated when the surrounding block ends
938 ec.StartFlowBranching (this);
942 protected override void DoEmit (EmitContext ec)
944 if (ig != null && ig != ec.ig)
945 throw new InternalErrorException ("cannot happen");
947 ec.ig.MarkLabel (label);
950 public void AddReference ()
958 /// `goto default' statement
960 public class GotoDefault : Statement {
962 public GotoDefault (Location l)
967 public override bool Resolve (EmitContext ec)
969 ec.CurrentBranching.CurrentUsageVector.Goto ();
973 protected override void DoEmit (EmitContext ec)
975 if (ec.Switch == null){
976 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
980 if (!ec.Switch.GotDefault){
981 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
984 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
989 /// `goto case' statement
991 public class GotoCase : Statement {
995 public GotoCase (Expression e, Location l)
1001 public override bool Resolve (EmitContext ec)
1003 if (ec.Switch == null){
1004 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1008 expr = expr.Resolve (ec);
1012 Constant c = expr as Constant;
1014 Error (150, "A constant value is expected");
1018 Type type = ec.Switch.SwitchType;
1019 if (!Convert.ImplicitStandardConversionExists (c, type))
1020 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1021 "convertible to type `{0}'", TypeManager.CSharpName (type));
1024 object val = c.GetValue ();
1025 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1026 val = TypeManager.ChangeType (val, type, out fail);
1029 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1030 c.GetSignatureForError (), TypeManager.CSharpName (type));
1035 val = SwitchLabel.NullStringCase;
1037 sl = (SwitchLabel) ec.Switch.Elements [val];
1040 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1041 (c.GetValue () == null ? "null" : val.ToString ()));
1045 ec.CurrentBranching.CurrentUsageVector.Goto ();
1049 protected override void DoEmit (EmitContext ec)
1051 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1054 protected override void CloneTo (CloneContext clonectx, Statement t)
1056 GotoCase target = (GotoCase) t;
1058 target.expr = expr.Clone (clonectx);
1059 target.sl = sl.Clone (clonectx);
1063 public class Throw : Statement {
1066 public Throw (Expression expr, Location l)
1072 public override bool Resolve (EmitContext ec)
1074 ec.CurrentBranching.CurrentUsageVector.Goto ();
1077 expr = expr.Resolve (ec);
1081 ExprClass eclass = expr.eclass;
1083 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1084 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1085 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1091 if ((t != TypeManager.exception_type) &&
1092 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1093 !(expr is NullLiteral)) {
1095 "The type caught or thrown must be derived " +
1096 "from System.Exception");
1103 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
1108 Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1114 protected override void DoEmit (EmitContext ec)
1117 ec.ig.Emit (OpCodes.Rethrow);
1121 ec.ig.Emit (OpCodes.Throw);
1125 protected override void CloneTo (CloneContext clonectx, Statement t)
1127 Throw target = (Throw) t;
1130 target.expr = expr.Clone (clonectx);
1134 public class Break : Statement {
1136 public Break (Location l)
1141 bool unwind_protect;
1143 public override bool Resolve (EmitContext ec)
1145 int errors = Report.Errors;
1146 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1147 ec.CurrentBranching.CurrentUsageVector.Goto ();
1148 return errors == Report.Errors;
1151 protected override void DoEmit (EmitContext ec)
1153 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1156 protected override void CloneTo (CloneContext clonectx, Statement t)
1162 public class Continue : Statement {
1164 public Continue (Location l)
1169 bool unwind_protect;
1171 public override bool Resolve (EmitContext ec)
1173 int errors = Report.Errors;
1174 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1175 ec.CurrentBranching.CurrentUsageVector.Goto ();
1176 return errors == Report.Errors;
1179 protected override void DoEmit (EmitContext ec)
1181 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1184 protected override void CloneTo (CloneContext clonectx, Statement t)
1190 public abstract class Variable
1192 public abstract Type Type {
1196 public abstract bool HasInstance {
1200 public abstract bool NeedsTemporary {
1204 public abstract void EmitInstance (EmitContext ec);
1206 public abstract void Emit (EmitContext ec);
1208 public abstract void EmitAssign (EmitContext ec);
1210 public abstract void EmitAddressOf (EmitContext ec);
1213 public interface IKnownVariable {
1214 Block Block { get; }
1215 Location Location { get; }
1219 // The information about a user-perceived local variable
1221 public class LocalInfo : IKnownVariable {
1222 public Expression Type;
1224 public Type VariableType;
1225 public readonly string Name;
1226 public readonly Location Location;
1227 public readonly Block Block;
1229 public VariableInfo VariableInfo;
1232 public Variable Variable {
1244 CompilerGenerated = 64,
1248 public enum ReadOnlyContext: byte {
1255 ReadOnlyContext ro_context;
1256 LocalBuilder builder;
1258 public LocalInfo (Expression type, string name, Block block, Location l)
1266 public LocalInfo (DeclSpace ds, Block block, Location l)
1268 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1273 public void ResolveVariable (EmitContext ec)
1275 Block theblock = Block;
1276 if (theblock.ScopeInfo != null)
1277 var = theblock.ScopeInfo.GetCapturedVariable (this);
1282 // This is needed to compile on both .NET 1.x and .NET 2.x
1283 // the later introduced `DeclareLocal (Type t, bool pinned)'
1285 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1287 builder = ec.ig.DeclareLocal (VariableType);
1289 var = new LocalVariable (this, builder);
1293 public void EmitSymbolInfo (EmitContext ec, string name)
1295 if (builder != null)
1296 ec.DefineLocalVariable (Name, builder);
1299 public bool IsThisAssigned (EmitContext ec)
1301 if (VariableInfo == null)
1302 throw new Exception ();
1304 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1307 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1310 public bool IsAssigned (EmitContext ec)
1312 if (VariableInfo == null)
1313 throw new Exception ();
1315 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1318 public bool Resolve (EmitContext ec)
1320 if (VariableType == null) {
1321 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1325 VariableType = texpr.Type;
1328 if (TypeManager.IsGenericParameter (VariableType))
1331 if (VariableType == TypeManager.void_type) {
1332 Expression.Error_VoidInvalidInTheContext (Location);
1336 if (VariableType.IsAbstract && VariableType.IsSealed) {
1337 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1341 if (VariableType.IsPointer && !ec.InUnsafe)
1342 Expression.UnsafeError (Location);
1347 public bool IsCaptured {
1348 get { return (flags & Flags.Captured) != 0; }
1349 set { flags |= Flags.Captured; }
1352 public bool IsConstant {
1353 get { return (flags & Flags.IsConstant) != 0; }
1354 set { flags |= Flags.IsConstant; }
1357 public bool AddressTaken {
1358 get { return (flags & Flags.AddressTaken) != 0; }
1359 set { flags |= Flags.AddressTaken; }
1362 public bool CompilerGenerated {
1363 get { return (flags & Flags.CompilerGenerated) != 0; }
1364 set { flags |= Flags.CompilerGenerated; }
1367 public override string ToString ()
1369 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1370 Name, Type, VariableInfo, Location);
1374 get { return (flags & Flags.Used) != 0; }
1375 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1378 public bool ReadOnly {
1379 get { return (flags & Flags.ReadOnly) != 0; }
1382 public void SetReadOnlyContext (ReadOnlyContext context)
1384 flags |= Flags.ReadOnly;
1385 ro_context = context;
1388 public string GetReadOnlyContext ()
1391 throw new InternalErrorException ("Variable is not readonly");
1393 switch (ro_context) {
1394 case ReadOnlyContext.Fixed:
1395 return "fixed variable";
1396 case ReadOnlyContext.Foreach:
1397 return "foreach iteration variable";
1398 case ReadOnlyContext.Using:
1399 return "using variable";
1401 throw new NotImplementedException ();
1405 // Whether the variable is pinned, if Pinned the variable has been
1406 // allocated in a pinned slot with DeclareLocal.
1408 public bool Pinned {
1409 get { return (flags & Flags.Pinned) != 0; }
1410 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1413 public bool IsThis {
1414 get { return (flags & Flags.IsThis) != 0; }
1415 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1418 Block IKnownVariable.Block {
1419 get { return Block; }
1422 Location IKnownVariable.Location {
1423 get { return Location; }
1426 protected class LocalVariable : Variable
1428 public readonly LocalInfo LocalInfo;
1429 LocalBuilder builder;
1431 public LocalVariable (LocalInfo local, LocalBuilder builder)
1433 this.LocalInfo = local;
1434 this.builder = builder;
1437 public override Type Type {
1438 get { return LocalInfo.VariableType; }
1441 public override bool HasInstance {
1442 get { return false; }
1445 public override bool NeedsTemporary {
1446 get { return false; }
1449 public override void EmitInstance (EmitContext ec)
1454 public override void Emit (EmitContext ec)
1456 ec.ig.Emit (OpCodes.Ldloc, builder);
1459 public override void EmitAssign (EmitContext ec)
1461 ec.ig.Emit (OpCodes.Stloc, builder);
1464 public override void EmitAddressOf (EmitContext ec)
1466 ec.ig.Emit (OpCodes.Ldloca, builder);
1470 public LocalInfo Clone (CloneContext clonectx)
1473 // Variables in anonymous block are not resolved yet
1475 if (VariableType == null)
1476 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1479 // Variables in method block are resolved
1481 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1482 li.VariableType = VariableType;
1488 /// Block represents a C# block.
1492 /// This class is used in a number of places: either to represent
1493 /// explicit blocks that the programmer places or implicit blocks.
1495 /// Implicit blocks are used as labels or to introduce variable
1498 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1499 /// they contain extra information that is not necessary on normal blocks.
1501 public class Block : Statement {
1502 public Block Parent;
1503 public readonly Location StartLocation;
1504 public Location EndLocation = Location.Null;
1506 public ExplicitBlock Explicit;
1507 public ToplevelBlock Toplevel;
1510 public enum Flags : byte {
1513 VariablesInitialized = 4,
1517 HasVarargs = 64, // Used in ToplevelBlock
1520 protected Flags flags;
1522 public bool Unchecked {
1523 get { return (flags & Flags.Unchecked) != 0; }
1524 set { flags |= Flags.Unchecked; }
1527 public bool Unsafe {
1528 get { return (flags & Flags.Unsafe) != 0; }
1529 set { flags |= Flags.Unsafe; }
1533 // The statements in this block
1535 protected ArrayList statements;
1539 // An array of Blocks. We keep track of children just
1540 // to generate the local variable declarations.
1542 // Statements and child statements are handled through the
1548 // Labels. (label, block) pairs.
1550 HybridDictionary labels;
1553 // Keeps track of (name, type) pairs
1555 IDictionary variables;
1558 // Keeps track of constants
1559 HybridDictionary constants;
1562 // Temporary variables.
1564 ArrayList temporary_variables;
1567 // If this is a switch section, the enclosing switch block.
1571 // TODO: merge with scope_initializers
1572 ExpressionStatement scope_init;
1573 ArrayList scope_initializers;
1575 ArrayList anonymous_children;
1577 protected static int id;
1581 int assignable_slots;
1582 protected ScopeInfo scope_info;
1583 bool unreachable_shown;
1586 public Block (Block parent)
1587 : this (parent, (Flags) 0, Location.Null, Location.Null)
1590 public Block (Block parent, Flags flags)
1591 : this (parent, flags, Location.Null, Location.Null)
1594 public Block (Block parent, Location start, Location end)
1595 : this (parent, (Flags) 0, start, end)
1598 public Block (Block parent, Flags flags, Location start, Location end)
1600 if (parent != null) {
1601 parent.AddChild (this);
1603 // the appropriate constructors will fixup these fields
1604 Toplevel = parent.Toplevel;
1605 Explicit = parent.Explicit;
1608 this.Parent = parent;
1610 this.StartLocation = start;
1611 this.EndLocation = end;
1614 statements = new ArrayList (4);
1617 public Block CreateSwitchBlock (Location start)
1619 // FIXME: should this be implicit?
1620 Block new_block = new ExplicitBlock (this, start, start);
1621 new_block.switch_block = this;
1626 get { return this_id; }
1629 public IDictionary Variables {
1631 if (variables == null)
1632 variables = new ListDictionary ();
1637 void AddChild (Block b)
1639 if (children == null)
1640 children = new ArrayList (1);
1645 public void SetEndLocation (Location loc)
1650 protected static void Error_158 (string name, Location loc)
1652 Report.Error (158, loc, "The label `{0}' shadows another label " +
1653 "by the same name in a contained scope", name);
1657 /// Adds a label to the current block.
1661 /// false if the name already exists in this block. true
1665 public bool AddLabel (LabeledStatement target)
1667 if (switch_block != null)
1668 return switch_block.AddLabel (target);
1670 string name = target.Name;
1673 while (cur != null) {
1674 LabeledStatement s = cur.DoLookupLabel (name);
1676 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1677 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1681 if (this == Explicit)
1687 while (cur != null) {
1688 if (cur.DoLookupLabel (name) != null) {
1689 Error_158 (name, target.loc);
1693 if (children != null) {
1694 foreach (Block b in children) {
1695 LabeledStatement s = b.DoLookupLabel (name);
1699 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1700 Error_158 (name, target.loc);
1708 Toplevel.CheckError158 (name, target.loc);
1711 labels = new HybridDictionary();
1713 labels.Add (name, target);
1717 public LabeledStatement LookupLabel (string name)
1719 LabeledStatement s = DoLookupLabel (name);
1723 if (children == null)
1726 foreach (Block child in children) {
1727 if (Explicit != child.Explicit)
1730 s = child.LookupLabel (name);
1738 LabeledStatement DoLookupLabel (string name)
1740 if (switch_block != null)
1741 return switch_block.LookupLabel (name);
1744 if (labels.Contains (name))
1745 return ((LabeledStatement) labels [name]);
1750 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1753 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1754 while (kvi == null) {
1755 b = b.Explicit.Parent;
1758 kvi = b.Explicit.GetKnownVariable (name);
1764 // Is kvi.Block nested inside 'b'
1765 if (b.Explicit != kvi.Block.Explicit) {
1767 // If a variable by the same name it defined in a nested block of this
1768 // block, we violate the invariant meaning in a block.
1771 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1772 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1777 // It's ok if the definition is in a nested subblock of b, but not
1778 // nested inside this block -- a definition in a sibling block
1779 // should not affect us.
1785 // Block 'b' and kvi.Block are the same textual block.
1786 // However, different variables are extant.
1788 // Check if the variable is in scope in both blocks. We use
1789 // an indirect check that depends on AddVariable doing its
1790 // part in maintaining the invariant-meaning-in-block property.
1792 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1796 // Even though we detected the error when the name is used, we
1797 // treat it as if the variable declaration was in error.
1799 Report.SymbolRelatedToPreviousError (loc, name);
1800 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1804 public LocalInfo AddVariable (Expression type, string name, Location l)
1806 LocalInfo vi = GetLocalInfo (name);
1808 Report.SymbolRelatedToPreviousError (vi.Location, name);
1809 if (Explicit == vi.Block.Explicit)
1810 Error_AlreadyDeclared (l, name, null);
1812 Error_AlreadyDeclared (l, name, "parent");
1816 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1818 Report.SymbolRelatedToPreviousError (pi.Location, name);
1819 Error_AlreadyDeclared (loc, name,
1820 pi.Block == Toplevel ? "method argument" : "parent or current");
1824 if (Toplevel.GenericMethod != null) {
1825 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1826 if (tp.Name == name) {
1827 Report.SymbolRelatedToPreviousError (tp);
1828 Error_AlreadyDeclaredTypeParameter (loc, name);
1834 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1836 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1837 Error_AlreadyDeclared (l, name, "child");
1841 vi = new LocalInfo (type, name, this, l);
1844 if ((flags & Flags.VariablesInitialized) != 0)
1845 throw new InternalErrorException ("block has already been resolved");
1850 protected virtual void AddVariable (LocalInfo li)
1852 Variables.Add (li.Name, li);
1853 Explicit.AddKnownVariable (li.Name, li);
1856 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1858 if (reason == null) {
1859 Error_AlreadyDeclared (loc, var);
1863 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1864 "in this scope because it would give a different meaning " +
1865 "to `{0}', which is already used in a `{1}' scope " +
1866 "to denote something else", var, reason);
1869 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1871 Report.Error (128, loc,
1872 "A local variable named `{0}' is already defined in this scope", name);
1875 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1877 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1880 public bool AddConstant (Expression type, string name, Expression value, Location l)
1882 if (AddVariable (type, name, l) == null)
1885 if (constants == null)
1886 constants = new HybridDictionary();
1888 constants.Add (name, value);
1890 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1895 static int next_temp_id = 0;
1897 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1899 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1901 if (temporary_variables == null)
1902 temporary_variables = new ArrayList ();
1904 int id = ++next_temp_id;
1905 string name = "$s_" + id.ToString ();
1907 LocalInfo li = new LocalInfo (te, name, this, loc);
1908 li.CompilerGenerated = true;
1909 temporary_variables.Add (li);
1913 public LocalInfo GetLocalInfo (string name)
1915 for (Block b = this; b != null; b = b.Parent) {
1916 if (b.variables != null) {
1917 LocalInfo ret = b.variables [name] as LocalInfo;
1925 public Expression GetVariableType (string name)
1927 LocalInfo vi = GetLocalInfo (name);
1928 return vi == null ? null : vi.Type;
1931 public Expression GetConstantExpression (string name)
1933 for (Block b = this; b != null; b = b.Parent) {
1934 if (b.constants != null) {
1935 Expression ret = b.constants [name] as Expression;
1944 // It should be used by expressions which require to
1945 // register a statement during resolve process.
1947 public void AddScopeStatement (StatementExpression s)
1949 if (scope_initializers == null)
1950 scope_initializers = new ArrayList ();
1952 scope_initializers.Add (s);
1955 public void AddStatement (Statement s)
1958 flags |= Flags.BlockUsed;
1962 get { return (flags & Flags.BlockUsed) != 0; }
1967 flags |= Flags.BlockUsed;
1970 public bool HasRet {
1971 get { return (flags & Flags.HasRet) != 0; }
1974 public bool IsDestructor {
1975 get { return (flags & Flags.IsDestructor) != 0; }
1978 public void SetDestructor ()
1980 flags |= Flags.IsDestructor;
1983 public int AssignableSlots {
1985 if ((flags & Flags.VariablesInitialized) == 0)
1986 throw new Exception ("Variables have not been initialized yet");
1987 return assignable_slots;
1991 public ScopeInfo ScopeInfo {
1992 get { return scope_info; }
1995 public ScopeInfo CreateScopeInfo ()
1997 if (scope_info == null)
1998 scope_info = ScopeInfo.CreateScope (this);
2003 public ArrayList AnonymousChildren {
2004 get { return anonymous_children; }
2007 public void AddAnonymousChild (ToplevelBlock b)
2009 if (anonymous_children == null)
2010 anonymous_children = new ArrayList ();
2012 anonymous_children.Add (b);
2015 void DoResolveConstants (EmitContext ec)
2017 if (constants == null)
2020 if (variables == null)
2021 throw new InternalErrorException ("cannot happen");
2023 foreach (DictionaryEntry de in variables) {
2024 string name = (string) de.Key;
2025 LocalInfo vi = (LocalInfo) de.Value;
2026 Type variable_type = vi.VariableType;
2028 if (variable_type == null) {
2029 if (vi.Type is VarExpr)
2030 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2035 Expression cv = (Expression) constants [name];
2039 // Don't let 'const int Foo = Foo;' succeed.
2040 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2041 // which in turn causes the 'must be constant' error to be triggered.
2042 constants.Remove (name);
2044 if (!Const.IsConstantTypeValid (variable_type)) {
2045 Const.Error_InvalidConstantType (variable_type, loc);
2049 ec.CurrentBlock = this;
2051 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2052 e = cv.Resolve (ec);
2057 Constant ce = e as Constant;
2059 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2063 e = ce.ConvertImplicitly (variable_type);
2065 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2066 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2068 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2072 constants.Add (name, e);
2073 vi.IsConstant = true;
2077 protected void ResolveMeta (EmitContext ec, int offset)
2079 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2081 // If some parent block was unsafe, we remain unsafe even if this block
2082 // isn't explicitly marked as such.
2083 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2084 flags |= Flags.VariablesInitialized;
2086 if (variables != null) {
2087 foreach (LocalInfo li in variables.Values) {
2088 if (!li.Resolve (ec))
2090 li.VariableInfo = new VariableInfo (li, offset);
2091 offset += li.VariableInfo.Length;
2094 assignable_slots = offset;
2096 DoResolveConstants (ec);
2098 if (children == null)
2100 foreach (Block b in children)
2101 b.ResolveMeta (ec, offset);
2106 // Emits the local variable declarations for a block
2108 public virtual void EmitMeta (EmitContext ec)
2110 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2111 if (ScopeInfo != null) {
2112 scope_init = ScopeInfo.GetScopeInitializer (ec);
2113 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2117 if (variables != null){
2118 foreach (LocalInfo vi in variables.Values)
2119 vi.ResolveVariable (ec);
2122 if (temporary_variables != null) {
2123 for (int i = 0; i < temporary_variables.Count; i++)
2124 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2127 if (children != null){
2128 for (int i = 0; i < children.Count; i++)
2129 ((Block)children[i]).EmitMeta(ec);
2133 void UsageWarning (FlowBranching.UsageVector vector)
2137 if ((variables != null) && (Report.WarningLevel >= 3)) {
2138 foreach (DictionaryEntry de in variables){
2139 LocalInfo vi = (LocalInfo) de.Value;
2144 name = (string) de.Key;
2146 // vi.VariableInfo can be null for 'catch' variables
2147 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2148 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2150 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2156 private void CheckPossibleMistakenEmptyStatement (Statement s)
2160 // Some statements are wrapped by a Block. Since
2161 // others' internal could be changed, here I treat
2162 // them as possibly wrapped by Block equally.
2163 Block b = s as Block;
2164 if (b != null && b.statements.Count == 1)
2165 s = (Statement) b.statements [0];
2168 body = ((Lock) s).Statement;
2170 body = ((For) s).Statement;
2171 else if (s is Foreach)
2172 body = ((Foreach) s).Statement;
2173 else if (s is While)
2174 body = ((While) s).Statement;
2175 else if (s is Using)
2176 body = ((Using) s).Statement;
2177 else if (s is Fixed)
2178 body = ((Fixed) s).Statement;
2182 if (body == null || body is EmptyStatement)
2183 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2186 public override bool Resolve (EmitContext ec)
2188 Block prev_block = ec.CurrentBlock;
2191 int errors = Report.Errors;
2193 ec.CurrentBlock = this;
2194 ec.StartFlowBranching (this);
2196 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2199 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2200 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2201 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2202 // responsible for handling the situation.
2204 int statement_count = statements.Count;
2205 for (int ix = 0; ix < statement_count; ix++){
2206 Statement s = (Statement) statements [ix];
2207 // Check possible empty statement (CS0642)
2208 if (Report.WarningLevel >= 3 &&
2209 ix + 1 < statement_count &&
2210 statements [ix + 1] is Block)
2211 CheckPossibleMistakenEmptyStatement (s);
2214 // Warn if we detect unreachable code.
2217 if (s is EmptyStatement)
2221 ((Block) s).unreachable = true;
2223 if (!unreachable_shown && !(s is LabeledStatement)) {
2224 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2225 unreachable_shown = true;
2230 // Note that we're not using ResolveUnreachable() for unreachable
2231 // statements here. ResolveUnreachable() creates a temporary
2232 // flow branching and kills it afterwards. This leads to problems
2233 // if you have two unreachable statements where the first one
2234 // assigns a variable and the second one tries to access it.
2237 if (!s.Resolve (ec)) {
2239 if (ec.IsInProbingMode)
2242 statements [ix] = EmptyStatement.Value;
2246 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2247 statements [ix] = EmptyStatement.Value;
2249 num_statements = ix + 1;
2251 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2252 if (unreachable && s is LabeledStatement)
2253 throw new InternalErrorException ("should not happen");
2256 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2257 ec.CurrentBranching, statement_count, num_statements);
2259 while (ec.CurrentBranching is FlowBranchingLabeled)
2260 ec.EndFlowBranching ();
2262 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2264 ec.CurrentBlock = prev_block;
2266 // If we're a non-static `struct' constructor which doesn't have an
2267 // initializer, then we must initialize all of the struct's fields.
2268 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
2271 if ((labels != null) && (Report.WarningLevel >= 2)) {
2272 foreach (LabeledStatement label in labels.Values)
2273 if (!label.HasBeenReferenced)
2274 Report.Warning (164, 2, label.loc,
2275 "This label has not been referenced");
2278 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2280 if (vector.IsUnreachable)
2281 flags |= Flags.HasRet;
2283 if (ok && (errors == Report.Errors)) {
2284 UsageWarning (vector);
2290 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2292 unreachable_shown = true;
2296 Report.Warning (162, 2, loc, "Unreachable code detected");
2298 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2299 bool ok = Resolve (ec);
2300 ec.KillFlowBranching ();
2305 protected override void DoEmit (EmitContext ec)
2307 for (int ix = 0; ix < num_statements; ix++){
2308 Statement s = (Statement) statements [ix];
2313 public override void Emit (EmitContext ec)
2315 Block prev_block = ec.CurrentBlock;
2317 ec.CurrentBlock = this;
2319 bool emit_debug_info = SymbolWriter.HasSymbolWriter;
2320 bool is_lexical_block = (this == Explicit) && (Parent != null) &&
2321 ((flags & Flags.IsIterator) == 0);
2323 bool omit_debug_info = ec.OmitDebuggingInfo;
2325 if (emit_debug_info) {
2326 if (is_lexical_block)
2330 if ((scope_init != null) || (scope_initializers != null))
2331 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2333 if (scope_init != null) {
2334 ec.OmitDebuggingInfo = true;
2335 scope_init.EmitStatement (ec);
2336 ec.OmitDebuggingInfo = omit_debug_info;
2338 if (scope_initializers != null) {
2339 ec.OmitDebuggingInfo = true;
2340 foreach (StatementExpression s in scope_initializers)
2342 ec.OmitDebuggingInfo = omit_debug_info;
2345 if ((scope_init != null) || (scope_initializers != null))
2346 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2348 ec.Mark (StartLocation, true);
2351 if (emit_debug_info) {
2352 EmitSymbolInfo (ec);
2354 if (is_lexical_block)
2358 ec.CurrentBlock = prev_block;
2361 protected virtual void EmitSymbolInfo (EmitContext ec)
2363 if (variables != null) {
2364 foreach (DictionaryEntry de in variables) {
2365 string name = (string) de.Key;
2366 LocalInfo vi = (LocalInfo) de.Value;
2368 vi.EmitSymbolInfo (ec, name);
2373 public override string ToString ()
2375 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2378 protected override void CloneTo (CloneContext clonectx, Statement t)
2380 Block target = (Block) t;
2382 clonectx.AddBlockMap (this, target);
2384 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2385 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2387 target.Parent = clonectx.RemapBlockCopy (Parent);
2389 if (variables != null){
2390 target.variables = new Hashtable ();
2392 foreach (DictionaryEntry de in variables){
2393 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2394 target.variables [de.Key] = newlocal;
2395 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2399 target.statements = new ArrayList (statements.Count);
2400 foreach (Statement s in statements)
2401 target.statements.Add (s.Clone (clonectx));
2403 if (target.children != null){
2404 target.children = new ArrayList (children.Count);
2405 foreach (Block b in children){
2406 target.children.Add (clonectx.LookupBlock (b));
2411 // TODO: labels, switch_block, constants (?), anonymous_children
2416 public class ExplicitBlock : Block {
2417 public ExplicitBlock (Block parent, Location start, Location end)
2418 : this (parent, (Flags) 0, start, end)
2422 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2423 : base (parent, flags, start, end)
2425 this.Explicit = this;
2428 public bool IsIterator {
2429 get { return (flags & Flags.IsIterator) != 0; }
2432 HybridDictionary known_variables;
2435 // Marks a variable with name @name as being used in this or a child block.
2436 // If a variable name has been used in a child block, it's illegal to
2437 // declare a variable with the same name in the current block.
2439 internal void AddKnownVariable (string name, IKnownVariable info)
2441 if (known_variables == null)
2442 known_variables = new HybridDictionary();
2444 known_variables [name] = info;
2447 Parent.Explicit.AddKnownVariable (name, info);
2450 internal IKnownVariable GetKnownVariable (string name)
2452 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2455 protected override void CloneTo (CloneContext clonectx, Statement t)
2457 ExplicitBlock target = (ExplicitBlock) t;
2458 target.known_variables = null;
2459 base.CloneTo (clonectx, t);
2463 public class ToplevelParameterInfo : IKnownVariable {
2464 public readonly ToplevelBlock Block;
2465 public readonly int Index;
2466 public VariableInfo VariableInfo;
2468 Block IKnownVariable.Block {
2469 get { return Block; }
2471 public Parameter Parameter {
2472 get { return Block.Parameters [Index]; }
2474 public Location Location {
2475 get { return Parameter.Location; }
2478 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2486 // A toplevel block contains extra information, the split is done
2487 // only to separate information that would otherwise bloat the more
2488 // lightweight Block.
2490 // In particular, this was introduced when the support for Anonymous
2491 // Methods was implemented.
2493 public class ToplevelBlock : ExplicitBlock {
2494 GenericMethod generic;
2495 FlowBranchingToplevel top_level_branching;
2496 AnonymousContainer anonymous_container;
2497 RootScopeInfo root_scope;
2498 Parameters parameters;
2499 ToplevelParameterInfo[] parameter_info;
2501 public bool HasVarargs {
2502 get { return (flags & Flags.HasVarargs) != 0; }
2503 set { flags |= Flags.HasVarargs; }
2507 // The parameters for the block.
2509 public Parameters Parameters {
2510 get { return parameters; }
2513 public bool CompleteContexts (EmitContext ec)
2515 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2517 if (root_scope != null)
2518 root_scope.LinkScopes ();
2520 if (Parent == null && root_scope != null) {
2521 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2523 if (root_scope.DefineType () == null)
2525 if (!root_scope.ResolveType ())
2527 if (!root_scope.ResolveMembers ())
2529 if (!root_scope.DefineMembers ())
2536 public GenericMethod GenericMethod {
2537 get { return generic; }
2540 public ToplevelBlock Container {
2541 get { return Parent == null ? null : Parent.Toplevel; }
2544 public AnonymousContainer AnonymousContainer {
2545 get { return anonymous_container; }
2546 set { anonymous_container = value; }
2549 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2550 this (parent, (Flags) 0, parameters, start)
2554 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2555 this (parent, parameters, start)
2557 this.generic = generic;
2560 public ToplevelBlock (Parameters parameters, Location start) :
2561 this (null, (Flags) 0, parameters, start)
2565 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2566 this (null, flags, parameters, start)
2570 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2571 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2572 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2573 base (null, flags, start, Location.Null)
2575 this.Toplevel = this;
2577 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2578 this.Parent = parent;
2580 parent.AddAnonymousChild (this);
2582 if (this.parameters.Count != 0)
2583 ProcessParameters ();
2586 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2590 protected override void CloneTo (CloneContext clonectx, Statement t)
2592 ToplevelBlock target = (ToplevelBlock) t;
2593 base.CloneTo (clonectx, t);
2595 if (parameters.Count != 0)
2596 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2597 for (int i = 0; i < parameters.Count; ++i)
2598 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2601 public bool CheckError158 (string name, Location loc)
2603 if (AnonymousChildren != null) {
2604 foreach (ToplevelBlock child in AnonymousChildren) {
2605 if (!child.CheckError158 (name, loc))
2610 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2611 if (!c.DoCheckError158 (name, loc))
2618 public virtual Expression GetTransparentIdentifier (string name)
2623 void ProcessParameters ()
2625 int n = parameters.Count;
2626 parameter_info = new ToplevelParameterInfo [n];
2627 for (int i = 0; i < n; ++i) {
2628 parameter_info [i] = new ToplevelParameterInfo (this, i);
2630 Parameter p = parameters [i];
2634 string name = p.Name;
2635 LocalInfo vi = GetLocalInfo (name);
2637 Report.SymbolRelatedToPreviousError (vi.Location, name);
2638 Error_AlreadyDeclared (loc, name, "parent or current");
2642 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2644 Report.SymbolRelatedToPreviousError (pi.Location, name);
2645 Error_AlreadyDeclared (loc, name, "parent or current");
2649 AddKnownVariable (name, parameter_info [i]);
2652 // mark this block as "used" so that we create local declarations in a sub-block
2653 // FIXME: This appears to uncover a lot of bugs
2657 bool DoCheckError158 (string name, Location loc)
2659 LabeledStatement s = LookupLabel (name);
2661 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2662 Error_158 (name, loc);
2669 public RootScopeInfo CreateRootScope (TypeContainer host)
2671 if (root_scope != null)
2674 if (Container == null)
2675 root_scope = new RootScopeInfo (
2676 this, host, generic, StartLocation);
2678 if (scope_info != null)
2679 throw new InternalErrorException ();
2681 scope_info = root_scope;
2685 public override Expression CreateExpressionTree (EmitContext ec)
2687 return ((Statement) statements [0]).CreateExpressionTree (ec);
2690 public void CreateIteratorHost (RootScopeInfo root)
2692 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2694 if (Parent != null || root_scope != null)
2695 throw new InternalErrorException ();
2697 scope_info = root_scope = root;
2700 public RootScopeInfo RootScope {
2702 if (root_scope != null)
2704 else if (Container != null)
2705 return Container.RootScope;
2711 public FlowBranchingToplevel TopLevelBranching {
2712 get { return top_level_branching; }
2716 // This is used if anonymous methods are used inside an iterator
2717 // (see 2test-22.cs for an example).
2719 // The AnonymousMethod is created while parsing - at a time when we don't
2720 // know yet that we're inside an iterator, so it's `Container' is initially
2721 // null. Later on, when resolving the iterator, we need to move the
2722 // anonymous method into that iterator.
2724 public void ReParent (ToplevelBlock new_parent)
2726 if ((flags & Flags.VariablesInitialized) != 0)
2727 throw new InternalErrorException ("block has already been resolved");
2729 Parent = new_parent;
2733 // Returns a `ParameterReference' for the given name, or null if there
2734 // is no such parameter
2736 public ParameterReference GetParameterReference (string name, Location loc)
2738 ToplevelParameterInfo p = GetParameterInfo (name);
2739 return p == null ? null : new ParameterReference (this, p, loc);
2742 public ToplevelParameterInfo GetParameterInfo (string name)
2745 for (ToplevelBlock t = this; t != null; t = t.Container) {
2746 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2748 return t.parameter_info [idx];
2753 LocalInfo this_variable = null;
2756 // Returns the "this" instance variable of this block.
2757 // See AddThisVariable() for more information.
2759 public LocalInfo ThisVariable {
2760 get { return this_variable; }
2765 // This is used by non-static `struct' constructors which do not have an
2766 // initializer - in this case, the constructor must initialize all of the
2767 // struct's fields. To do this, we add a "this" variable and use the flow
2768 // analysis code to ensure that it's been fully initialized before control
2769 // leaves the constructor.
2771 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2773 if (this_variable == null) {
2774 this_variable = new LocalInfo (ds, this, l);
2775 this_variable.Used = true;
2776 this_variable.IsThis = true;
2778 Variables.Add ("this", this_variable);
2781 return this_variable;
2784 public bool IsThisAssigned (EmitContext ec)
2786 return this_variable == null || this_variable.IsThisAssigned (ec);
2789 public bool ResolveMeta (EmitContext ec, Parameters ip)
2791 int errors = Report.Errors;
2792 int orig_count = parameters.Count;
2794 if (top_level_branching != null)
2800 // Assert: orig_count != parameter.Count => orig_count == 0
2801 if (orig_count != 0 && orig_count != parameters.Count)
2802 throw new InternalErrorException ("parameter information mismatch");
2804 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2806 for (int i = 0; i < orig_count; ++i) {
2807 Parameter.Modifier mod = parameters.ParameterModifier (i);
2809 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2812 VariableInfo vi = new VariableInfo (ip, i, offset);
2813 parameter_info [i].VariableInfo = vi;
2814 offset += vi.Length;
2817 ResolveMeta (ec, offset);
2819 top_level_branching = ec.StartFlowBranching (this);
2821 return Report.Errors == errors;
2825 // Check whether all `out' parameters have been assigned.
2827 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2829 if (vector.IsUnreachable)
2832 int n = parameter_info == null ? 0 : parameter_info.Length;
2834 for (int i = 0; i < n; i++) {
2835 VariableInfo var = parameter_info [i].VariableInfo;
2840 if (vector.IsAssigned (var, false))
2843 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2848 public override void EmitMeta (EmitContext ec)
2850 // Avoid declaring an IL variable for this_variable since it is not accessed
2851 // from the generated IL
2852 if (this_variable != null)
2853 Variables.Remove ("this");
2855 parameters.ResolveVariable (this);
2858 protected override void EmitSymbolInfo (EmitContext ec)
2860 if ((AnonymousContainer != null) && (AnonymousContainer.Scope != null))
2861 SymbolWriter.DefineScopeVariable (AnonymousContainer.Scope.ID);
2863 base.EmitSymbolInfo (ec);
2866 public void MakeIterator (Iterator iterator)
2868 flags |= Flags.IsIterator;
2870 Block block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2871 foreach (Statement stmt in statements)
2872 block.AddStatement (stmt);
2873 statements.Clear ();
2874 statements.Add (new MoveNextStatement (iterator, block));
2877 protected class MoveNextStatement : Statement {
2881 public MoveNextStatement (Iterator iterator, Block block)
2883 this.iterator = iterator;
2885 this.loc = iterator.Location;
2888 public override bool Resolve (EmitContext ec)
2890 return block.Resolve (ec);
2893 protected override void DoEmit (EmitContext ec)
2895 iterator.EmitMoveNext (ec, block);
2899 public override string ToString ()
2901 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2902 root_scope, anonymous_container != null ?
2903 anonymous_container.Scope : null);
2907 public class SwitchLabel {
2914 Label il_label_code;
2915 bool il_label_code_set;
2917 public static readonly object NullStringCase = new object ();
2920 // if expr == null, then it is the default case.
2922 public SwitchLabel (Expression expr, Location l)
2928 public Expression Label {
2934 public object Converted {
2940 public Label GetILLabel (EmitContext ec)
2943 il_label = ec.ig.DefineLabel ();
2944 il_label_set = true;
2949 public Label GetILLabelCode (EmitContext ec)
2951 if (!il_label_code_set){
2952 il_label_code = ec.ig.DefineLabel ();
2953 il_label_code_set = true;
2955 return il_label_code;
2959 // Resolves the expression, reduces it to a literal if possible
2960 // and then converts it to the requested type.
2962 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2964 Expression e = label.Resolve (ec);
2969 Constant c = e as Constant;
2971 Report.Error (150, loc, "A constant value is expected");
2975 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2976 converted = NullStringCase;
2980 if (allow_nullable && c.GetValue () == null) {
2981 converted = NullStringCase;
2985 c = c.ImplicitConversionRequired (required_type, loc);
2989 converted = c.GetValue ();
2993 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2996 if (converted == null)
2998 else if (converted == NullStringCase)
3000 else if (TypeManager.IsEnumType (switch_type))
3001 label = TypeManager.CSharpEnumValue (switch_type, converted);
3003 label = converted.ToString ();
3005 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3006 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3009 public SwitchLabel Clone (CloneContext clonectx)
3011 return new SwitchLabel (label.Clone (clonectx), loc);
3015 public class SwitchSection {
3016 // An array of SwitchLabels.
3017 public readonly ArrayList Labels;
3018 public readonly Block Block;
3020 public SwitchSection (ArrayList labels, Block block)
3026 public SwitchSection Clone (CloneContext clonectx)
3028 ArrayList cloned_labels = new ArrayList ();
3030 foreach (SwitchLabel sl in cloned_labels)
3031 cloned_labels.Add (sl.Clone (clonectx));
3033 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3037 public class Switch : Statement {
3038 public ArrayList Sections;
3039 public Expression Expr;
3042 /// Maps constants whose type type SwitchType to their SwitchLabels.
3044 public IDictionary Elements;
3047 /// The governing switch type
3049 public Type SwitchType;
3054 Label default_target;
3056 Expression new_expr;
3058 SwitchSection constant_section;
3059 SwitchSection default_section;
3063 // Nullable Types support for GMCS.
3065 Nullable.Unwrap unwrap;
3067 protected bool HaveUnwrap {
3068 get { return unwrap != null; }
3071 protected bool HaveUnwrap {
3072 get { return false; }
3077 // The types allowed to be implicitly cast from
3078 // on the governing type
3080 static Type [] allowed_types;
3082 public Switch (Expression e, ArrayList sects, Location l)
3089 public bool GotDefault {
3091 return default_section != null;
3095 public Label DefaultTarget {
3097 return default_target;
3102 // Determines the governing type for a switch. The returned
3103 // expression might be the expression from the switch, or an
3104 // expression that includes any potential conversions to the
3105 // integral types or to string.
3107 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3111 if (t == TypeManager.byte_type ||
3112 t == TypeManager.sbyte_type ||
3113 t == TypeManager.ushort_type ||
3114 t == TypeManager.short_type ||
3115 t == TypeManager.uint32_type ||
3116 t == TypeManager.int32_type ||
3117 t == TypeManager.uint64_type ||
3118 t == TypeManager.int64_type ||
3119 t == TypeManager.char_type ||
3120 t == TypeManager.string_type ||
3121 t == TypeManager.bool_type ||
3122 TypeManager.IsEnumType (t))
3125 if (allowed_types == null){
3126 allowed_types = new Type [] {
3127 TypeManager.sbyte_type,
3128 TypeManager.byte_type,
3129 TypeManager.short_type,
3130 TypeManager.ushort_type,
3131 TypeManager.int32_type,
3132 TypeManager.uint32_type,
3133 TypeManager.int64_type,
3134 TypeManager.uint64_type,
3135 TypeManager.char_type,
3136 TypeManager.string_type,
3137 TypeManager.bool_type
3142 // Try to find a *user* defined implicit conversion.
3144 // If there is no implicit conversion, or if there are multiple
3145 // conversions, we have to report an error
3147 Expression converted = null;
3148 foreach (Type tt in allowed_types){
3151 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3156 // Ignore over-worked ImplicitUserConversions that do
3157 // an implicit conversion in addition to the user conversion.
3159 if (!(e is UserCast))
3162 if (converted != null){
3163 Report.ExtraInformation (
3165 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3166 TypeManager.CSharpName (expr.Type)));
3176 // Performs the basic sanity checks on the switch statement
3177 // (looks for duplicate keys and non-constant expressions).
3179 // It also returns a hashtable with the keys that we will later
3180 // use to compute the switch tables
3182 bool CheckSwitch (EmitContext ec)
3185 Elements = Sections.Count > 10 ?
3186 (IDictionary)new Hashtable () :
3187 (IDictionary)new ListDictionary ();
3189 foreach (SwitchSection ss in Sections){
3190 foreach (SwitchLabel sl in ss.Labels){
3191 if (sl.Label == null){
3192 if (default_section != null){
3193 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3196 default_section = ss;
3200 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3205 object key = sl.Converted;
3207 Elements.Add (key, sl);
3208 } catch (ArgumentException) {
3209 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3217 void EmitObjectInteger (ILGenerator ig, object k)
3220 IntConstant.EmitInt (ig, (int) k);
3221 else if (k is Constant) {
3222 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3225 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3228 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3230 IntConstant.EmitInt (ig, (int) (long) k);
3231 ig.Emit (OpCodes.Conv_I8);
3234 LongConstant.EmitLong (ig, (long) k);
3236 else if (k is ulong)
3238 ulong ul = (ulong) k;
3241 IntConstant.EmitInt (ig, unchecked ((int) ul));
3242 ig.Emit (OpCodes.Conv_U8);
3246 LongConstant.EmitLong (ig, unchecked ((long) ul));
3250 IntConstant.EmitInt (ig, (int) ((char) k));
3251 else if (k is sbyte)
3252 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3254 IntConstant.EmitInt (ig, (int) ((byte) k));
3255 else if (k is short)
3256 IntConstant.EmitInt (ig, (int) ((short) k));
3257 else if (k is ushort)
3258 IntConstant.EmitInt (ig, (int) ((ushort) k));
3260 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3262 throw new Exception ("Unhandled case");
3265 // structure used to hold blocks of keys while calculating table switch
3266 class KeyBlock : IComparable
3268 public KeyBlock (long _first)
3270 first = last = _first;
3274 public ArrayList element_keys = null;
3275 // how many items are in the bucket
3276 public int Size = 1;
3279 get { return (int) (last - first + 1); }
3281 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3283 return kb_last.last - kb_first.first + 1;
3285 public int CompareTo (object obj)
3287 KeyBlock kb = (KeyBlock) obj;
3288 int nLength = Length;
3289 int nLengthOther = kb.Length;
3290 if (nLengthOther == nLength)
3291 return (int) (kb.first - first);
3292 return nLength - nLengthOther;
3297 /// This method emits code for a lookup-based switch statement (non-string)
3298 /// Basically it groups the cases into blocks that are at least half full,
3299 /// and then spits out individual lookup opcodes for each block.
3300 /// It emits the longest blocks first, and short blocks are just
3301 /// handled with direct compares.
3303 /// <param name="ec"></param>
3304 /// <param name="val"></param>
3305 /// <returns></returns>
3306 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3308 int element_count = Elements.Count;
3309 object [] element_keys = new object [element_count];
3310 Elements.Keys.CopyTo (element_keys, 0);
3311 Array.Sort (element_keys);
3313 // initialize the block list with one element per key
3314 ArrayList key_blocks = new ArrayList ();
3315 foreach (object key in element_keys)
3316 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3318 KeyBlock current_kb;
3319 // iteratively merge the blocks while they are at least half full
3320 // there's probably a really cool way to do this with a tree...
3321 while (key_blocks.Count > 1)
3323 ArrayList key_blocks_new = new ArrayList ();
3324 current_kb = (KeyBlock) key_blocks [0];
3325 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3327 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3328 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3331 current_kb.last = kb.last;
3332 current_kb.Size += kb.Size;
3336 // start a new block
3337 key_blocks_new.Add (current_kb);
3341 key_blocks_new.Add (current_kb);
3342 if (key_blocks.Count == key_blocks_new.Count)
3344 key_blocks = key_blocks_new;
3347 // initialize the key lists
3348 foreach (KeyBlock kb in key_blocks)
3349 kb.element_keys = new ArrayList ();
3351 // fill the key lists
3353 if (key_blocks.Count > 0) {
3354 current_kb = (KeyBlock) key_blocks [0];
3355 foreach (object key in element_keys)
3357 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3358 System.Convert.ToInt64 (key) > current_kb.last;
3360 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3361 current_kb.element_keys.Add (key);
3365 // sort the blocks so we can tackle the largest ones first
3368 // okay now we can start...
3369 ILGenerator ig = ec.ig;
3370 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3371 Label lbl_default = ig.DefineLabel ();
3373 Type type_keys = null;
3374 if (element_keys.Length > 0)
3375 type_keys = element_keys [0].GetType (); // used for conversions
3379 if (TypeManager.IsEnumType (SwitchType))
3380 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3382 compare_type = SwitchType;
3384 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3386 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3387 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3390 foreach (object key in kb.element_keys)
3392 ig.Emit (OpCodes.Ldloc, val);
3393 EmitObjectInteger (ig, key);
3394 SwitchLabel sl = (SwitchLabel) Elements [key];
3395 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3400 // TODO: if all the keys in the block are the same and there are
3401 // no gaps/defaults then just use a range-check.
3402 if (compare_type == TypeManager.int64_type ||
3403 compare_type == TypeManager.uint64_type)
3405 // TODO: optimize constant/I4 cases
3407 // check block range (could be > 2^31)
3408 ig.Emit (OpCodes.Ldloc, val);
3409 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3410 ig.Emit (OpCodes.Blt, lbl_default);
3411 ig.Emit (OpCodes.Ldloc, val);
3412 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3413 ig.Emit (OpCodes.Bgt, lbl_default);
3416 ig.Emit (OpCodes.Ldloc, val);
3419 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3420 ig.Emit (OpCodes.Sub);
3422 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3427 ig.Emit (OpCodes.Ldloc, val);
3428 int first = (int) kb.first;
3431 IntConstant.EmitInt (ig, first);
3432 ig.Emit (OpCodes.Sub);
3436 IntConstant.EmitInt (ig, -first);
3437 ig.Emit (OpCodes.Add);
3441 // first, build the list of labels for the switch
3443 int cJumps = kb.Length;
3444 Label [] switch_labels = new Label [cJumps];
3445 for (int iJump = 0; iJump < cJumps; iJump++)
3447 object key = kb.element_keys [iKey];
3448 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3450 SwitchLabel sl = (SwitchLabel) Elements [key];
3451 switch_labels [iJump] = sl.GetILLabel (ec);
3455 switch_labels [iJump] = lbl_default;
3457 // emit the switch opcode
3458 ig.Emit (OpCodes.Switch, switch_labels);
3461 // mark the default for this block
3463 ig.MarkLabel (lbl_default);
3466 // TODO: find the default case and emit it here,
3467 // to prevent having to do the following jump.
3468 // make sure to mark other labels in the default section
3470 // the last default just goes to the end
3471 ig.Emit (OpCodes.Br, lbl_default);
3473 // now emit the code for the sections
3474 bool found_default = false;
3475 bool found_null = false;
3476 foreach (SwitchSection ss in Sections)
3478 foreach (SwitchLabel sl in ss.Labels)
3479 if (sl.Converted == SwitchLabel.NullStringCase)
3483 foreach (SwitchSection ss in Sections)
3485 foreach (SwitchLabel sl in ss.Labels)
3487 ig.MarkLabel (sl.GetILLabel (ec));
3488 ig.MarkLabel (sl.GetILLabelCode (ec));
3489 if (sl.Converted == SwitchLabel.NullStringCase)
3490 ig.MarkLabel (null_target);
3491 else if (sl.Label == null) {
3492 ig.MarkLabel (lbl_default);
3493 found_default = true;
3495 ig.MarkLabel (null_target);
3501 if (!found_default) {
3502 ig.MarkLabel (lbl_default);
3503 if (HaveUnwrap && !found_null) {
3504 ig.MarkLabel (null_target);
3508 ig.MarkLabel (lbl_end);
3511 // This simple emit switch works, but does not take advantage of the
3513 // TODO: remove non-string logic from here
3514 // TODO: binary search strings?
3516 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3518 ILGenerator ig = ec.ig;
3519 Label end_of_switch = ig.DefineLabel ();
3520 Label next_test = ig.DefineLabel ();
3521 bool first_test = true;
3522 bool pending_goto_end = false;
3523 bool null_marked = false;
3525 int section_count = Sections.Count;
3527 // TODO: implement switch optimization for string by using Hashtable
3528 //if (SwitchType == TypeManager.string_type && section_count > 7)
3529 // Console.WriteLine ("Switch optimization possible " + loc);
3531 ig.Emit (OpCodes.Ldloc, val);
3533 if (Elements.Contains (SwitchLabel.NullStringCase)){
3534 ig.Emit (OpCodes.Brfalse, null_target);
3536 ig.Emit (OpCodes.Brfalse, default_target);
3538 ig.Emit (OpCodes.Ldloc, val);
3539 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3540 ig.Emit (OpCodes.Stloc, val);
3542 for (int section = 0; section < section_count; section++){
3543 SwitchSection ss = (SwitchSection) Sections [section];
3545 if (ss == default_section)
3548 Label sec_begin = ig.DefineLabel ();
3550 ig.Emit (OpCodes.Nop);
3552 if (pending_goto_end)
3553 ig.Emit (OpCodes.Br, end_of_switch);
3555 int label_count = ss.Labels.Count;
3557 for (int label = 0; label < label_count; label++){
3558 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3559 ig.MarkLabel (sl.GetILLabel (ec));
3562 ig.MarkLabel (next_test);
3563 next_test = ig.DefineLabel ();
3566 // If we are the default target
3568 if (sl.Label != null){
3569 object lit = sl.Converted;
3571 if (lit == SwitchLabel.NullStringCase){
3573 if (label + 1 == label_count)
3574 ig.Emit (OpCodes.Br, next_test);
3578 ig.Emit (OpCodes.Ldloc, val);
3579 ig.Emit (OpCodes.Ldstr, (string)lit);
3580 if (label_count == 1)
3581 ig.Emit (OpCodes.Bne_Un, next_test);
3583 if (label+1 == label_count)
3584 ig.Emit (OpCodes.Bne_Un, next_test);
3586 ig.Emit (OpCodes.Beq, sec_begin);
3591 ig.MarkLabel (null_target);
3594 ig.MarkLabel (sec_begin);
3595 foreach (SwitchLabel sl in ss.Labels)
3596 ig.MarkLabel (sl.GetILLabelCode (ec));
3599 pending_goto_end = !ss.Block.HasRet;
3602 ig.MarkLabel (next_test);
3603 ig.MarkLabel (default_target);
3605 ig.MarkLabel (null_target);
3606 if (default_section != null)
3607 default_section.Block.Emit (ec);
3608 ig.MarkLabel (end_of_switch);
3611 SwitchSection FindSection (SwitchLabel label)
3613 foreach (SwitchSection ss in Sections){
3614 foreach (SwitchLabel sl in ss.Labels){
3623 public override bool Resolve (EmitContext ec)
3625 Expr = Expr.Resolve (ec);
3629 new_expr = SwitchGoverningType (ec, Expr);
3632 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3633 unwrap = Nullable.Unwrap.Create (Expr, ec);
3637 new_expr = SwitchGoverningType (ec, unwrap);
3641 if (new_expr == null){
3642 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3647 SwitchType = new_expr.Type;
3649 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3650 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3654 if (!CheckSwitch (ec))
3658 Elements.Remove (SwitchLabel.NullStringCase);
3660 Switch old_switch = ec.Switch;
3662 ec.Switch.SwitchType = SwitchType;
3664 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3665 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3667 is_constant = new_expr is Constant;
3669 object key = ((Constant) new_expr).GetValue ();
3670 SwitchLabel label = (SwitchLabel) Elements [key];
3672 constant_section = FindSection (label);
3673 if (constant_section == null)
3674 constant_section = default_section;
3679 foreach (SwitchSection ss in Sections){
3681 ec.CurrentBranching.CreateSibling (
3682 null, FlowBranching.SiblingType.SwitchSection);
3686 if (is_constant && (ss != constant_section)) {
3687 // If we're a constant switch, we're only emitting
3688 // one single section - mark all the others as
3690 ec.CurrentBranching.CurrentUsageVector.Goto ();
3691 if (!ss.Block.ResolveUnreachable (ec, true)) {
3695 if (!ss.Block.Resolve (ec))
3700 if (default_section == null)
3701 ec.CurrentBranching.CreateSibling (
3702 null, FlowBranching.SiblingType.SwitchSection);
3704 ec.EndFlowBranching ();
3705 ec.Switch = old_switch;
3707 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3709 if (TypeManager.string_isinterned_string == null) {
3710 TypeManager.string_isinterned_string = TypeManager.GetPredefinedMethod (TypeManager.string_type,
3711 "IsInterned", loc, TypeManager.string_type);
3717 protected override void DoEmit (EmitContext ec)
3719 ILGenerator ig = ec.ig;
3721 default_target = ig.DefineLabel ();
3722 null_target = ig.DefineLabel ();
3724 // Store variable for comparission purposes
3727 value = ig.DeclareLocal (SwitchType);
3729 unwrap.EmitCheck (ec);
3730 ig.Emit (OpCodes.Brfalse, null_target);
3732 ig.Emit (OpCodes.Stloc, value);
3734 } else if (!is_constant) {
3735 value = ig.DeclareLocal (SwitchType);
3737 ig.Emit (OpCodes.Stloc, value);
3742 // Setup the codegen context
3744 Label old_end = ec.LoopEnd;
3745 Switch old_switch = ec.Switch;
3747 ec.LoopEnd = ig.DefineLabel ();
3752 if (constant_section != null)
3753 constant_section.Block.Emit (ec);
3754 } else if (SwitchType == TypeManager.string_type)
3755 SimpleSwitchEmit (ec, value);
3757 TableSwitchEmit (ec, value);
3759 // Restore context state.
3760 ig.MarkLabel (ec.LoopEnd);
3763 // Restore the previous context
3765 ec.LoopEnd = old_end;
3766 ec.Switch = old_switch;
3769 protected override void CloneTo (CloneContext clonectx, Statement t)
3771 Switch target = (Switch) t;
3773 target.Expr = Expr.Clone (clonectx);
3774 target.Sections = new ArrayList ();
3775 foreach (SwitchSection ss in Sections){
3776 target.Sections.Add (ss.Clone (clonectx));
3781 public abstract class ExceptionStatement : Statement
3783 public abstract void EmitFinally (EmitContext ec);
3785 protected bool emit_finally = true;
3786 ArrayList parent_vectors;
3788 protected void DoEmitFinally (EmitContext ec)
3791 ec.ig.BeginFinallyBlock ();
3792 else if (ec.InIterator)
3793 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3797 protected void ResolveFinally (FlowBranchingException branching)
3799 emit_finally = branching.EmitFinally;
3801 branching.Parent.StealFinallyClauses (ref parent_vectors);
3805 public class Lock : ExceptionStatement {
3807 public Statement Statement;
3808 TemporaryVariable temp;
3810 public Lock (Expression expr, Statement stmt, Location l)
3817 public override bool Resolve (EmitContext ec)
3819 expr = expr.Resolve (ec);
3823 if (expr.Type.IsValueType){
3824 Report.Error (185, loc,
3825 "`{0}' is not a reference type as required by the lock statement",
3826 TypeManager.CSharpName (expr.Type));
3830 FlowBranchingException branching = ec.StartFlowBranching (this);
3831 bool ok = Statement.Resolve (ec);
3833 ResolveFinally (branching);
3835 ec.EndFlowBranching ();
3837 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
3838 // So, ensure there's some IL code after the finally block.
3839 ec.NeedReturnLabel ();
3841 // Avoid creating libraries that reference the internal
3844 if (t == TypeManager.null_type)
3845 t = TypeManager.object_type;
3847 temp = new TemporaryVariable (t, loc);
3850 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
3851 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
3852 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
3853 monitor_type, "Enter", loc, TypeManager.object_type);
3854 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
3855 monitor_type, "Exit", loc, TypeManager.object_type);
3861 protected override void DoEmit (EmitContext ec)
3863 ILGenerator ig = ec.ig;
3865 temp.Store (ec, expr);
3867 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3871 ig.BeginExceptionBlock ();
3872 Statement.Emit (ec);
3877 ig.EndExceptionBlock ();
3880 public override void EmitFinally (EmitContext ec)
3883 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3886 protected override void CloneTo (CloneContext clonectx, Statement t)
3888 Lock target = (Lock) t;
3890 target.expr = expr.Clone (clonectx);
3891 target.Statement = Statement.Clone (clonectx);
3895 public class Unchecked : Statement {
3898 public Unchecked (Block b)
3904 public override bool Resolve (EmitContext ec)
3906 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3907 return Block.Resolve (ec);
3910 protected override void DoEmit (EmitContext ec)
3912 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3916 protected override void CloneTo (CloneContext clonectx, Statement t)
3918 Unchecked target = (Unchecked) t;
3920 target.Block = clonectx.LookupBlock (Block);
3924 public class Checked : Statement {
3927 public Checked (Block b)
3930 b.Unchecked = false;
3933 public override bool Resolve (EmitContext ec)
3935 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3936 return Block.Resolve (ec);
3939 protected override void DoEmit (EmitContext ec)
3941 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3945 protected override void CloneTo (CloneContext clonectx, Statement t)
3947 Checked target = (Checked) t;
3949 target.Block = clonectx.LookupBlock (Block);
3953 public class Unsafe : Statement {
3956 public Unsafe (Block b)
3959 Block.Unsafe = true;
3962 public override bool Resolve (EmitContext ec)
3964 using (ec.With (EmitContext.Flags.InUnsafe, true))
3965 return Block.Resolve (ec);
3968 protected override void DoEmit (EmitContext ec)
3970 using (ec.With (EmitContext.Flags.InUnsafe, true))
3973 protected override void CloneTo (CloneContext clonectx, Statement t)
3975 Unsafe target = (Unsafe) t;
3977 target.Block = clonectx.LookupBlock (Block);
3984 public class Fixed : Statement {
3986 ArrayList declarators;
3987 Statement statement;
3992 abstract class Emitter
3994 protected LocalInfo vi;
3995 protected Expression converted;
3997 protected Emitter (Expression expr, LocalInfo li)
4003 public abstract void Emit (EmitContext ec);
4004 public abstract void EmitExit (EmitContext ec);
4007 class ExpressionEmitter : Emitter {
4008 public ExpressionEmitter (Expression converted, LocalInfo li) :
4009 base (converted, li)
4013 public override void Emit (EmitContext ec) {
4015 // Store pointer in pinned location
4017 converted.Emit (ec);
4018 vi.Variable.EmitAssign (ec);
4021 public override void EmitExit (EmitContext ec)
4023 ec.ig.Emit (OpCodes.Ldc_I4_0);
4024 ec.ig.Emit (OpCodes.Conv_U);
4025 vi.Variable.EmitAssign (ec);
4029 class StringEmitter : Emitter {
4030 LocalBuilder pinned_string;
4033 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4039 public override void Emit (EmitContext ec)
4041 ILGenerator ig = ec.ig;
4042 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4044 converted.Emit (ec);
4045 ig.Emit (OpCodes.Stloc, pinned_string);
4047 Expression sptr = new StringPtr (pinned_string, loc);
4048 converted = Convert.ImplicitConversionRequired (
4049 ec, sptr, vi.VariableType, loc);
4051 if (converted == null)
4054 converted.Emit (ec);
4055 vi.Variable.EmitAssign (ec);
4058 public override void EmitExit (EmitContext ec)
4060 ec.ig.Emit (OpCodes.Ldnull);
4061 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4065 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4068 declarators = decls;
4073 public Statement Statement {
4074 get { return statement; }
4077 public override bool Resolve (EmitContext ec)
4080 Expression.UnsafeError (loc);
4084 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4085 if (texpr == null) {
4086 if (type is VarExpr)
4087 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4092 expr_type = texpr.Type;
4094 data = new Emitter [declarators.Count];
4096 if (!expr_type.IsPointer){
4097 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4102 foreach (Pair p in declarators){
4103 LocalInfo vi = (LocalInfo) p.First;
4104 Expression e = (Expression) p.Second;
4106 vi.VariableInfo.SetAssigned (ec);
4107 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4110 // The rules for the possible declarators are pretty wise,
4111 // but the production on the grammar is more concise.
4113 // So we have to enforce these rules here.
4115 // We do not resolve before doing the case 1 test,
4116 // because the grammar is explicit in that the token &
4117 // is present, so we need to test for this particular case.
4121 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4126 // Case 1: & object.
4128 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4129 Expression child = ((Unary) e).Expr;
4131 if (child is ParameterReference || child is LocalVariableReference){
4134 "No need to use fixed statement for parameters or " +
4135 "local variable declarations (address is already " +
4140 ec.InFixedInitializer = true;
4142 ec.InFixedInitializer = false;
4146 child = ((Unary) e).Expr;
4148 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4151 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4152 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4156 data [i] = new ExpressionEmitter (e, vi);
4162 ec.InFixedInitializer = true;
4164 ec.InFixedInitializer = false;
4171 if (e.Type.IsArray){
4172 Type array_type = TypeManager.GetElementType (e.Type);
4175 // Provided that array_type is unmanaged,
4177 if (!TypeManager.VerifyUnManaged (array_type, loc))
4181 // and T* is implicitly convertible to the
4182 // pointer type given in the fixed statement.
4184 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4186 Expression converted = Convert.ImplicitConversionRequired (
4187 ec, array_ptr, vi.VariableType, loc);
4188 if (converted == null)
4192 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4194 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4195 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4196 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4200 converted = converted.Resolve (ec);
4202 data [i] = new ExpressionEmitter (converted, vi);
4211 if (e.Type == TypeManager.string_type){
4212 data [i] = new StringEmitter (e, vi, loc);
4217 // Case 4: fixed buffer
4218 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4219 if (fixed_buffer_ptr != null) {
4220 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4225 // For other cases, flag a `this is already fixed expression'
4227 if (e is LocalVariableReference || e is ParameterReference ||
4228 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4230 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4234 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4238 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4239 bool ok = statement.Resolve (ec);
4240 bool flow_unreachable = ec.EndFlowBranching ();
4241 has_ret = flow_unreachable;
4246 protected override void DoEmit (EmitContext ec)
4248 for (int i = 0; i < data.Length; i++) {
4252 statement.Emit (ec);
4258 // Clear the pinned variable
4260 for (int i = 0; i < data.Length; i++) {
4261 data [i].EmitExit (ec);
4265 protected override void CloneTo (CloneContext clonectx, Statement t)
4267 Fixed target = (Fixed) t;
4269 target.type = type.Clone (clonectx);
4270 target.declarators = new ArrayList (declarators.Count);
4271 foreach (Pair p in declarators) {
4272 LocalInfo vi = (LocalInfo) p.First;
4273 Expression e = (Expression) p.Second;
4275 target.declarators.Add (
4276 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4279 target.statement = statement.Clone (clonectx);
4283 public class Catch : Statement {
4284 public readonly string Name;
4286 public Block VarBlock;
4288 Expression type_expr;
4291 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4296 VarBlock = var_block;
4300 public Type CatchType {
4306 public bool IsGeneral {
4308 return type_expr == null;
4312 protected override void DoEmit(EmitContext ec)
4314 ILGenerator ig = ec.ig;
4316 if (CatchType != null)
4317 ig.BeginCatchBlock (CatchType);
4319 ig.BeginCatchBlock (TypeManager.object_type);
4321 if (VarBlock != null)
4325 LocalInfo vi = Block.GetLocalInfo (Name);
4327 throw new Exception ("Variable does not exist in this block");
4329 if (vi.Variable.NeedsTemporary) {
4330 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4331 ig.Emit (OpCodes.Stloc, e);
4333 vi.Variable.EmitInstance (ec);
4334 ig.Emit (OpCodes.Ldloc, e);
4335 vi.Variable.EmitAssign (ec);
4337 vi.Variable.EmitAssign (ec);
4339 ig.Emit (OpCodes.Pop);
4344 public override bool Resolve (EmitContext ec)
4346 using (ec.With (EmitContext.Flags.InCatch, true)) {
4347 if (type_expr != null) {
4348 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4354 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4355 Error (155, "The type caught or thrown must be derived from System.Exception");
4361 if (!Block.Resolve (ec))
4364 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4365 // emit the "unused variable" warnings.
4366 if (VarBlock != null)
4367 return VarBlock.Resolve (ec);
4373 protected override void CloneTo (CloneContext clonectx, Statement t)
4375 Catch target = (Catch) t;
4377 if (type_expr != null)
4378 target.type_expr = type_expr.Clone (clonectx);
4379 if (VarBlock != null)
4380 target.VarBlock = clonectx.LookupBlock (VarBlock);
4381 target.Block = clonectx.LookupBlock (Block);
4385 public class Try : ExceptionStatement {
4386 public Block Fini, Block;
4387 public ArrayList Specific;
4388 public Catch General;
4390 bool need_exc_block;
4393 // specific, general and fini might all be null.
4395 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4397 if (specific == null && general == null){
4398 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4402 this.Specific = specific;
4403 this.General = general;
4408 public override bool Resolve (EmitContext ec)
4412 FlowBranchingException branching = ec.StartFlowBranching (this);
4414 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4416 if (!Block.Resolve (ec))
4419 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4421 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4423 Type[] prev_catches = new Type [Specific.Count];
4425 foreach (Catch c in Specific){
4426 ec.CurrentBranching.CreateSibling (
4427 c.Block, FlowBranching.SiblingType.Catch);
4429 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4431 if (c.Name != null) {
4432 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4434 throw new Exception ();
4436 vi.VariableInfo = null;
4439 if (!c.Resolve (ec))
4442 Type resolved_type = c.CatchType;
4443 for (int ii = 0; ii < last_index; ++ii) {
4444 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4445 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4450 prev_catches [last_index++] = resolved_type;
4451 need_exc_block = true;
4454 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4456 if (General != null){
4457 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4458 foreach (Catch c in Specific){
4459 if (c.CatchType == TypeManager.exception_type) {
4460 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'");
4465 ec.CurrentBranching.CreateSibling (
4466 General.Block, FlowBranching.SiblingType.Catch);
4468 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4470 if (!General.Resolve (ec))
4473 need_exc_block = true;
4476 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4480 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4482 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4483 using (ec.With (EmitContext.Flags.InFinally, true)) {
4484 if (!Fini.Resolve (ec))
4489 need_exc_block = true;
4492 if (ec.InIterator) {
4493 ResolveFinally (branching);
4494 need_exc_block |= emit_finally;
4496 emit_finally = Fini != null;
4498 ec.EndFlowBranching ();
4500 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4501 // So, ensure there's some IL code after the finally block.
4502 ec.NeedReturnLabel ();
4504 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4506 Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
4511 protected override void DoEmit (EmitContext ec)
4513 ILGenerator ig = ec.ig;
4516 ig.BeginExceptionBlock ();
4519 foreach (Catch c in Specific)
4522 if (General != null)
4527 ig.EndExceptionBlock ();
4530 public override void EmitFinally (EmitContext ec)
4536 public bool HasCatch
4539 return General != null || Specific.Count > 0;
4543 protected override void CloneTo (CloneContext clonectx, Statement t)
4545 Try target = (Try) t;
4547 target.Block = clonectx.LookupBlock (Block);
4549 target.Fini = clonectx.LookupBlock (Fini);
4550 if (General != null)
4551 target.General = (Catch) General.Clone (clonectx);
4552 if (Specific != null){
4553 target.Specific = new ArrayList ();
4554 foreach (Catch c in Specific)
4555 target.Specific.Add (c.Clone (clonectx));
4560 public class Using : ExceptionStatement {
4561 object expression_or_block;
4562 public Statement Statement;
4566 Expression [] resolved_vars;
4567 Expression [] converted_vars;
4568 Expression [] assign;
4569 TemporaryVariable local_copy;
4571 public Using (object expression_or_block, Statement stmt, Location l)
4573 this.expression_or_block = expression_or_block;
4579 // Resolves for the case of using using a local variable declaration.
4581 bool ResolveLocalVariableDecls (EmitContext ec)
4583 resolved_vars = new Expression[var_list.Count];
4584 assign = new Expression [var_list.Count];
4585 converted_vars = new Expression[var_list.Count];
4587 for (int i = 0; i < assign.Length; ++i) {
4588 DictionaryEntry e = (DictionaryEntry) var_list [i];
4589 Expression var = (Expression) e.Key;
4590 Expression new_expr = (Expression) e.Value;
4592 Expression a = new Assign (var, new_expr, loc);
4597 resolved_vars [i] = var;
4600 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4601 converted_vars [i] = var;
4605 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4607 Error_IsNotConvertibleToIDisposable (var);
4611 converted_vars [i] = a;
4617 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4619 Report.SymbolRelatedToPreviousError (expr.Type);
4620 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4621 expr.GetSignatureForError ());
4624 bool ResolveExpression (EmitContext ec)
4626 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4627 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4628 Error_IsNotConvertibleToIDisposable (expr);
4633 local_copy = new TemporaryVariable (expr_type, loc);
4634 local_copy.Resolve (ec);
4640 // Emits the code for the case of using using a local variable declaration.
4642 void EmitLocalVariableDecls (EmitContext ec)
4644 ILGenerator ig = ec.ig;
4647 for (i = 0; i < assign.Length; i++) {
4648 ExpressionStatement es = assign [i] as ExpressionStatement;
4651 es.EmitStatement (ec);
4653 assign [i].Emit (ec);
4654 ig.Emit (OpCodes.Pop);
4658 ig.BeginExceptionBlock ();
4660 Statement.Emit (ec);
4662 var_list.Reverse ();
4667 void EmitLocalVariableDeclFinally (EmitContext ec)
4669 ILGenerator ig = ec.ig;
4671 int i = assign.Length;
4672 for (int ii = 0; ii < var_list.Count; ++ii){
4673 Expression var = resolved_vars [--i];
4674 Label skip = ig.DefineLabel ();
4677 ig.BeginFinallyBlock ();
4679 if (!var.Type.IsValueType) {
4681 ig.Emit (OpCodes.Brfalse, skip);
4682 converted_vars [i].Emit (ec);
4683 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4685 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4687 if (!(ml is MethodGroupExpr)) {
4689 ig.Emit (OpCodes.Box, var.Type);
4690 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4692 MethodInfo mi = null;
4694 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4695 if (TypeManager.GetParameterData (mk).Count == 0) {
4702 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4706 IMemoryLocation mloc = (IMemoryLocation) var;
4708 mloc.AddressOf (ec, AddressOp.Load);
4709 ig.Emit (OpCodes.Call, mi);
4713 ig.MarkLabel (skip);
4716 ig.EndExceptionBlock ();
4718 ig.BeginFinallyBlock ();
4723 void EmitExpression (EmitContext ec)
4726 // Make a copy of the expression and operate on that.
4728 ILGenerator ig = ec.ig;
4730 local_copy.Store (ec, expr);
4733 ig.BeginExceptionBlock ();
4735 Statement.Emit (ec);
4739 ig.EndExceptionBlock ();
4742 void EmitExpressionFinally (EmitContext ec)
4744 ILGenerator ig = ec.ig;
4745 if (!expr_type.IsValueType) {
4746 Label skip = ig.DefineLabel ();
4747 local_copy.Emit (ec);
4748 ig.Emit (OpCodes.Brfalse, skip);
4749 local_copy.Emit (ec);
4750 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4751 ig.MarkLabel (skip);
4753 Expression ml = Expression.MemberLookup (
4754 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4755 "Dispose", Location.Null);
4757 if (!(ml is MethodGroupExpr)) {
4758 local_copy.Emit (ec);
4759 ig.Emit (OpCodes.Box, expr_type);
4760 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4762 MethodInfo mi = null;
4764 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4765 if (TypeManager.GetParameterData (mk).Count == 0) {
4772 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4776 local_copy.AddressOf (ec, AddressOp.Load);
4777 ig.Emit (OpCodes.Call, mi);
4782 public override bool Resolve (EmitContext ec)
4784 if (expression_or_block is DictionaryEntry){
4785 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4786 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4788 if (!ResolveLocalVariableDecls (ec))
4791 } else if (expression_or_block is Expression){
4792 expr = (Expression) expression_or_block;
4794 expr = expr.Resolve (ec);
4798 expr_type = expr.Type;
4800 if (!ResolveExpression (ec))
4804 FlowBranchingException branching = ec.StartFlowBranching (this);
4806 bool ok = Statement.Resolve (ec);
4808 ResolveFinally (branching);
4810 ec.EndFlowBranching ();
4812 // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
4813 // So, ensure there's some IL code after the finally block.
4814 ec.NeedReturnLabel ();
4816 if (TypeManager.void_dispose_void == null) {
4817 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4818 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4824 protected override void DoEmit (EmitContext ec)
4826 if (expression_or_block is DictionaryEntry)
4827 EmitLocalVariableDecls (ec);
4828 else if (expression_or_block is Expression)
4829 EmitExpression (ec);
4832 public override void EmitFinally (EmitContext ec)
4834 if (expression_or_block is DictionaryEntry)
4835 EmitLocalVariableDeclFinally (ec);
4836 else if (expression_or_block is Expression)
4837 EmitExpressionFinally (ec);
4840 protected override void CloneTo (CloneContext clonectx, Statement t)
4842 Using target = (Using) t;
4844 if (expression_or_block is Expression) {
4845 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4847 DictionaryEntry de = (DictionaryEntry) expression_or_block;
4848 ArrayList var_list = (ArrayList) de.Value;
4849 ArrayList target_var_list = new ArrayList (var_list.Count);
4851 foreach (DictionaryEntry de_variable in var_list)
4852 target_var_list.Add (new DictionaryEntry (
4853 ((Expression) de_variable.Key).Clone (clonectx),
4854 ((Expression) de_variable.Value).Clone (clonectx)));
4856 target.expression_or_block = new DictionaryEntry (
4857 ((Expression) de.Key).Clone (clonectx),
4861 target.Statement = Statement.Clone (clonectx);
4866 /// Implementation of the foreach C# statement
4868 public class Foreach : Statement {
4870 Expression variable;
4872 Statement statement;
4874 CollectionForeach collection;
4876 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4877 Statement stmt, Location l)
4880 this.variable = var;
4886 public Statement Statement {
4887 get { return statement; }
4890 public override bool Resolve (EmitContext ec)
4892 expr = expr.Resolve (ec);
4897 Report.Error (186, loc, "Use of null is not valid in this context");
4901 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4902 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4903 expr.ExprClassName);
4908 // We need an instance variable. Not sure this is the best
4909 // way of doing this.
4911 // FIXME: When we implement propertyaccess, will those turn
4912 // out to return values in ExprClass? I think they should.
4914 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4915 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4916 collection.Error_Enumerator ();
4920 if (expr.Type.IsArray) {
4921 array = new ArrayForeach (type, variable, expr, statement, loc);
4922 return array.Resolve (ec);
4925 collection = new CollectionForeach (type, variable, expr, statement, loc);
4926 return collection.Resolve (ec);
4929 protected override void DoEmit (EmitContext ec)
4931 ILGenerator ig = ec.ig;
4933 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4934 ec.LoopBegin = ig.DefineLabel ();
4935 ec.LoopEnd = ig.DefineLabel ();
4937 if (collection != null)
4938 collection.Emit (ec);
4942 ec.LoopBegin = old_begin;
4943 ec.LoopEnd = old_end;
4946 protected class ArrayCounter : TemporaryVariable
4948 public ArrayCounter (Location loc)
4949 : base (TypeManager.int32_type, loc)
4952 public void Initialize (EmitContext ec)
4955 ec.ig.Emit (OpCodes.Ldc_I4_0);
4959 public void Increment (EmitContext ec)
4963 ec.ig.Emit (OpCodes.Ldc_I4_1);
4964 ec.ig.Emit (OpCodes.Add);
4969 protected class ArrayForeach : Statement
4971 Expression variable, expr, conv;
4972 Statement statement;
4974 Expression var_type;
4975 TemporaryVariable[] lengths;
4976 ArrayCounter[] counter;
4979 TemporaryVariable copy;
4982 public ArrayForeach (Expression var_type, Expression var,
4983 Expression expr, Statement stmt, Location l)
4985 this.var_type = var_type;
4986 this.variable = var;
4992 public override bool Resolve (EmitContext ec)
4994 array_type = expr.Type;
4995 rank = array_type.GetArrayRank ();
4997 copy = new TemporaryVariable (array_type, loc);
5000 counter = new ArrayCounter [rank];
5001 lengths = new TemporaryVariable [rank];
5003 ArrayList list = new ArrayList ();
5004 for (int i = 0; i < rank; i++) {
5005 counter [i] = new ArrayCounter (loc);
5006 counter [i].Resolve (ec);
5008 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5009 lengths [i].Resolve (ec);
5011 list.Add (counter [i]);
5014 access = new ElementAccess (copy, list).Resolve (ec);
5018 VarExpr ve = var_type as VarExpr;
5020 // Infer implicitly typed local variable from foreach array type
5021 var_type = new TypeExpression (access.Type, ve.Location);
5024 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5025 if (var_type == null)
5028 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5034 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5035 ec.CurrentBranching.CreateSibling ();
5037 variable = variable.ResolveLValue (ec, conv, loc);
5038 if (variable == null)
5041 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5042 if (!statement.Resolve (ec))
5044 ec.EndFlowBranching ();
5046 // There's no direct control flow from the end of the embedded statement to the end of the loop
5047 ec.CurrentBranching.CurrentUsageVector.Goto ();
5049 ec.EndFlowBranching ();
5054 protected override void DoEmit (EmitContext ec)
5056 ILGenerator ig = ec.ig;
5058 copy.Store (ec, expr);
5060 Label[] test = new Label [rank];
5061 Label[] loop = new Label [rank];
5063 for (int i = 0; i < rank; i++) {
5064 test [i] = ig.DefineLabel ();
5065 loop [i] = ig.DefineLabel ();
5067 lengths [i].EmitThis (ec);
5068 ((ArrayAccess) access).EmitGetLength (ec, i);
5069 lengths [i].EmitStore (ec);
5072 for (int i = 0; i < rank; i++) {
5073 counter [i].Initialize (ec);
5075 ig.Emit (OpCodes.Br, test [i]);
5076 ig.MarkLabel (loop [i]);
5079 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5081 statement.Emit (ec);
5083 ig.MarkLabel (ec.LoopBegin);
5085 for (int i = rank - 1; i >= 0; i--){
5086 counter [i].Increment (ec);
5088 ig.MarkLabel (test [i]);
5089 counter [i].Emit (ec);
5090 lengths [i].Emit (ec);
5091 ig.Emit (OpCodes.Blt, loop [i]);
5094 ig.MarkLabel (ec.LoopEnd);
5098 protected class CollectionForeach : ExceptionStatement
5100 Expression variable, expr;
5101 Statement statement;
5103 TemporaryVariable enumerator;
5107 MethodGroupExpr get_enumerator;
5108 PropertyExpr get_current;
5109 MethodInfo move_next;
5110 Expression var_type;
5111 Type enumerator_type;
5113 bool enumerator_found;
5115 public CollectionForeach (Expression var_type, Expression var,
5116 Expression expr, Statement stmt, Location l)
5118 this.var_type = var_type;
5119 this.variable = var;
5125 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5127 Type return_type = mi.ReturnType;
5129 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5131 // Apply the same optimization as MS: skip the GetEnumerator
5132 // returning an IEnumerator, and use the one returning a
5133 // CharEnumerator instead. This allows us to avoid the
5134 // try-finally block and the boxing.
5139 // Ok, we can access it, now make sure that we can do something
5140 // with this `GetEnumerator'
5143 if (return_type == TypeManager.ienumerator_type ||
5144 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5145 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5147 // If it is not an interface, lets try to find the methods ourselves.
5148 // For example, if we have:
5149 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5150 // We can avoid the iface call. This is a runtime perf boost.
5151 // even bigger if we have a ValueType, because we avoid the cost
5154 // We have to make sure that both methods exist for us to take
5155 // this path. If one of the methods does not exist, we will just
5156 // use the interface. Sadly, this complex if statement is the only
5157 // way I could do this without a goto
5160 if (TypeManager.bool_movenext_void == null) {
5161 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5162 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5165 if (TypeManager.ienumerator_getcurrent == null) {
5166 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5167 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5172 // Prefer a generic enumerator over a non-generic one.
5174 if (return_type.IsInterface && return_type.IsGenericType) {
5175 enumerator_type = return_type;
5176 if (!FetchGetCurrent (ec, return_type))
5177 get_current = new PropertyExpr (
5178 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5179 if (!FetchMoveNext (return_type))
5180 move_next = TypeManager.bool_movenext_void;
5185 if (return_type.IsInterface ||
5186 !FetchMoveNext (return_type) ||
5187 !FetchGetCurrent (ec, return_type)) {
5188 enumerator_type = return_type;
5189 move_next = TypeManager.bool_movenext_void;
5190 get_current = new PropertyExpr (
5191 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5196 // Ok, so they dont return an IEnumerable, we will have to
5197 // find if they support the GetEnumerator pattern.
5200 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5201 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",
5202 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5207 enumerator_type = return_type;
5208 is_disposable = !enumerator_type.IsSealed ||
5209 TypeManager.ImplementsInterface (
5210 enumerator_type, TypeManager.idisposable_type);
5216 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5218 bool FetchMoveNext (Type t)
5220 MemberList move_next_list;
5222 move_next_list = TypeContainer.FindMembers (
5223 t, MemberTypes.Method,
5224 BindingFlags.Public | BindingFlags.Instance,
5225 Type.FilterName, "MoveNext");
5226 if (move_next_list.Count == 0)
5229 foreach (MemberInfo m in move_next_list){
5230 MethodInfo mi = (MethodInfo) m;
5232 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5233 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5243 // Retrieves a `public T get_Current ()' method from the Type `t'
5245 bool FetchGetCurrent (EmitContext ec, Type t)
5247 PropertyExpr pe = Expression.MemberLookup (
5248 ec.ContainerType, t, "Current", MemberTypes.Property,
5249 Expression.AllBindingFlags, loc) as PropertyExpr;
5258 // Retrieves a `public void Dispose ()' method from the Type `t'
5260 static MethodInfo FetchMethodDispose (Type t)
5262 MemberList dispose_list;
5264 dispose_list = TypeContainer.FindMembers (
5265 t, MemberTypes.Method,
5266 BindingFlags.Public | BindingFlags.Instance,
5267 Type.FilterName, "Dispose");
5268 if (dispose_list.Count == 0)
5271 foreach (MemberInfo m in dispose_list){
5272 MethodInfo mi = (MethodInfo) m;
5274 if (TypeManager.GetParameterData (mi).Count == 0){
5275 if (mi.ReturnType == TypeManager.void_type)
5282 public void Error_Enumerator ()
5284 if (enumerator_found) {
5288 Report.Error (1579, loc,
5289 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5290 TypeManager.CSharpName (expr.Type));
5293 bool IsOverride (MethodInfo m)
5295 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5297 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5299 if (m is MethodBuilder)
5302 MethodInfo base_method = m.GetBaseDefinition ();
5303 return base_method != m;
5306 bool TryType (EmitContext ec, Type t)
5308 MethodGroupExpr mg = Expression.MemberLookup (
5309 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5310 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5314 MethodInfo result = null;
5315 MethodInfo tmp_move_next = null;
5316 PropertyExpr tmp_get_cur = null;
5317 Type tmp_enumerator_type = enumerator_type;
5318 foreach (MethodInfo mi in mg.Methods) {
5319 if (TypeManager.GetParameterData (mi).Count != 0)
5322 // Check whether GetEnumerator is public
5323 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5326 if (IsOverride (mi))
5329 enumerator_found = true;
5331 if (!GetEnumeratorFilter (ec, mi))
5334 if (result != null) {
5335 if (TypeManager.IsGenericType (result.ReturnType)) {
5336 if (!TypeManager.IsGenericType (mi.ReturnType))
5339 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5340 Report.SymbolRelatedToPreviousError (t);
5341 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5342 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5343 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5347 // Always prefer generics enumerators
5348 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5349 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5350 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5353 Report.SymbolRelatedToPreviousError (result);
5354 Report.SymbolRelatedToPreviousError (mi);
5355 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5356 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5361 tmp_move_next = move_next;
5362 tmp_get_cur = get_current;
5363 tmp_enumerator_type = enumerator_type;
5364 if (mi.DeclaringType == t)
5368 if (result != null) {
5369 move_next = tmp_move_next;
5370 get_current = tmp_get_cur;
5371 enumerator_type = tmp_enumerator_type;
5372 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5373 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5375 if (t != expr.Type) {
5376 expr = Convert.ExplicitConversion (
5379 throw new InternalErrorException ();
5382 get_enumerator.InstanceExpression = expr;
5383 get_enumerator.IsBase = t != expr.Type;
5391 bool ProbeCollectionType (EmitContext ec, Type t)
5393 int errors = Report.Errors;
5394 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5395 if (TryType (ec, tt))
5400 if (Report.Errors > errors)
5404 // Now try to find the method in the interfaces
5406 Type [] ifaces = TypeManager.GetInterfaces (t);
5407 foreach (Type i in ifaces){
5408 if (TryType (ec, i))
5415 public override bool Resolve (EmitContext ec)
5417 enumerator_type = TypeManager.ienumerator_type;
5418 is_disposable = true;
5420 if (!ProbeCollectionType (ec, expr.Type)) {
5421 Error_Enumerator ();
5425 VarExpr ve = var_type as VarExpr;
5427 // Infer implicitly typed local variable from foreach enumerable type
5428 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5431 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5432 if (var_type == null)
5435 enumerator = new TemporaryVariable (enumerator_type, loc);
5436 enumerator.Resolve (ec);
5438 init = new Invocation (get_enumerator, null);
5439 init = init.Resolve (ec);
5443 Expression move_next_expr;
5445 MemberInfo[] mi = new MemberInfo[] { move_next };
5446 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5447 mg.InstanceExpression = enumerator;
5449 move_next_expr = new Invocation (mg, null);
5452 get_current.InstanceExpression = enumerator;
5454 Statement block = new CollectionForeachStatement (
5455 var_type.Type, variable, get_current, statement, loc);
5457 loop = new While (move_next_expr, block, loc);
5461 FlowBranchingException branching = null;
5463 branching = ec.StartFlowBranching (this);
5465 if (!loop.Resolve (ec))
5468 if (is_disposable) {
5469 ResolveFinally (branching);
5470 ec.EndFlowBranching ();
5472 if (TypeManager.void_dispose_void == null) {
5473 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5474 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5477 emit_finally = true;
5482 protected override void DoEmit (EmitContext ec)
5484 ILGenerator ig = ec.ig;
5486 enumerator.Store (ec, init);
5489 // Protect the code in a try/finalize block, so that
5490 // if the beast implement IDisposable, we get rid of it
5492 if (is_disposable && emit_finally)
5493 ig.BeginExceptionBlock ();
5498 // Now the finally block
5500 if (is_disposable) {
5503 ig.EndExceptionBlock ();
5508 public override void EmitFinally (EmitContext ec)
5510 ILGenerator ig = ec.ig;
5512 if (enumerator_type.IsValueType) {
5513 MethodInfo mi = FetchMethodDispose (enumerator_type);
5515 enumerator.EmitLoadAddress (ec);
5516 ig.Emit (OpCodes.Call, mi);
5518 enumerator.Emit (ec);
5519 ig.Emit (OpCodes.Box, enumerator_type);
5520 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5523 Label call_dispose = ig.DefineLabel ();
5525 enumerator.Emit (ec);
5526 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5527 ig.Emit (OpCodes.Dup);
5528 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5529 ig.Emit (OpCodes.Pop);
5531 Label end_finally = ig.DefineLabel ();
5532 ig.Emit (OpCodes.Br, end_finally);
5534 ig.MarkLabel (call_dispose);
5535 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5536 ig.MarkLabel (end_finally);
5541 protected class CollectionForeachStatement : Statement
5544 Expression variable, current, conv;
5545 Statement statement;
5548 public CollectionForeachStatement (Type type, Expression variable,
5549 Expression current, Statement statement,
5553 this.variable = variable;
5554 this.current = current;
5555 this.statement = statement;
5559 public override bool Resolve (EmitContext ec)
5561 current = current.Resolve (ec);
5562 if (current == null)
5565 conv = Convert.ExplicitConversion (ec, current, type, loc);
5569 assign = new Assign (variable, conv, loc);
5570 if (assign.Resolve (ec) == null)
5573 if (!statement.Resolve (ec))
5579 protected override void DoEmit (EmitContext ec)
5581 assign.EmitStatement (ec);
5582 statement.Emit (ec);
5586 protected override void CloneTo (CloneContext clonectx, Statement t)
5588 Foreach target = (Foreach) t;
5590 target.type = type.Clone (clonectx);
5591 target.variable = variable.Clone (clonectx);
5592 target.expr = expr.Clone (clonectx);
5593 target.statement = statement.Clone (clonectx);