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 ()
2135 if (variables == null || Report.WarningLevel < 3)
2138 foreach (DictionaryEntry de in variables) {
2139 LocalInfo vi = (LocalInfo) de.Value;
2142 string name = (string) de.Key;
2144 // vi.VariableInfo can be null for 'catch' variables
2145 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2146 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2148 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2153 private void CheckPossibleMistakenEmptyStatement (Statement s)
2157 // Some statements are wrapped by a Block. Since
2158 // others' internal could be changed, here I treat
2159 // them as possibly wrapped by Block equally.
2160 Block b = s as Block;
2161 if (b != null && b.statements.Count == 1)
2162 s = (Statement) b.statements [0];
2165 body = ((Lock) s).Statement;
2167 body = ((For) s).Statement;
2168 else if (s is Foreach)
2169 body = ((Foreach) s).Statement;
2170 else if (s is While)
2171 body = ((While) s).Statement;
2172 else if (s is Using)
2173 body = ((Using) s).Statement;
2174 else if (s is Fixed)
2175 body = ((Fixed) s).Statement;
2179 if (body == null || body is EmptyStatement)
2180 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2183 public override bool Resolve (EmitContext ec)
2185 Block prev_block = ec.CurrentBlock;
2188 int errors = Report.Errors;
2190 ec.CurrentBlock = this;
2191 ec.StartFlowBranching (this);
2193 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2196 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2197 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2198 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2199 // responsible for handling the situation.
2201 int statement_count = statements.Count;
2202 for (int ix = 0; ix < statement_count; ix++){
2203 Statement s = (Statement) statements [ix];
2204 // Check possible empty statement (CS0642)
2205 if (Report.WarningLevel >= 3 &&
2206 ix + 1 < statement_count &&
2207 statements [ix + 1] is Block)
2208 CheckPossibleMistakenEmptyStatement (s);
2211 // Warn if we detect unreachable code.
2214 if (s is EmptyStatement)
2218 ((Block) s).unreachable = true;
2220 if (!unreachable_shown && !(s is LabeledStatement)) {
2221 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2222 unreachable_shown = true;
2227 // Note that we're not using ResolveUnreachable() for unreachable
2228 // statements here. ResolveUnreachable() creates a temporary
2229 // flow branching and kills it afterwards. This leads to problems
2230 // if you have two unreachable statements where the first one
2231 // assigns a variable and the second one tries to access it.
2234 if (!s.Resolve (ec)) {
2236 if (ec.IsInProbingMode)
2239 statements [ix] = EmptyStatement.Value;
2243 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2244 statements [ix] = EmptyStatement.Value;
2246 num_statements = ix + 1;
2248 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2249 if (unreachable && s is LabeledStatement)
2250 throw new InternalErrorException ("should not happen");
2253 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2254 ec.CurrentBranching, statement_count, num_statements);
2256 while (ec.CurrentBranching is FlowBranchingLabeled)
2257 ec.EndFlowBranching ();
2259 bool flow_unreachable = ec.EndFlowBranching ();
2261 ec.CurrentBlock = prev_block;
2263 if (flow_unreachable)
2264 flags |= Flags.HasRet;
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) && !flow_unreachable)
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, "This label has not been referenced");
2277 if (ok && errors == Report.Errors)
2283 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2285 unreachable_shown = true;
2289 Report.Warning (162, 2, loc, "Unreachable code detected");
2291 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2292 bool ok = Resolve (ec);
2293 ec.KillFlowBranching ();
2298 protected override void DoEmit (EmitContext ec)
2300 for (int ix = 0; ix < num_statements; ix++){
2301 Statement s = (Statement) statements [ix];
2306 public override void Emit (EmitContext ec)
2308 Block prev_block = ec.CurrentBlock;
2310 ec.CurrentBlock = this;
2312 bool emit_debug_info = SymbolWriter.HasSymbolWriter;
2313 bool is_lexical_block = (this == Explicit) && (Parent != null) &&
2314 ((flags & Flags.IsIterator) == 0);
2316 bool omit_debug_info = ec.OmitDebuggingInfo;
2318 if (emit_debug_info) {
2319 if (is_lexical_block)
2323 if ((scope_init != null) || (scope_initializers != null))
2324 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2326 if (scope_init != null) {
2327 ec.OmitDebuggingInfo = true;
2328 scope_init.EmitStatement (ec);
2329 ec.OmitDebuggingInfo = omit_debug_info;
2331 if (scope_initializers != null) {
2332 ec.OmitDebuggingInfo = true;
2333 foreach (StatementExpression s in scope_initializers)
2335 ec.OmitDebuggingInfo = omit_debug_info;
2338 if ((scope_init != null) || (scope_initializers != null))
2339 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2341 ec.Mark (StartLocation, true);
2344 if (emit_debug_info) {
2345 EmitSymbolInfo (ec);
2347 if (is_lexical_block)
2351 ec.CurrentBlock = prev_block;
2354 protected virtual void EmitSymbolInfo (EmitContext ec)
2356 if (variables != null) {
2357 foreach (DictionaryEntry de in variables) {
2358 string name = (string) de.Key;
2359 LocalInfo vi = (LocalInfo) de.Value;
2361 vi.EmitSymbolInfo (ec, name);
2366 public override string ToString ()
2368 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2371 protected override void CloneTo (CloneContext clonectx, Statement t)
2373 Block target = (Block) t;
2375 clonectx.AddBlockMap (this, target);
2377 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2378 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2380 target.Parent = clonectx.RemapBlockCopy (Parent);
2382 if (variables != null){
2383 target.variables = new Hashtable ();
2385 foreach (DictionaryEntry de in variables){
2386 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2387 target.variables [de.Key] = newlocal;
2388 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2392 target.statements = new ArrayList (statements.Count);
2393 foreach (Statement s in statements)
2394 target.statements.Add (s.Clone (clonectx));
2396 if (target.children != null){
2397 target.children = new ArrayList (children.Count);
2398 foreach (Block b in children){
2399 target.children.Add (clonectx.LookupBlock (b));
2404 // TODO: labels, switch_block, constants (?), anonymous_children
2409 public class ExplicitBlock : Block {
2410 public ExplicitBlock (Block parent, Location start, Location end)
2411 : this (parent, (Flags) 0, start, end)
2415 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2416 : base (parent, flags, start, end)
2418 this.Explicit = this;
2421 public bool IsIterator {
2422 get { return (flags & Flags.IsIterator) != 0; }
2425 HybridDictionary known_variables;
2428 // Marks a variable with name @name as being used in this or a child block.
2429 // If a variable name has been used in a child block, it's illegal to
2430 // declare a variable with the same name in the current block.
2432 internal void AddKnownVariable (string name, IKnownVariable info)
2434 if (known_variables == null)
2435 known_variables = new HybridDictionary();
2437 known_variables [name] = info;
2440 Parent.Explicit.AddKnownVariable (name, info);
2443 internal IKnownVariable GetKnownVariable (string name)
2445 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2448 protected override void CloneTo (CloneContext clonectx, Statement t)
2450 ExplicitBlock target = (ExplicitBlock) t;
2451 target.known_variables = null;
2452 base.CloneTo (clonectx, t);
2456 public class ToplevelParameterInfo : IKnownVariable {
2457 public readonly ToplevelBlock Block;
2458 public readonly int Index;
2459 public VariableInfo VariableInfo;
2461 Block IKnownVariable.Block {
2462 get { return Block; }
2464 public Parameter Parameter {
2465 get { return Block.Parameters [Index]; }
2467 public Location Location {
2468 get { return Parameter.Location; }
2471 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2479 // A toplevel block contains extra information, the split is done
2480 // only to separate information that would otherwise bloat the more
2481 // lightweight Block.
2483 // In particular, this was introduced when the support for Anonymous
2484 // Methods was implemented.
2486 public class ToplevelBlock : ExplicitBlock {
2487 GenericMethod generic;
2488 FlowBranchingToplevel top_level_branching;
2489 AnonymousContainer anonymous_container;
2490 RootScopeInfo root_scope;
2491 Parameters parameters;
2492 ToplevelParameterInfo[] parameter_info;
2494 public bool HasVarargs {
2495 get { return (flags & Flags.HasVarargs) != 0; }
2496 set { flags |= Flags.HasVarargs; }
2500 // The parameters for the block.
2502 public Parameters Parameters {
2503 get { return parameters; }
2506 public bool CompleteContexts (EmitContext ec)
2508 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
2510 if (root_scope != null)
2511 root_scope.LinkScopes ();
2513 if (Parent == null && root_scope != null) {
2514 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
2516 if (root_scope.DefineType () == null)
2518 if (!root_scope.ResolveType ())
2520 if (!root_scope.ResolveMembers ())
2522 if (!root_scope.DefineMembers ())
2529 public GenericMethod GenericMethod {
2530 get { return generic; }
2533 public ToplevelBlock Container {
2534 get { return Parent == null ? null : Parent.Toplevel; }
2537 public AnonymousContainer AnonymousContainer {
2538 get { return anonymous_container; }
2539 set { anonymous_container = value; }
2542 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2543 this (parent, (Flags) 0, parameters, start)
2547 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2548 this (parent, parameters, start)
2550 this.generic = generic;
2553 public ToplevelBlock (Parameters parameters, Location start) :
2554 this (null, (Flags) 0, parameters, start)
2558 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2559 this (null, flags, parameters, start)
2563 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2564 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2565 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2566 base (null, flags, start, Location.Null)
2568 this.Toplevel = this;
2570 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2571 this.Parent = parent;
2573 parent.AddAnonymousChild (this);
2575 if (this.parameters.Count != 0)
2576 ProcessParameters ();
2579 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2583 protected override void CloneTo (CloneContext clonectx, Statement t)
2585 ToplevelBlock target = (ToplevelBlock) t;
2586 base.CloneTo (clonectx, t);
2588 if (parameters.Count != 0)
2589 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2590 for (int i = 0; i < parameters.Count; ++i)
2591 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2594 public bool CheckError158 (string name, Location loc)
2596 if (AnonymousChildren != null) {
2597 foreach (ToplevelBlock child in AnonymousChildren) {
2598 if (!child.CheckError158 (name, loc))
2603 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2604 if (!c.DoCheckError158 (name, loc))
2611 public virtual Expression GetTransparentIdentifier (string name)
2616 void ProcessParameters ()
2618 int n = parameters.Count;
2619 parameter_info = new ToplevelParameterInfo [n];
2620 for (int i = 0; i < n; ++i) {
2621 parameter_info [i] = new ToplevelParameterInfo (this, i);
2623 Parameter p = parameters [i];
2627 string name = p.Name;
2628 LocalInfo vi = GetLocalInfo (name);
2630 Report.SymbolRelatedToPreviousError (vi.Location, name);
2631 Error_AlreadyDeclared (loc, name, "parent or current");
2635 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2637 Report.SymbolRelatedToPreviousError (pi.Location, name);
2638 Error_AlreadyDeclared (loc, name, "parent or current");
2642 AddKnownVariable (name, parameter_info [i]);
2645 // mark this block as "used" so that we create local declarations in a sub-block
2646 // FIXME: This appears to uncover a lot of bugs
2650 bool DoCheckError158 (string name, Location loc)
2652 LabeledStatement s = LookupLabel (name);
2654 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2655 Error_158 (name, loc);
2662 public RootScopeInfo CreateRootScope (TypeContainer host)
2664 if (root_scope != null)
2667 if (Container == null)
2668 root_scope = new RootScopeInfo (
2669 this, host, generic, StartLocation);
2671 if (scope_info != null)
2672 throw new InternalErrorException ();
2674 scope_info = root_scope;
2678 public override Expression CreateExpressionTree (EmitContext ec)
2680 return ((Statement) statements [0]).CreateExpressionTree (ec);
2683 public void CreateIteratorHost (RootScopeInfo root)
2685 Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
2687 if (Parent != null || root_scope != null)
2688 throw new InternalErrorException ();
2690 scope_info = root_scope = root;
2693 public RootScopeInfo RootScope {
2695 if (root_scope != null)
2697 else if (Container != null)
2698 return Container.RootScope;
2704 public FlowBranchingToplevel TopLevelBranching {
2705 get { return top_level_branching; }
2709 // This is used if anonymous methods are used inside an iterator
2710 // (see 2test-22.cs for an example).
2712 // The AnonymousMethod is created while parsing - at a time when we don't
2713 // know yet that we're inside an iterator, so it's `Container' is initially
2714 // null. Later on, when resolving the iterator, we need to move the
2715 // anonymous method into that iterator.
2717 public void ReParent (ToplevelBlock new_parent)
2719 if ((flags & Flags.VariablesInitialized) != 0)
2720 throw new InternalErrorException ("block has already been resolved");
2722 Parent = new_parent;
2726 // Returns a `ParameterReference' for the given name, or null if there
2727 // is no such parameter
2729 public ParameterReference GetParameterReference (string name, Location loc)
2731 ToplevelParameterInfo p = GetParameterInfo (name);
2732 return p == null ? null : new ParameterReference (this, p, loc);
2735 public ToplevelParameterInfo GetParameterInfo (string name)
2738 for (ToplevelBlock t = this; t != null; t = t.Container) {
2739 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2741 return t.parameter_info [idx];
2746 LocalInfo this_variable = null;
2749 // Returns the "this" instance variable of this block.
2750 // See AddThisVariable() for more information.
2752 public LocalInfo ThisVariable {
2753 get { return this_variable; }
2758 // This is used by non-static `struct' constructors which do not have an
2759 // initializer - in this case, the constructor must initialize all of the
2760 // struct's fields. To do this, we add a "this" variable and use the flow
2761 // analysis code to ensure that it's been fully initialized before control
2762 // leaves the constructor.
2764 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2766 if (this_variable == null) {
2767 this_variable = new LocalInfo (ds, this, l);
2768 this_variable.Used = true;
2769 this_variable.IsThis = true;
2771 Variables.Add ("this", this_variable);
2774 return this_variable;
2777 public bool IsThisAssigned (EmitContext ec)
2779 return this_variable == null || this_variable.IsThisAssigned (ec);
2782 public bool ResolveMeta (EmitContext ec, Parameters ip)
2784 int errors = Report.Errors;
2785 int orig_count = parameters.Count;
2787 if (top_level_branching != null)
2793 // Assert: orig_count != parameter.Count => orig_count == 0
2794 if (orig_count != 0 && orig_count != parameters.Count)
2795 throw new InternalErrorException ("parameter information mismatch");
2797 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2799 for (int i = 0; i < orig_count; ++i) {
2800 Parameter.Modifier mod = parameters.ParameterModifier (i);
2802 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2805 VariableInfo vi = new VariableInfo (ip, i, offset);
2806 parameter_info [i].VariableInfo = vi;
2807 offset += vi.Length;
2810 ResolveMeta (ec, offset);
2812 top_level_branching = ec.StartFlowBranching (this);
2814 return Report.Errors == errors;
2818 // Check whether all `out' parameters have been assigned.
2820 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2822 if (vector.IsUnreachable)
2825 int n = parameter_info == null ? 0 : parameter_info.Length;
2827 for (int i = 0; i < n; i++) {
2828 VariableInfo var = parameter_info [i].VariableInfo;
2833 if (vector.IsAssigned (var, false))
2836 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2841 public override void EmitMeta (EmitContext ec)
2843 // Avoid declaring an IL variable for this_variable since it is not accessed
2844 // from the generated IL
2845 if (this_variable != null)
2846 Variables.Remove ("this");
2848 parameters.ResolveVariable (this);
2851 protected override void EmitSymbolInfo (EmitContext ec)
2853 if ((AnonymousContainer != null) && (AnonymousContainer.Scope != null))
2854 SymbolWriter.DefineScopeVariable (AnonymousContainer.Scope.ID);
2856 base.EmitSymbolInfo (ec);
2859 public void MakeIterator (Iterator iterator)
2861 flags |= Flags.IsIterator;
2863 Block block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2864 foreach (Statement stmt in statements)
2865 block.AddStatement (stmt);
2866 statements.Clear ();
2867 statements.Add (new MoveNextStatement (iterator, block));
2870 protected class MoveNextStatement : Statement {
2874 public MoveNextStatement (Iterator iterator, Block block)
2876 this.iterator = iterator;
2878 this.loc = iterator.Location;
2881 public override bool Resolve (EmitContext ec)
2883 return block.Resolve (ec);
2886 protected override void DoEmit (EmitContext ec)
2888 iterator.EmitMoveNext (ec, block);
2892 public override string ToString ()
2894 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2895 root_scope, anonymous_container != null ?
2896 anonymous_container.Scope : null);
2900 public class SwitchLabel {
2907 Label il_label_code;
2908 bool il_label_code_set;
2910 public static readonly object NullStringCase = new object ();
2913 // if expr == null, then it is the default case.
2915 public SwitchLabel (Expression expr, Location l)
2921 public Expression Label {
2927 public object Converted {
2933 public Label GetILLabel (EmitContext ec)
2936 il_label = ec.ig.DefineLabel ();
2937 il_label_set = true;
2942 public Label GetILLabelCode (EmitContext ec)
2944 if (!il_label_code_set){
2945 il_label_code = ec.ig.DefineLabel ();
2946 il_label_code_set = true;
2948 return il_label_code;
2952 // Resolves the expression, reduces it to a literal if possible
2953 // and then converts it to the requested type.
2955 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2957 Expression e = label.Resolve (ec);
2962 Constant c = e as Constant;
2964 Report.Error (150, loc, "A constant value is expected");
2968 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2969 converted = NullStringCase;
2973 if (allow_nullable && c.GetValue () == null) {
2974 converted = NullStringCase;
2978 c = c.ImplicitConversionRequired (required_type, loc);
2982 converted = c.GetValue ();
2986 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2989 if (converted == null)
2991 else if (converted == NullStringCase)
2993 else if (TypeManager.IsEnumType (switch_type))
2994 label = TypeManager.CSharpEnumValue (switch_type, converted);
2996 label = converted.ToString ();
2998 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2999 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3002 public SwitchLabel Clone (CloneContext clonectx)
3004 return new SwitchLabel (label.Clone (clonectx), loc);
3008 public class SwitchSection {
3009 // An array of SwitchLabels.
3010 public readonly ArrayList Labels;
3011 public readonly Block Block;
3013 public SwitchSection (ArrayList labels, Block block)
3019 public SwitchSection Clone (CloneContext clonectx)
3021 ArrayList cloned_labels = new ArrayList ();
3023 foreach (SwitchLabel sl in cloned_labels)
3024 cloned_labels.Add (sl.Clone (clonectx));
3026 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3030 public class Switch : Statement {
3031 public ArrayList Sections;
3032 public Expression Expr;
3035 /// Maps constants whose type type SwitchType to their SwitchLabels.
3037 public IDictionary Elements;
3040 /// The governing switch type
3042 public Type SwitchType;
3047 Label default_target;
3049 Expression new_expr;
3051 SwitchSection constant_section;
3052 SwitchSection default_section;
3056 // Nullable Types support for GMCS.
3058 Nullable.Unwrap unwrap;
3060 protected bool HaveUnwrap {
3061 get { return unwrap != null; }
3064 protected bool HaveUnwrap {
3065 get { return false; }
3070 // The types allowed to be implicitly cast from
3071 // on the governing type
3073 static Type [] allowed_types;
3075 public Switch (Expression e, ArrayList sects, Location l)
3082 public bool GotDefault {
3084 return default_section != null;
3088 public Label DefaultTarget {
3090 return default_target;
3095 // Determines the governing type for a switch. The returned
3096 // expression might be the expression from the switch, or an
3097 // expression that includes any potential conversions to the
3098 // integral types or to string.
3100 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3104 if (t == TypeManager.byte_type ||
3105 t == TypeManager.sbyte_type ||
3106 t == TypeManager.ushort_type ||
3107 t == TypeManager.short_type ||
3108 t == TypeManager.uint32_type ||
3109 t == TypeManager.int32_type ||
3110 t == TypeManager.uint64_type ||
3111 t == TypeManager.int64_type ||
3112 t == TypeManager.char_type ||
3113 t == TypeManager.string_type ||
3114 t == TypeManager.bool_type ||
3115 TypeManager.IsEnumType (t))
3118 if (allowed_types == null){
3119 allowed_types = new Type [] {
3120 TypeManager.sbyte_type,
3121 TypeManager.byte_type,
3122 TypeManager.short_type,
3123 TypeManager.ushort_type,
3124 TypeManager.int32_type,
3125 TypeManager.uint32_type,
3126 TypeManager.int64_type,
3127 TypeManager.uint64_type,
3128 TypeManager.char_type,
3129 TypeManager.string_type,
3130 TypeManager.bool_type
3135 // Try to find a *user* defined implicit conversion.
3137 // If there is no implicit conversion, or if there are multiple
3138 // conversions, we have to report an error
3140 Expression converted = null;
3141 foreach (Type tt in allowed_types){
3144 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3149 // Ignore over-worked ImplicitUserConversions that do
3150 // an implicit conversion in addition to the user conversion.
3152 if (!(e is UserCast))
3155 if (converted != null){
3156 Report.ExtraInformation (
3158 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3159 TypeManager.CSharpName (expr.Type)));
3169 // Performs the basic sanity checks on the switch statement
3170 // (looks for duplicate keys and non-constant expressions).
3172 // It also returns a hashtable with the keys that we will later
3173 // use to compute the switch tables
3175 bool CheckSwitch (EmitContext ec)
3178 Elements = Sections.Count > 10 ?
3179 (IDictionary)new Hashtable () :
3180 (IDictionary)new ListDictionary ();
3182 foreach (SwitchSection ss in Sections){
3183 foreach (SwitchLabel sl in ss.Labels){
3184 if (sl.Label == null){
3185 if (default_section != null){
3186 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3189 default_section = ss;
3193 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3198 object key = sl.Converted;
3200 Elements.Add (key, sl);
3201 } catch (ArgumentException) {
3202 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3210 void EmitObjectInteger (ILGenerator ig, object k)
3213 IntConstant.EmitInt (ig, (int) k);
3214 else if (k is Constant) {
3215 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3218 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3221 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3223 IntConstant.EmitInt (ig, (int) (long) k);
3224 ig.Emit (OpCodes.Conv_I8);
3227 LongConstant.EmitLong (ig, (long) k);
3229 else if (k is ulong)
3231 ulong ul = (ulong) k;
3234 IntConstant.EmitInt (ig, unchecked ((int) ul));
3235 ig.Emit (OpCodes.Conv_U8);
3239 LongConstant.EmitLong (ig, unchecked ((long) ul));
3243 IntConstant.EmitInt (ig, (int) ((char) k));
3244 else if (k is sbyte)
3245 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3247 IntConstant.EmitInt (ig, (int) ((byte) k));
3248 else if (k is short)
3249 IntConstant.EmitInt (ig, (int) ((short) k));
3250 else if (k is ushort)
3251 IntConstant.EmitInt (ig, (int) ((ushort) k));
3253 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3255 throw new Exception ("Unhandled case");
3258 // structure used to hold blocks of keys while calculating table switch
3259 class KeyBlock : IComparable
3261 public KeyBlock (long _first)
3263 first = last = _first;
3267 public ArrayList element_keys = null;
3268 // how many items are in the bucket
3269 public int Size = 1;
3272 get { return (int) (last - first + 1); }
3274 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3276 return kb_last.last - kb_first.first + 1;
3278 public int CompareTo (object obj)
3280 KeyBlock kb = (KeyBlock) obj;
3281 int nLength = Length;
3282 int nLengthOther = kb.Length;
3283 if (nLengthOther == nLength)
3284 return (int) (kb.first - first);
3285 return nLength - nLengthOther;
3290 /// This method emits code for a lookup-based switch statement (non-string)
3291 /// Basically it groups the cases into blocks that are at least half full,
3292 /// and then spits out individual lookup opcodes for each block.
3293 /// It emits the longest blocks first, and short blocks are just
3294 /// handled with direct compares.
3296 /// <param name="ec"></param>
3297 /// <param name="val"></param>
3298 /// <returns></returns>
3299 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3301 int element_count = Elements.Count;
3302 object [] element_keys = new object [element_count];
3303 Elements.Keys.CopyTo (element_keys, 0);
3304 Array.Sort (element_keys);
3306 // initialize the block list with one element per key
3307 ArrayList key_blocks = new ArrayList ();
3308 foreach (object key in element_keys)
3309 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3311 KeyBlock current_kb;
3312 // iteratively merge the blocks while they are at least half full
3313 // there's probably a really cool way to do this with a tree...
3314 while (key_blocks.Count > 1)
3316 ArrayList key_blocks_new = new ArrayList ();
3317 current_kb = (KeyBlock) key_blocks [0];
3318 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3320 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3321 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3324 current_kb.last = kb.last;
3325 current_kb.Size += kb.Size;
3329 // start a new block
3330 key_blocks_new.Add (current_kb);
3334 key_blocks_new.Add (current_kb);
3335 if (key_blocks.Count == key_blocks_new.Count)
3337 key_blocks = key_blocks_new;
3340 // initialize the key lists
3341 foreach (KeyBlock kb in key_blocks)
3342 kb.element_keys = new ArrayList ();
3344 // fill the key lists
3346 if (key_blocks.Count > 0) {
3347 current_kb = (KeyBlock) key_blocks [0];
3348 foreach (object key in element_keys)
3350 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3351 System.Convert.ToInt64 (key) > current_kb.last;
3353 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3354 current_kb.element_keys.Add (key);
3358 // sort the blocks so we can tackle the largest ones first
3361 // okay now we can start...
3362 ILGenerator ig = ec.ig;
3363 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3364 Label lbl_default = ig.DefineLabel ();
3366 Type type_keys = null;
3367 if (element_keys.Length > 0)
3368 type_keys = element_keys [0].GetType (); // used for conversions
3372 if (TypeManager.IsEnumType (SwitchType))
3373 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3375 compare_type = SwitchType;
3377 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3379 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3380 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3383 foreach (object key in kb.element_keys)
3385 ig.Emit (OpCodes.Ldloc, val);
3386 EmitObjectInteger (ig, key);
3387 SwitchLabel sl = (SwitchLabel) Elements [key];
3388 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3393 // TODO: if all the keys in the block are the same and there are
3394 // no gaps/defaults then just use a range-check.
3395 if (compare_type == TypeManager.int64_type ||
3396 compare_type == TypeManager.uint64_type)
3398 // TODO: optimize constant/I4 cases
3400 // check block range (could be > 2^31)
3401 ig.Emit (OpCodes.Ldloc, val);
3402 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3403 ig.Emit (OpCodes.Blt, lbl_default);
3404 ig.Emit (OpCodes.Ldloc, val);
3405 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3406 ig.Emit (OpCodes.Bgt, lbl_default);
3409 ig.Emit (OpCodes.Ldloc, val);
3412 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3413 ig.Emit (OpCodes.Sub);
3415 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3420 ig.Emit (OpCodes.Ldloc, val);
3421 int first = (int) kb.first;
3424 IntConstant.EmitInt (ig, first);
3425 ig.Emit (OpCodes.Sub);
3429 IntConstant.EmitInt (ig, -first);
3430 ig.Emit (OpCodes.Add);
3434 // first, build the list of labels for the switch
3436 int cJumps = kb.Length;
3437 Label [] switch_labels = new Label [cJumps];
3438 for (int iJump = 0; iJump < cJumps; iJump++)
3440 object key = kb.element_keys [iKey];
3441 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3443 SwitchLabel sl = (SwitchLabel) Elements [key];
3444 switch_labels [iJump] = sl.GetILLabel (ec);
3448 switch_labels [iJump] = lbl_default;
3450 // emit the switch opcode
3451 ig.Emit (OpCodes.Switch, switch_labels);
3454 // mark the default for this block
3456 ig.MarkLabel (lbl_default);
3459 // TODO: find the default case and emit it here,
3460 // to prevent having to do the following jump.
3461 // make sure to mark other labels in the default section
3463 // the last default just goes to the end
3464 ig.Emit (OpCodes.Br, lbl_default);
3466 // now emit the code for the sections
3467 bool found_default = false;
3468 bool found_null = false;
3469 foreach (SwitchSection ss in Sections)
3471 foreach (SwitchLabel sl in ss.Labels)
3472 if (sl.Converted == SwitchLabel.NullStringCase)
3476 foreach (SwitchSection ss in Sections)
3478 foreach (SwitchLabel sl in ss.Labels)
3480 ig.MarkLabel (sl.GetILLabel (ec));
3481 ig.MarkLabel (sl.GetILLabelCode (ec));
3482 if (sl.Converted == SwitchLabel.NullStringCase)
3483 ig.MarkLabel (null_target);
3484 else if (sl.Label == null) {
3485 ig.MarkLabel (lbl_default);
3486 found_default = true;
3488 ig.MarkLabel (null_target);
3494 if (!found_default) {
3495 ig.MarkLabel (lbl_default);
3496 if (HaveUnwrap && !found_null) {
3497 ig.MarkLabel (null_target);
3501 ig.MarkLabel (lbl_end);
3504 // This simple emit switch works, but does not take advantage of the
3506 // TODO: remove non-string logic from here
3507 // TODO: binary search strings?
3509 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3511 ILGenerator ig = ec.ig;
3512 Label end_of_switch = ig.DefineLabel ();
3513 Label next_test = ig.DefineLabel ();
3514 bool first_test = true;
3515 bool pending_goto_end = false;
3516 bool null_marked = false;
3518 int section_count = Sections.Count;
3520 // TODO: implement switch optimization for string by using Hashtable
3521 //if (SwitchType == TypeManager.string_type && section_count > 7)
3522 // Console.WriteLine ("Switch optimization possible " + loc);
3524 ig.Emit (OpCodes.Ldloc, val);
3526 if (Elements.Contains (SwitchLabel.NullStringCase)){
3527 ig.Emit (OpCodes.Brfalse, null_target);
3529 ig.Emit (OpCodes.Brfalse, default_target);
3531 ig.Emit (OpCodes.Ldloc, val);
3532 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3533 ig.Emit (OpCodes.Stloc, val);
3535 for (int section = 0; section < section_count; section++){
3536 SwitchSection ss = (SwitchSection) Sections [section];
3538 if (ss == default_section)
3541 Label sec_begin = ig.DefineLabel ();
3543 ig.Emit (OpCodes.Nop);
3545 if (pending_goto_end)
3546 ig.Emit (OpCodes.Br, end_of_switch);
3548 int label_count = ss.Labels.Count;
3550 for (int label = 0; label < label_count; label++){
3551 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3552 ig.MarkLabel (sl.GetILLabel (ec));
3555 ig.MarkLabel (next_test);
3556 next_test = ig.DefineLabel ();
3559 // If we are the default target
3561 if (sl.Label != null){
3562 object lit = sl.Converted;
3564 if (lit == SwitchLabel.NullStringCase){
3566 if (label + 1 == label_count)
3567 ig.Emit (OpCodes.Br, next_test);
3571 ig.Emit (OpCodes.Ldloc, val);
3572 ig.Emit (OpCodes.Ldstr, (string)lit);
3573 if (label_count == 1)
3574 ig.Emit (OpCodes.Bne_Un, next_test);
3576 if (label+1 == label_count)
3577 ig.Emit (OpCodes.Bne_Un, next_test);
3579 ig.Emit (OpCodes.Beq, sec_begin);
3584 ig.MarkLabel (null_target);
3587 ig.MarkLabel (sec_begin);
3588 foreach (SwitchLabel sl in ss.Labels)
3589 ig.MarkLabel (sl.GetILLabelCode (ec));
3592 pending_goto_end = !ss.Block.HasRet;
3595 ig.MarkLabel (next_test);
3596 ig.MarkLabel (default_target);
3598 ig.MarkLabel (null_target);
3599 if (default_section != null)
3600 default_section.Block.Emit (ec);
3601 ig.MarkLabel (end_of_switch);
3604 SwitchSection FindSection (SwitchLabel label)
3606 foreach (SwitchSection ss in Sections){
3607 foreach (SwitchLabel sl in ss.Labels){
3616 public override bool Resolve (EmitContext ec)
3618 Expr = Expr.Resolve (ec);
3622 new_expr = SwitchGoverningType (ec, Expr);
3625 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3626 unwrap = Nullable.Unwrap.Create (Expr, ec);
3630 new_expr = SwitchGoverningType (ec, unwrap);
3634 if (new_expr == null){
3635 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3640 SwitchType = new_expr.Type;
3642 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3643 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3647 if (!CheckSwitch (ec))
3651 Elements.Remove (SwitchLabel.NullStringCase);
3653 Switch old_switch = ec.Switch;
3655 ec.Switch.SwitchType = SwitchType;
3657 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3658 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3660 is_constant = new_expr is Constant;
3662 object key = ((Constant) new_expr).GetValue ();
3663 SwitchLabel label = (SwitchLabel) Elements [key];
3665 constant_section = FindSection (label);
3666 if (constant_section == null)
3667 constant_section = default_section;
3672 foreach (SwitchSection ss in Sections){
3674 ec.CurrentBranching.CreateSibling (
3675 null, FlowBranching.SiblingType.SwitchSection);
3679 if (is_constant && (ss != constant_section)) {
3680 // If we're a constant switch, we're only emitting
3681 // one single section - mark all the others as
3683 ec.CurrentBranching.CurrentUsageVector.Goto ();
3684 if (!ss.Block.ResolveUnreachable (ec, true)) {
3688 if (!ss.Block.Resolve (ec))
3693 if (default_section == null)
3694 ec.CurrentBranching.CreateSibling (
3695 null, FlowBranching.SiblingType.SwitchSection);
3697 ec.EndFlowBranching ();
3698 ec.Switch = old_switch;
3700 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3702 if (TypeManager.string_isinterned_string == null) {
3703 TypeManager.string_isinterned_string = TypeManager.GetPredefinedMethod (TypeManager.string_type,
3704 "IsInterned", loc, TypeManager.string_type);
3710 protected override void DoEmit (EmitContext ec)
3712 ILGenerator ig = ec.ig;
3714 default_target = ig.DefineLabel ();
3715 null_target = ig.DefineLabel ();
3717 // Store variable for comparission purposes
3720 value = ig.DeclareLocal (SwitchType);
3722 unwrap.EmitCheck (ec);
3723 ig.Emit (OpCodes.Brfalse, null_target);
3725 ig.Emit (OpCodes.Stloc, value);
3727 } else if (!is_constant) {
3728 value = ig.DeclareLocal (SwitchType);
3730 ig.Emit (OpCodes.Stloc, value);
3735 // Setup the codegen context
3737 Label old_end = ec.LoopEnd;
3738 Switch old_switch = ec.Switch;
3740 ec.LoopEnd = ig.DefineLabel ();
3745 if (constant_section != null)
3746 constant_section.Block.Emit (ec);
3747 } else if (SwitchType == TypeManager.string_type)
3748 SimpleSwitchEmit (ec, value);
3750 TableSwitchEmit (ec, value);
3752 // Restore context state.
3753 ig.MarkLabel (ec.LoopEnd);
3756 // Restore the previous context
3758 ec.LoopEnd = old_end;
3759 ec.Switch = old_switch;
3762 protected override void CloneTo (CloneContext clonectx, Statement t)
3764 Switch target = (Switch) t;
3766 target.Expr = Expr.Clone (clonectx);
3767 target.Sections = new ArrayList ();
3768 foreach (SwitchSection ss in Sections){
3769 target.Sections.Add (ss.Clone (clonectx));
3774 public abstract class ExceptionStatement : Statement
3776 public abstract void EmitFinallyBody (EmitContext ec);
3778 protected bool emit_finally = true;
3780 protected void EmitFinally (EmitContext ec)
3782 Label end_finally = ec.ig.DefineLabel ();
3784 ec.ig.BeginFinallyBlock ();
3785 else if (ec.InIterator) {
3786 ec.ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3787 ec.ig.Emit (OpCodes.Brtrue, end_finally);
3789 EmitFinallyBody (ec);
3790 ec.ig.MarkLabel (end_finally);
3793 protected void ResolveFinally (FlowBranchingException branching)
3795 emit_finally = branching.EmitFinally;
3799 public class Lock : ExceptionStatement {
3801 public Statement Statement;
3802 TemporaryVariable temp;
3804 public Lock (Expression expr, Statement stmt, Location l)
3811 public override bool Resolve (EmitContext ec)
3813 expr = expr.Resolve (ec);
3817 if (expr.Type.IsValueType){
3818 Report.Error (185, loc,
3819 "`{0}' is not a reference type as required by the lock statement",
3820 TypeManager.CSharpName (expr.Type));
3824 FlowBranchingException branching = ec.StartFlowBranching (this);
3825 bool ok = Statement.Resolve (ec);
3827 ResolveFinally (branching);
3829 ec.EndFlowBranching ();
3831 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3832 // So, ensure there's some IL code after this statement.
3833 ec.NeedReturnLabel ();
3835 // Avoid creating libraries that reference the internal
3838 if (t == TypeManager.null_type)
3839 t = TypeManager.object_type;
3841 temp = new TemporaryVariable (t, loc);
3844 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
3845 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
3846 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
3847 monitor_type, "Enter", loc, TypeManager.object_type);
3848 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
3849 monitor_type, "Exit", loc, TypeManager.object_type);
3855 protected override void DoEmit (EmitContext ec)
3857 ILGenerator ig = ec.ig;
3859 temp.Store (ec, expr);
3861 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3865 ig.BeginExceptionBlock ();
3866 Statement.Emit (ec);
3871 ig.EndExceptionBlock ();
3874 public override void EmitFinallyBody (EmitContext ec)
3877 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3880 protected override void CloneTo (CloneContext clonectx, Statement t)
3882 Lock target = (Lock) t;
3884 target.expr = expr.Clone (clonectx);
3885 target.Statement = Statement.Clone (clonectx);
3889 public class Unchecked : Statement {
3892 public Unchecked (Block b)
3898 public override bool Resolve (EmitContext ec)
3900 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3901 return Block.Resolve (ec);
3904 protected override void DoEmit (EmitContext ec)
3906 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3910 protected override void CloneTo (CloneContext clonectx, Statement t)
3912 Unchecked target = (Unchecked) t;
3914 target.Block = clonectx.LookupBlock (Block);
3918 public class Checked : Statement {
3921 public Checked (Block b)
3924 b.Unchecked = false;
3927 public override bool Resolve (EmitContext ec)
3929 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3930 return Block.Resolve (ec);
3933 protected override void DoEmit (EmitContext ec)
3935 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3939 protected override void CloneTo (CloneContext clonectx, Statement t)
3941 Checked target = (Checked) t;
3943 target.Block = clonectx.LookupBlock (Block);
3947 public class Unsafe : Statement {
3950 public Unsafe (Block b)
3953 Block.Unsafe = true;
3956 public override bool Resolve (EmitContext ec)
3958 using (ec.With (EmitContext.Flags.InUnsafe, true))
3959 return Block.Resolve (ec);
3962 protected override void DoEmit (EmitContext ec)
3964 using (ec.With (EmitContext.Flags.InUnsafe, true))
3967 protected override void CloneTo (CloneContext clonectx, Statement t)
3969 Unsafe target = (Unsafe) t;
3971 target.Block = clonectx.LookupBlock (Block);
3978 public class Fixed : Statement {
3980 ArrayList declarators;
3981 Statement statement;
3986 abstract class Emitter
3988 protected LocalInfo vi;
3989 protected Expression converted;
3991 protected Emitter (Expression expr, LocalInfo li)
3997 public abstract void Emit (EmitContext ec);
3998 public abstract void EmitExit (EmitContext ec);
4001 class ExpressionEmitter : Emitter {
4002 public ExpressionEmitter (Expression converted, LocalInfo li) :
4003 base (converted, li)
4007 public override void Emit (EmitContext ec) {
4009 // Store pointer in pinned location
4011 converted.Emit (ec);
4012 vi.Variable.EmitAssign (ec);
4015 public override void EmitExit (EmitContext ec)
4017 ec.ig.Emit (OpCodes.Ldc_I4_0);
4018 ec.ig.Emit (OpCodes.Conv_U);
4019 vi.Variable.EmitAssign (ec);
4023 class StringEmitter : Emitter {
4024 LocalBuilder pinned_string;
4027 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4033 public override void Emit (EmitContext ec)
4035 ILGenerator ig = ec.ig;
4036 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4038 converted.Emit (ec);
4039 ig.Emit (OpCodes.Stloc, pinned_string);
4041 Expression sptr = new StringPtr (pinned_string, loc);
4042 converted = Convert.ImplicitConversionRequired (
4043 ec, sptr, vi.VariableType, loc);
4045 if (converted == null)
4048 converted.Emit (ec);
4049 vi.Variable.EmitAssign (ec);
4052 public override void EmitExit (EmitContext ec)
4054 ec.ig.Emit (OpCodes.Ldnull);
4055 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4059 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4062 declarators = decls;
4067 public Statement Statement {
4068 get { return statement; }
4071 public override bool Resolve (EmitContext ec)
4074 Expression.UnsafeError (loc);
4078 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4079 if (texpr == null) {
4080 if (type is VarExpr)
4081 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4086 expr_type = texpr.Type;
4088 data = new Emitter [declarators.Count];
4090 if (!expr_type.IsPointer){
4091 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4096 foreach (Pair p in declarators){
4097 LocalInfo vi = (LocalInfo) p.First;
4098 Expression e = (Expression) p.Second;
4100 vi.VariableInfo.SetAssigned (ec);
4101 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4104 // The rules for the possible declarators are pretty wise,
4105 // but the production on the grammar is more concise.
4107 // So we have to enforce these rules here.
4109 // We do not resolve before doing the case 1 test,
4110 // because the grammar is explicit in that the token &
4111 // is present, so we need to test for this particular case.
4115 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4120 // Case 1: & object.
4122 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4123 Expression child = ((Unary) e).Expr;
4125 if (child is ParameterReference || child is LocalVariableReference){
4128 "No need to use fixed statement for parameters or " +
4129 "local variable declarations (address is already " +
4134 ec.InFixedInitializer = true;
4136 ec.InFixedInitializer = false;
4140 child = ((Unary) e).Expr;
4142 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4145 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4146 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4150 data [i] = new ExpressionEmitter (e, vi);
4156 ec.InFixedInitializer = true;
4158 ec.InFixedInitializer = false;
4165 if (e.Type.IsArray){
4166 Type array_type = TypeManager.GetElementType (e.Type);
4169 // Provided that array_type is unmanaged,
4171 if (!TypeManager.VerifyUnManaged (array_type, loc))
4175 // and T* is implicitly convertible to the
4176 // pointer type given in the fixed statement.
4178 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4180 Expression converted = Convert.ImplicitConversionRequired (
4181 ec, array_ptr, vi.VariableType, loc);
4182 if (converted == null)
4186 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4188 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4189 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4190 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4194 converted = converted.Resolve (ec);
4196 data [i] = new ExpressionEmitter (converted, vi);
4205 if (e.Type == TypeManager.string_type){
4206 data [i] = new StringEmitter (e, vi, loc);
4211 // Case 4: fixed buffer
4212 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4213 if (fixed_buffer_ptr != null) {
4214 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4219 // For other cases, flag a `this is already fixed expression'
4221 if (e is LocalVariableReference || e is ParameterReference ||
4222 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4224 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4228 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4232 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4233 bool ok = statement.Resolve (ec);
4234 bool flow_unreachable = ec.EndFlowBranching ();
4235 has_ret = flow_unreachable;
4240 protected override void DoEmit (EmitContext ec)
4242 for (int i = 0; i < data.Length; i++) {
4246 statement.Emit (ec);
4252 // Clear the pinned variable
4254 for (int i = 0; i < data.Length; i++) {
4255 data [i].EmitExit (ec);
4259 protected override void CloneTo (CloneContext clonectx, Statement t)
4261 Fixed target = (Fixed) t;
4263 target.type = type.Clone (clonectx);
4264 target.declarators = new ArrayList (declarators.Count);
4265 foreach (Pair p in declarators) {
4266 LocalInfo vi = (LocalInfo) p.First;
4267 Expression e = (Expression) p.Second;
4269 target.declarators.Add (
4270 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4273 target.statement = statement.Clone (clonectx);
4277 public class Catch : Statement {
4278 public readonly string Name;
4280 public Block VarBlock;
4282 Expression type_expr;
4285 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4290 VarBlock = var_block;
4294 public Type CatchType {
4300 public bool IsGeneral {
4302 return type_expr == null;
4306 protected override void DoEmit (EmitContext ec)
4308 ILGenerator ig = ec.ig;
4310 if (CatchType != null)
4311 ig.BeginCatchBlock (CatchType);
4313 ig.BeginCatchBlock (TypeManager.object_type);
4315 if (VarBlock != null)
4319 LocalInfo vi = Block.GetLocalInfo (Name);
4321 throw new Exception ("Variable does not exist in this block");
4323 if (vi.Variable.NeedsTemporary) {
4324 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4325 ig.Emit (OpCodes.Stloc, e);
4327 vi.Variable.EmitInstance (ec);
4328 ig.Emit (OpCodes.Ldloc, e);
4329 vi.Variable.EmitAssign (ec);
4331 vi.Variable.EmitAssign (ec);
4333 ig.Emit (OpCodes.Pop);
4338 public override bool Resolve (EmitContext ec)
4340 using (ec.With (EmitContext.Flags.InCatch, true)) {
4341 if (type_expr != null) {
4342 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4348 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4349 Error (155, "The type caught or thrown must be derived from System.Exception");
4355 if (!Block.Resolve (ec))
4358 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4359 // emit the "unused variable" warnings.
4360 if (VarBlock != null)
4361 return VarBlock.Resolve (ec);
4367 protected override void CloneTo (CloneContext clonectx, Statement t)
4369 Catch target = (Catch) t;
4371 if (type_expr != null)
4372 target.type_expr = type_expr.Clone (clonectx);
4373 if (VarBlock != null)
4374 target.VarBlock = clonectx.LookupBlock (VarBlock);
4375 target.Block = clonectx.LookupBlock (Block);
4379 public class TryFinally : ExceptionStatement {
4383 public TryFinally (Statement stmt, Block fini, Location l)
4390 public override bool Resolve (EmitContext ec)
4394 FlowBranchingException branching = ec.StartFlowBranching (this);
4396 if (!stmt.Resolve (ec))
4400 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4401 using (ec.With (EmitContext.Flags.InFinally, true)) {
4402 if (!fini.Resolve (ec))
4406 ResolveFinally (branching);
4407 ec.EndFlowBranching ();
4409 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4410 // So, ensure there's some IL code after this statement.
4411 ec.NeedReturnLabel ();
4416 protected override void DoEmit (EmitContext ec)
4418 ILGenerator ig = ec.ig;
4421 ig.BeginExceptionBlock ();
4426 ig.EndExceptionBlock ();
4429 public override void EmitFinallyBody (EmitContext ec)
4434 protected override void CloneTo (CloneContext clonectx, Statement t)
4436 TryFinally target = (TryFinally) t;
4438 target.stmt = (Statement) stmt.Clone (clonectx);
4440 target.fini = clonectx.LookupBlock (fini);
4444 public class TryCatch : Statement {
4446 public ArrayList Specific;
4447 public Catch General;
4448 bool inside_try_finally;
4450 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4453 this.Specific = catch_clauses;
4454 this.General = null;
4455 this.inside_try_finally = inside_try_finally;
4457 for (int i = 0; i < catch_clauses.Count; ++i) {
4458 Catch c = (Catch) catch_clauses [i];
4460 if (i != catch_clauses.Count - 1)
4461 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4463 catch_clauses.RemoveAt (i);
4471 public override bool Resolve (EmitContext ec)
4475 ec.StartFlowBranching (FlowBranching.BranchingType.TryCatch, loc);
4477 if (!Block.Resolve (ec))
4480 Type[] prev_catches = new Type [Specific.Count];
4482 foreach (Catch c in Specific){
4483 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4485 if (c.Name != null) {
4486 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4488 throw new Exception ();
4490 vi.VariableInfo = null;
4493 if (!c.Resolve (ec))
4496 Type resolved_type = c.CatchType;
4497 for (int ii = 0; ii < last_index; ++ii) {
4498 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4499 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4504 prev_catches [last_index++] = resolved_type;
4507 if (General != null) {
4508 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4509 foreach (Catch c in Specific){
4510 if (c.CatchType == TypeManager.exception_type) {
4511 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'");
4516 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4518 if (!General.Resolve (ec))
4522 ec.EndFlowBranching ();
4524 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4525 // So, ensure there's some IL code after this statement
4526 ec.NeedReturnLabel ();
4531 protected override void DoEmit (EmitContext ec)
4533 ILGenerator ig = ec.ig;
4535 // FIXME: remove the InIterator
4536 if (!inside_try_finally || ec.InIterator)
4537 ig.BeginExceptionBlock ();
4541 foreach (Catch c in Specific)
4544 if (General != null)
4547 // FIXME: remove the InIterator
4548 if (!inside_try_finally || ec.InIterator)
4549 ig.EndExceptionBlock ();
4552 protected override void CloneTo (CloneContext clonectx, Statement t)
4554 TryCatch target = (TryCatch) t;
4556 target.Block = clonectx.LookupBlock (Block);
4557 if (General != null)
4558 target.General = (Catch) General.Clone (clonectx);
4559 if (Specific != null){
4560 target.Specific = new ArrayList ();
4561 foreach (Catch c in Specific)
4562 target.Specific.Add (c.Clone (clonectx));
4567 public class Using : ExceptionStatement {
4568 object expression_or_block;
4569 public Statement Statement;
4573 Expression [] resolved_vars;
4574 Expression [] converted_vars;
4575 Expression [] assign;
4576 TemporaryVariable local_copy;
4578 public Using (object expression_or_block, Statement stmt, Location l)
4580 this.expression_or_block = expression_or_block;
4586 // Resolves for the case of using using a local variable declaration.
4588 bool ResolveLocalVariableDecls (EmitContext ec)
4590 resolved_vars = new Expression[var_list.Count];
4591 assign = new Expression [var_list.Count];
4592 converted_vars = new Expression[var_list.Count];
4594 for (int i = 0; i < assign.Length; ++i) {
4595 DictionaryEntry e = (DictionaryEntry) var_list [i];
4596 Expression var = (Expression) e.Key;
4597 Expression new_expr = (Expression) e.Value;
4599 Expression a = new Assign (var, new_expr, loc);
4604 resolved_vars [i] = var;
4607 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4608 converted_vars [i] = var;
4612 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4614 Error_IsNotConvertibleToIDisposable (var);
4618 converted_vars [i] = a;
4624 static void Error_IsNotConvertibleToIDisposable (Expression expr)
4626 Report.SymbolRelatedToPreviousError (expr.Type);
4627 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4628 expr.GetSignatureForError ());
4631 bool ResolveExpression (EmitContext ec)
4633 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4634 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4635 Error_IsNotConvertibleToIDisposable (expr);
4640 local_copy = new TemporaryVariable (expr_type, loc);
4641 local_copy.Resolve (ec);
4647 // Emits the code for the case of using using a local variable declaration.
4649 void EmitLocalVariableDecls (EmitContext ec)
4651 ILGenerator ig = ec.ig;
4654 for (i = 0; i < assign.Length; i++) {
4655 ExpressionStatement es = assign [i] as ExpressionStatement;
4658 es.EmitStatement (ec);
4660 assign [i].Emit (ec);
4661 ig.Emit (OpCodes.Pop);
4665 ig.BeginExceptionBlock ();
4667 Statement.Emit (ec);
4669 var_list.Reverse ();
4674 void EmitLocalVariableDeclFinally (EmitContext ec)
4676 ILGenerator ig = ec.ig;
4678 int i = assign.Length;
4679 for (int ii = 0; ii < var_list.Count; ++ii){
4680 Expression var = resolved_vars [--i];
4681 Label skip = ig.DefineLabel ();
4684 ig.BeginFinallyBlock ();
4686 if (!var.Type.IsValueType) {
4688 ig.Emit (OpCodes.Brfalse, skip);
4689 converted_vars [i].Emit (ec);
4690 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4692 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4694 if (!(ml is MethodGroupExpr)) {
4696 ig.Emit (OpCodes.Box, var.Type);
4697 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4699 MethodInfo mi = null;
4701 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4702 if (TypeManager.GetParameterData (mk).Count == 0) {
4709 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4713 IMemoryLocation mloc = (IMemoryLocation) var;
4715 mloc.AddressOf (ec, AddressOp.Load);
4716 ig.Emit (OpCodes.Call, mi);
4720 ig.MarkLabel (skip);
4723 ig.EndExceptionBlock ();
4725 ig.BeginFinallyBlock ();
4730 void EmitExpression (EmitContext ec)
4733 // Make a copy of the expression and operate on that.
4735 ILGenerator ig = ec.ig;
4737 local_copy.Store (ec, expr);
4740 ig.BeginExceptionBlock ();
4742 Statement.Emit (ec);
4746 ig.EndExceptionBlock ();
4749 void EmitExpressionFinally (EmitContext ec)
4751 ILGenerator ig = ec.ig;
4752 if (!expr_type.IsValueType) {
4753 Label skip = ig.DefineLabel ();
4754 local_copy.Emit (ec);
4755 ig.Emit (OpCodes.Brfalse, skip);
4756 local_copy.Emit (ec);
4757 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4758 ig.MarkLabel (skip);
4760 Expression ml = Expression.MemberLookup (
4761 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4762 "Dispose", Location.Null);
4764 if (!(ml is MethodGroupExpr)) {
4765 local_copy.Emit (ec);
4766 ig.Emit (OpCodes.Box, expr_type);
4767 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4769 MethodInfo mi = null;
4771 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4772 if (TypeManager.GetParameterData (mk).Count == 0) {
4779 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4783 local_copy.AddressOf (ec, AddressOp.Load);
4784 ig.Emit (OpCodes.Call, mi);
4789 public override bool Resolve (EmitContext ec)
4791 if (expression_or_block is DictionaryEntry){
4792 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4793 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4795 if (!ResolveLocalVariableDecls (ec))
4798 } else if (expression_or_block is Expression){
4799 expr = (Expression) expression_or_block;
4801 expr = expr.Resolve (ec);
4805 expr_type = expr.Type;
4807 if (!ResolveExpression (ec))
4811 FlowBranchingException branching = ec.StartFlowBranching (this);
4813 bool ok = Statement.Resolve (ec);
4815 ResolveFinally (branching);
4817 ec.EndFlowBranching ();
4819 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4820 // So, ensure there's some IL code after this statement.
4821 ec.NeedReturnLabel ();
4823 if (TypeManager.void_dispose_void == null) {
4824 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4825 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4831 protected override void DoEmit (EmitContext ec)
4833 if (expression_or_block is DictionaryEntry)
4834 EmitLocalVariableDecls (ec);
4835 else if (expression_or_block is Expression)
4836 EmitExpression (ec);
4839 public override void EmitFinallyBody (EmitContext ec)
4841 if (expression_or_block is DictionaryEntry)
4842 EmitLocalVariableDeclFinally (ec);
4843 else if (expression_or_block is Expression)
4844 EmitExpressionFinally (ec);
4847 protected override void CloneTo (CloneContext clonectx, Statement t)
4849 Using target = (Using) t;
4851 if (expression_or_block is Expression) {
4852 target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
4854 DictionaryEntry de = (DictionaryEntry) expression_or_block;
4855 ArrayList var_list = (ArrayList) de.Value;
4856 ArrayList target_var_list = new ArrayList (var_list.Count);
4858 foreach (DictionaryEntry de_variable in var_list)
4859 target_var_list.Add (new DictionaryEntry (
4860 ((Expression) de_variable.Key).Clone (clonectx),
4861 ((Expression) de_variable.Value).Clone (clonectx)));
4863 target.expression_or_block = new DictionaryEntry (
4864 ((Expression) de.Key).Clone (clonectx),
4868 target.Statement = Statement.Clone (clonectx);
4873 /// Implementation of the foreach C# statement
4875 public class Foreach : Statement {
4877 Expression variable;
4879 Statement statement;
4881 CollectionForeach collection;
4883 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4884 Statement stmt, Location l)
4887 this.variable = var;
4893 public Statement Statement {
4894 get { return statement; }
4897 public override bool Resolve (EmitContext ec)
4899 expr = expr.Resolve (ec);
4904 Report.Error (186, loc, "Use of null is not valid in this context");
4908 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4909 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4910 expr.ExprClassName);
4915 // We need an instance variable. Not sure this is the best
4916 // way of doing this.
4918 // FIXME: When we implement propertyaccess, will those turn
4919 // out to return values in ExprClass? I think they should.
4921 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4922 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4923 collection.Error_Enumerator ();
4927 if (expr.Type.IsArray) {
4928 array = new ArrayForeach (type, variable, expr, statement, loc);
4929 return array.Resolve (ec);
4932 collection = new CollectionForeach (type, variable, expr, statement, loc);
4933 return collection.Resolve (ec);
4936 protected override void DoEmit (EmitContext ec)
4938 ILGenerator ig = ec.ig;
4940 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4941 ec.LoopBegin = ig.DefineLabel ();
4942 ec.LoopEnd = ig.DefineLabel ();
4944 if (collection != null)
4945 collection.Emit (ec);
4949 ec.LoopBegin = old_begin;
4950 ec.LoopEnd = old_end;
4953 protected class ArrayCounter : TemporaryVariable
4955 public ArrayCounter (Location loc)
4956 : base (TypeManager.int32_type, loc)
4959 public void Initialize (EmitContext ec)
4962 ec.ig.Emit (OpCodes.Ldc_I4_0);
4966 public void Increment (EmitContext ec)
4970 ec.ig.Emit (OpCodes.Ldc_I4_1);
4971 ec.ig.Emit (OpCodes.Add);
4976 protected class ArrayForeach : Statement
4978 Expression variable, expr, conv;
4979 Statement statement;
4981 Expression var_type;
4982 TemporaryVariable[] lengths;
4983 ArrayCounter[] counter;
4986 TemporaryVariable copy;
4989 public ArrayForeach (Expression var_type, Expression var,
4990 Expression expr, Statement stmt, Location l)
4992 this.var_type = var_type;
4993 this.variable = var;
4999 public override bool Resolve (EmitContext ec)
5001 array_type = expr.Type;
5002 rank = array_type.GetArrayRank ();
5004 copy = new TemporaryVariable (array_type, loc);
5007 counter = new ArrayCounter [rank];
5008 lengths = new TemporaryVariable [rank];
5010 ArrayList list = new ArrayList ();
5011 for (int i = 0; i < rank; i++) {
5012 counter [i] = new ArrayCounter (loc);
5013 counter [i].Resolve (ec);
5015 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5016 lengths [i].Resolve (ec);
5018 list.Add (counter [i]);
5021 access = new ElementAccess (copy, list).Resolve (ec);
5025 VarExpr ve = var_type as VarExpr;
5027 // Infer implicitly typed local variable from foreach array type
5028 var_type = new TypeExpression (access.Type, ve.Location);
5031 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5032 if (var_type == null)
5035 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5041 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5042 ec.CurrentBranching.CreateSibling ();
5044 variable = variable.ResolveLValue (ec, conv, loc);
5045 if (variable == null)
5048 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5049 if (!statement.Resolve (ec))
5051 ec.EndFlowBranching ();
5053 // There's no direct control flow from the end of the embedded statement to the end of the loop
5054 ec.CurrentBranching.CurrentUsageVector.Goto ();
5056 ec.EndFlowBranching ();
5061 protected override void DoEmit (EmitContext ec)
5063 ILGenerator ig = ec.ig;
5065 copy.Store (ec, expr);
5067 Label[] test = new Label [rank];
5068 Label[] loop = new Label [rank];
5070 for (int i = 0; i < rank; i++) {
5071 test [i] = ig.DefineLabel ();
5072 loop [i] = ig.DefineLabel ();
5074 lengths [i].EmitThis (ec);
5075 ((ArrayAccess) access).EmitGetLength (ec, i);
5076 lengths [i].EmitStore (ec);
5079 for (int i = 0; i < rank; i++) {
5080 counter [i].Initialize (ec);
5082 ig.Emit (OpCodes.Br, test [i]);
5083 ig.MarkLabel (loop [i]);
5086 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5088 statement.Emit (ec);
5090 ig.MarkLabel (ec.LoopBegin);
5092 for (int i = rank - 1; i >= 0; i--){
5093 counter [i].Increment (ec);
5095 ig.MarkLabel (test [i]);
5096 counter [i].Emit (ec);
5097 lengths [i].Emit (ec);
5098 ig.Emit (OpCodes.Blt, loop [i]);
5101 ig.MarkLabel (ec.LoopEnd);
5105 protected class CollectionForeach : Statement
5107 Expression variable, expr;
5108 Statement statement;
5110 TemporaryVariable enumerator;
5114 MethodGroupExpr get_enumerator;
5115 PropertyExpr get_current;
5116 MethodInfo move_next;
5117 Expression var_type;
5118 Type enumerator_type;
5119 bool enumerator_found;
5121 public CollectionForeach (Expression var_type, Expression var,
5122 Expression expr, Statement stmt, Location l)
5124 this.var_type = var_type;
5125 this.variable = var;
5131 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5133 Type return_type = mi.ReturnType;
5135 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5137 // Apply the same optimization as MS: skip the GetEnumerator
5138 // returning an IEnumerator, and use the one returning a
5139 // CharEnumerator instead. This allows us to avoid the
5140 // try-finally block and the boxing.
5145 // Ok, we can access it, now make sure that we can do something
5146 // with this `GetEnumerator'
5149 if (return_type == TypeManager.ienumerator_type ||
5150 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5151 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5153 // If it is not an interface, lets try to find the methods ourselves.
5154 // For example, if we have:
5155 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5156 // We can avoid the iface call. This is a runtime perf boost.
5157 // even bigger if we have a ValueType, because we avoid the cost
5160 // We have to make sure that both methods exist for us to take
5161 // this path. If one of the methods does not exist, we will just
5162 // use the interface. Sadly, this complex if statement is the only
5163 // way I could do this without a goto
5166 if (TypeManager.bool_movenext_void == null) {
5167 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5168 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5171 if (TypeManager.ienumerator_getcurrent == null) {
5172 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5173 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5178 // Prefer a generic enumerator over a non-generic one.
5180 if (return_type.IsInterface && return_type.IsGenericType) {
5181 enumerator_type = return_type;
5182 if (!FetchGetCurrent (ec, return_type))
5183 get_current = new PropertyExpr (
5184 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5185 if (!FetchMoveNext (return_type))
5186 move_next = TypeManager.bool_movenext_void;
5191 if (return_type.IsInterface ||
5192 !FetchMoveNext (return_type) ||
5193 !FetchGetCurrent (ec, return_type)) {
5194 enumerator_type = return_type;
5195 move_next = TypeManager.bool_movenext_void;
5196 get_current = new PropertyExpr (
5197 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5202 // Ok, so they dont return an IEnumerable, we will have to
5203 // find if they support the GetEnumerator pattern.
5206 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5207 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",
5208 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5213 enumerator_type = return_type;
5219 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5221 bool FetchMoveNext (Type t)
5223 MemberList move_next_list;
5225 move_next_list = TypeContainer.FindMembers (
5226 t, MemberTypes.Method,
5227 BindingFlags.Public | BindingFlags.Instance,
5228 Type.FilterName, "MoveNext");
5229 if (move_next_list.Count == 0)
5232 foreach (MemberInfo m in move_next_list){
5233 MethodInfo mi = (MethodInfo) m;
5235 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5236 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5246 // Retrieves a `public T get_Current ()' method from the Type `t'
5248 bool FetchGetCurrent (EmitContext ec, Type t)
5250 PropertyExpr pe = Expression.MemberLookup (
5251 ec.ContainerType, t, "Current", MemberTypes.Property,
5252 Expression.AllBindingFlags, loc) as PropertyExpr;
5261 // Retrieves a `public void Dispose ()' method from the Type `t'
5263 static MethodInfo FetchMethodDispose (Type t)
5265 MemberList dispose_list;
5267 dispose_list = TypeContainer.FindMembers (
5268 t, MemberTypes.Method,
5269 BindingFlags.Public | BindingFlags.Instance,
5270 Type.FilterName, "Dispose");
5271 if (dispose_list.Count == 0)
5274 foreach (MemberInfo m in dispose_list){
5275 MethodInfo mi = (MethodInfo) m;
5277 if (TypeManager.GetParameterData (mi).Count == 0){
5278 if (mi.ReturnType == TypeManager.void_type)
5285 public void Error_Enumerator ()
5287 if (enumerator_found) {
5291 Report.Error (1579, loc,
5292 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5293 TypeManager.CSharpName (expr.Type));
5296 bool IsOverride (MethodInfo m)
5298 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5300 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5302 if (m is MethodBuilder)
5305 MethodInfo base_method = m.GetBaseDefinition ();
5306 return base_method != m;
5309 bool TryType (EmitContext ec, Type t)
5311 MethodGroupExpr mg = Expression.MemberLookup (
5312 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5313 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5317 MethodInfo result = null;
5318 MethodInfo tmp_move_next = null;
5319 PropertyExpr tmp_get_cur = null;
5320 Type tmp_enumerator_type = enumerator_type;
5321 foreach (MethodInfo mi in mg.Methods) {
5322 if (TypeManager.GetParameterData (mi).Count != 0)
5325 // Check whether GetEnumerator is public
5326 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5329 if (IsOverride (mi))
5332 enumerator_found = true;
5334 if (!GetEnumeratorFilter (ec, mi))
5337 if (result != null) {
5338 if (TypeManager.IsGenericType (result.ReturnType)) {
5339 if (!TypeManager.IsGenericType (mi.ReturnType))
5342 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5343 Report.SymbolRelatedToPreviousError (t);
5344 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5345 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5346 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5350 // Always prefer generics enumerators
5351 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5352 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5353 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5356 Report.SymbolRelatedToPreviousError (result);
5357 Report.SymbolRelatedToPreviousError (mi);
5358 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5359 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5364 tmp_move_next = move_next;
5365 tmp_get_cur = get_current;
5366 tmp_enumerator_type = enumerator_type;
5367 if (mi.DeclaringType == t)
5371 if (result != null) {
5372 move_next = tmp_move_next;
5373 get_current = tmp_get_cur;
5374 enumerator_type = tmp_enumerator_type;
5375 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5376 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5378 if (t != expr.Type) {
5379 expr = Convert.ExplicitConversion (
5382 throw new InternalErrorException ();
5385 get_enumerator.InstanceExpression = expr;
5386 get_enumerator.IsBase = t != expr.Type;
5394 bool ProbeCollectionType (EmitContext ec, Type t)
5396 int errors = Report.Errors;
5397 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5398 if (TryType (ec, tt))
5403 if (Report.Errors > errors)
5407 // Now try to find the method in the interfaces
5409 Type [] ifaces = TypeManager.GetInterfaces (t);
5410 foreach (Type i in ifaces){
5411 if (TryType (ec, i))
5418 public override bool Resolve (EmitContext ec)
5420 enumerator_type = TypeManager.ienumerator_type;
5422 if (!ProbeCollectionType (ec, expr.Type)) {
5423 Error_Enumerator ();
5427 bool is_disposable = !enumerator_type.IsSealed ||
5428 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5430 VarExpr ve = var_type as VarExpr;
5432 // Infer implicitly typed local variable from foreach enumerable type
5433 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5436 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5437 if (var_type == null)
5440 enumerator = new TemporaryVariable (enumerator_type, loc);
5441 enumerator.Resolve (ec);
5443 init = new Invocation (get_enumerator, null);
5444 init = init.Resolve (ec);
5448 Expression move_next_expr;
5450 MemberInfo[] mi = new MemberInfo[] { move_next };
5451 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5452 mg.InstanceExpression = enumerator;
5454 move_next_expr = new Invocation (mg, null);
5457 get_current.InstanceExpression = enumerator;
5459 Statement block = new CollectionForeachStatement (
5460 var_type.Type, variable, get_current, statement, loc);
5462 loop = new While (move_next_expr, block, loc);
5465 loop = new DisposableWrapper (this, loop);
5467 return loop.Resolve (ec);
5470 protected override void DoEmit (EmitContext ec)
5472 enumerator.Store (ec, init);
5476 class DisposableWrapper : ExceptionStatement {
5477 CollectionForeach parent;
5480 internal DisposableWrapper (CollectionForeach parent, Statement loop)
5482 this.parent = parent;
5486 public override bool Resolve (EmitContext ec)
5490 FlowBranchingException branching = ec.StartFlowBranching (this);
5492 if (!loop.Resolve (ec))
5495 ResolveFinally (branching);
5496 ec.EndFlowBranching ();
5498 if (TypeManager.void_dispose_void == null) {
5499 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5500 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5505 protected override void DoEmit (EmitContext ec)
5507 ILGenerator ig = ec.ig;
5510 ig.BeginExceptionBlock ();
5514 ig.EndExceptionBlock ();
5517 public override void EmitFinallyBody (EmitContext ec)
5519 parent.EmitFinallyBody (ec);
5523 void EmitFinallyBody (EmitContext ec)
5525 ILGenerator ig = ec.ig;
5527 if (enumerator_type.IsValueType) {
5528 MethodInfo mi = FetchMethodDispose (enumerator_type);
5530 enumerator.EmitLoadAddress (ec);
5531 ig.Emit (OpCodes.Call, mi);
5533 enumerator.Emit (ec);
5534 ig.Emit (OpCodes.Box, enumerator_type);
5535 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5538 Label call_dispose = ig.DefineLabel ();
5540 enumerator.Emit (ec);
5541 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5542 ig.Emit (OpCodes.Dup);
5543 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5544 ig.Emit (OpCodes.Pop);
5546 Label end_finally = ig.DefineLabel ();
5547 ig.Emit (OpCodes.Br, end_finally);
5549 ig.MarkLabel (call_dispose);
5550 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5551 ig.MarkLabel (end_finally);
5556 protected class CollectionForeachStatement : Statement
5559 Expression variable, current, conv;
5560 Statement statement;
5563 public CollectionForeachStatement (Type type, Expression variable,
5564 Expression current, Statement statement,
5568 this.variable = variable;
5569 this.current = current;
5570 this.statement = statement;
5574 public override bool Resolve (EmitContext ec)
5576 current = current.Resolve (ec);
5577 if (current == null)
5580 conv = Convert.ExplicitConversion (ec, current, type, loc);
5584 assign = new Assign (variable, conv, loc);
5585 if (assign.Resolve (ec) == null)
5588 if (!statement.Resolve (ec))
5594 protected override void DoEmit (EmitContext ec)
5596 assign.EmitStatement (ec);
5597 statement.Emit (ec);
5601 protected override void CloneTo (CloneContext clonectx, Statement t)
5603 Foreach target = (Foreach) t;
5605 target.type = type.Clone (clonectx);
5606 target.variable = variable.Clone (clonectx);
5607 target.expr = expr.Clone (clonectx);
5608 target.statement = statement.Clone (clonectx);