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);
744 // A 'return' or a 'yield break'
745 public abstract class ExitStatement : Statement
747 protected bool unwind_protect;
748 protected abstract bool DoResolve (EmitContext ec);
750 public virtual void Error_FinallyClause ()
752 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
755 public sealed override bool Resolve (EmitContext ec)
760 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
762 ec.NeedReturnLabel ();
763 ec.CurrentBranching.CurrentUsageVector.Goto ();
769 /// Implements the return statement
771 public class Return : ExitStatement {
772 protected Expression Expr;
773 public Return (Expression expr, Location l)
779 protected override bool DoResolve (EmitContext ec)
782 if (ec.ReturnType == TypeManager.void_type)
785 Error (126, "An object of a type convertible to `{0}' is required " +
786 "for the return statement",
787 TypeManager.CSharpName (ec.ReturnType));
791 AnonymousContainer am = ec.CurrentAnonymousMethod;
792 if ((am != null) && am.IsIterator && ec.InIterator) {
793 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
794 "statement to return a value, or yield break to end the iteration");
797 if (am == null && ec.ReturnType == TypeManager.void_type) {
798 MemberCore mc = ec.ResolveContext as MemberCore;
799 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
800 mc.GetSignatureForError ());
803 Expr = Expr.Resolve (ec);
807 if (Expr.Type != ec.ReturnType) {
808 if (ec.InferReturnType) {
810 // void cannot be used in contextual return
812 if (Expr.Type == TypeManager.void_type)
815 ec.ReturnType = Expr.Type;
817 Expr = Convert.ImplicitConversionRequired (
818 ec, Expr, ec.ReturnType, loc);
822 Report.Error (1662, loc,
823 "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",
824 am.ContainerType, am.GetSignatureForError ());
834 protected override void DoEmit (EmitContext ec)
840 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
844 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
846 ec.ig.Emit (OpCodes.Ret);
849 protected override void CloneTo (CloneContext clonectx, Statement t)
851 Return target = (Return) t;
852 // It's null for simple return;
854 target.Expr = Expr.Clone (clonectx);
858 public class Goto : Statement {
860 LabeledStatement label;
863 public override bool Resolve (EmitContext ec)
865 int errors = Report.Errors;
866 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
867 ec.CurrentBranching.CurrentUsageVector.Goto ();
868 return errors == Report.Errors;
871 public Goto (string label, Location l)
877 public string Target {
878 get { return target; }
881 public void SetResolvedTarget (LabeledStatement label)
884 label.AddReference ();
887 protected override void DoEmit (EmitContext ec)
890 throw new InternalErrorException ("goto emitted before target resolved");
891 Label l = label.LabelTarget (ec);
892 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
896 public class LabeledStatement : Statement {
903 FlowBranching.UsageVector vectors;
905 public LabeledStatement (string name, Location l)
911 public Label LabelTarget (EmitContext ec)
916 label = ec.ig.DefineLabel ();
926 public bool IsDefined {
927 get { return defined; }
930 public bool HasBeenReferenced {
931 get { return referenced; }
934 public FlowBranching.UsageVector JumpOrigins {
935 get { return vectors; }
938 public void AddUsageVector (FlowBranching.UsageVector vector)
940 vector = vector.Clone ();
941 vector.Next = vectors;
945 public override bool Resolve (EmitContext ec)
947 // this flow-branching will be terminated when the surrounding block ends
948 ec.StartFlowBranching (this);
952 protected override void DoEmit (EmitContext ec)
954 if (ig != null && ig != ec.ig)
955 throw new InternalErrorException ("cannot happen");
957 ec.ig.MarkLabel (label);
960 public void AddReference ()
968 /// `goto default' statement
970 public class GotoDefault : Statement {
972 public GotoDefault (Location l)
977 public override bool Resolve (EmitContext ec)
979 ec.CurrentBranching.CurrentUsageVector.Goto ();
983 protected override void DoEmit (EmitContext ec)
985 if (ec.Switch == null){
986 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
990 if (!ec.Switch.GotDefault){
991 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
994 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
999 /// `goto case' statement
1001 public class GotoCase : Statement {
1005 public GotoCase (Expression e, Location l)
1011 public override bool Resolve (EmitContext ec)
1013 if (ec.Switch == null){
1014 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1018 expr = expr.Resolve (ec);
1022 Constant c = expr as Constant;
1024 Error (150, "A constant value is expected");
1028 Type type = ec.Switch.SwitchType;
1029 if (!Convert.ImplicitStandardConversionExists (c, type))
1030 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1031 "convertible to type `{0}'", TypeManager.CSharpName (type));
1034 object val = c.GetValue ();
1035 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1036 val = TypeManager.ChangeType (val, type, out fail);
1039 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1040 c.GetSignatureForError (), TypeManager.CSharpName (type));
1045 val = SwitchLabel.NullStringCase;
1047 sl = (SwitchLabel) ec.Switch.Elements [val];
1050 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1051 (c.GetValue () == null ? "null" : val.ToString ()));
1055 ec.CurrentBranching.CurrentUsageVector.Goto ();
1059 protected override void DoEmit (EmitContext ec)
1061 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1064 protected override void CloneTo (CloneContext clonectx, Statement t)
1066 GotoCase target = (GotoCase) t;
1068 target.expr = expr.Clone (clonectx);
1069 target.sl = sl.Clone (clonectx);
1073 public class Throw : Statement {
1076 public Throw (Expression expr, Location l)
1082 public override bool Resolve (EmitContext ec)
1084 ec.CurrentBranching.CurrentUsageVector.Goto ();
1087 return ec.CurrentBranching.CheckRethrow (loc);
1089 expr = expr.Resolve (ec);
1093 ExprClass eclass = expr.eclass;
1095 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1096 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1097 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1103 if ((t != TypeManager.exception_type) &&
1104 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1105 !(expr is NullLiteral)) {
1106 Error (155, "The type caught or thrown must be derived from System.Exception");
1112 protected override void DoEmit (EmitContext ec)
1115 ec.ig.Emit (OpCodes.Rethrow);
1119 ec.ig.Emit (OpCodes.Throw);
1123 protected override void CloneTo (CloneContext clonectx, Statement t)
1125 Throw target = (Throw) t;
1128 target.expr = expr.Clone (clonectx);
1132 public class Break : Statement {
1134 public Break (Location l)
1139 bool unwind_protect;
1141 public override bool Resolve (EmitContext ec)
1143 int errors = Report.Errors;
1144 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1145 ec.CurrentBranching.CurrentUsageVector.Goto ();
1146 return errors == Report.Errors;
1149 protected override void DoEmit (EmitContext ec)
1151 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1154 protected override void CloneTo (CloneContext clonectx, Statement t)
1160 public class Continue : Statement {
1162 public Continue (Location l)
1167 bool unwind_protect;
1169 public override bool Resolve (EmitContext ec)
1171 int errors = Report.Errors;
1172 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1173 ec.CurrentBranching.CurrentUsageVector.Goto ();
1174 return errors == Report.Errors;
1177 protected override void DoEmit (EmitContext ec)
1179 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1182 protected override void CloneTo (CloneContext clonectx, Statement t)
1188 public abstract class Variable
1190 public abstract Type Type {
1194 public abstract bool HasInstance {
1198 public abstract bool NeedsTemporary {
1202 public abstract void EmitInstance (EmitContext ec);
1204 public abstract void Emit (EmitContext ec);
1206 public abstract void EmitAssign (EmitContext ec);
1208 public abstract void EmitAddressOf (EmitContext ec);
1211 public interface IKnownVariable {
1212 Block Block { get; }
1213 Location Location { get; }
1217 // The information about a user-perceived local variable
1219 public class LocalInfo : IKnownVariable {
1220 public Expression Type;
1222 public Type VariableType;
1223 public readonly string Name;
1224 public readonly Location Location;
1225 public readonly Block Block;
1227 public VariableInfo VariableInfo;
1230 public Variable Variable {
1242 CompilerGenerated = 64,
1246 public enum ReadOnlyContext: byte {
1253 ReadOnlyContext ro_context;
1254 LocalBuilder builder;
1256 public LocalInfo (Expression type, string name, Block block, Location l)
1264 public LocalInfo (DeclSpace ds, Block block, Location l)
1266 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1271 public void ResolveVariable (EmitContext ec)
1273 Block theblock = Block;
1274 if (theblock.ScopeInfo != null)
1275 var = theblock.ScopeInfo.GetCapturedVariable (this);
1280 // This is needed to compile on both .NET 1.x and .NET 2.x
1281 // the later introduced `DeclareLocal (Type t, bool pinned)'
1283 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1285 builder = ec.ig.DeclareLocal (VariableType);
1287 var = new LocalVariable (this, builder);
1291 public void EmitSymbolInfo (EmitContext ec, string name)
1293 if (builder != null)
1294 ec.DefineLocalVariable (Name, builder);
1297 public bool IsThisAssigned (EmitContext ec)
1299 if (VariableInfo == null)
1300 throw new Exception ();
1302 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1305 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1308 public bool IsAssigned (EmitContext ec)
1310 if (VariableInfo == null)
1311 throw new Exception ();
1313 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1316 public bool Resolve (EmitContext ec)
1318 if (VariableType == null) {
1319 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1323 VariableType = texpr.Type;
1326 if (TypeManager.IsGenericParameter (VariableType))
1329 if (VariableType == TypeManager.void_type) {
1330 Expression.Error_VoidInvalidInTheContext (Location);
1334 if (VariableType.IsAbstract && VariableType.IsSealed) {
1335 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1339 if (VariableType.IsPointer && !ec.InUnsafe)
1340 Expression.UnsafeError (Location);
1345 public bool IsCaptured {
1346 get { return (flags & Flags.Captured) != 0; }
1347 set { flags |= Flags.Captured; }
1350 public bool IsConstant {
1351 get { return (flags & Flags.IsConstant) != 0; }
1352 set { flags |= Flags.IsConstant; }
1355 public bool AddressTaken {
1356 get { return (flags & Flags.AddressTaken) != 0; }
1357 set { flags |= Flags.AddressTaken; }
1360 public bool CompilerGenerated {
1361 get { return (flags & Flags.CompilerGenerated) != 0; }
1362 set { flags |= Flags.CompilerGenerated; }
1365 public override string ToString ()
1367 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1368 Name, Type, VariableInfo, Location);
1372 get { return (flags & Flags.Used) != 0; }
1373 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1376 public bool ReadOnly {
1377 get { return (flags & Flags.ReadOnly) != 0; }
1380 public void SetReadOnlyContext (ReadOnlyContext context)
1382 flags |= Flags.ReadOnly;
1383 ro_context = context;
1386 public string GetReadOnlyContext ()
1389 throw new InternalErrorException ("Variable is not readonly");
1391 switch (ro_context) {
1392 case ReadOnlyContext.Fixed:
1393 return "fixed variable";
1394 case ReadOnlyContext.Foreach:
1395 return "foreach iteration variable";
1396 case ReadOnlyContext.Using:
1397 return "using variable";
1399 throw new NotImplementedException ();
1403 // Whether the variable is pinned, if Pinned the variable has been
1404 // allocated in a pinned slot with DeclareLocal.
1406 public bool Pinned {
1407 get { return (flags & Flags.Pinned) != 0; }
1408 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1411 public bool IsThis {
1412 get { return (flags & Flags.IsThis) != 0; }
1413 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1416 Block IKnownVariable.Block {
1417 get { return Block; }
1420 Location IKnownVariable.Location {
1421 get { return Location; }
1424 protected class LocalVariable : Variable
1426 public readonly LocalInfo LocalInfo;
1427 LocalBuilder builder;
1429 public LocalVariable (LocalInfo local, LocalBuilder builder)
1431 this.LocalInfo = local;
1432 this.builder = builder;
1435 public override Type Type {
1436 get { return LocalInfo.VariableType; }
1439 public override bool HasInstance {
1440 get { return false; }
1443 public override bool NeedsTemporary {
1444 get { return false; }
1447 public override void EmitInstance (EmitContext ec)
1452 public override void Emit (EmitContext ec)
1454 ec.ig.Emit (OpCodes.Ldloc, builder);
1457 public override void EmitAssign (EmitContext ec)
1459 ec.ig.Emit (OpCodes.Stloc, builder);
1462 public override void EmitAddressOf (EmitContext ec)
1464 ec.ig.Emit (OpCodes.Ldloca, builder);
1468 public LocalInfo Clone (CloneContext clonectx)
1471 // Variables in anonymous block are not resolved yet
1473 if (VariableType == null)
1474 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1477 // Variables in method block are resolved
1479 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1480 li.VariableType = VariableType;
1486 /// Block represents a C# block.
1490 /// This class is used in a number of places: either to represent
1491 /// explicit blocks that the programmer places or implicit blocks.
1493 /// Implicit blocks are used as labels or to introduce variable
1496 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1497 /// they contain extra information that is not necessary on normal blocks.
1499 public class Block : Statement {
1500 public Block Parent;
1501 public readonly Location StartLocation;
1502 public Location EndLocation = Location.Null;
1504 public ExplicitBlock Explicit;
1505 public ToplevelBlock Toplevel;
1508 public enum Flags : byte {
1511 VariablesInitialized = 4,
1515 HasVarargs = 64, // Used in ToplevelBlock
1518 protected Flags flags;
1520 public bool Unchecked {
1521 get { return (flags & Flags.Unchecked) != 0; }
1522 set { flags |= Flags.Unchecked; }
1525 public bool Unsafe {
1526 get { return (flags & Flags.Unsafe) != 0; }
1527 set { flags |= Flags.Unsafe; }
1531 // The statements in this block
1533 protected ArrayList statements;
1537 // An array of Blocks. We keep track of children just
1538 // to generate the local variable declarations.
1540 // Statements and child statements are handled through the
1546 // Labels. (label, block) pairs.
1548 HybridDictionary labels;
1551 // Keeps track of (name, type) pairs
1553 IDictionary variables;
1556 // Keeps track of constants
1557 HybridDictionary constants;
1560 // Temporary variables.
1562 ArrayList temporary_variables;
1565 // If this is a switch section, the enclosing switch block.
1569 // TODO: merge with scope_initializers
1570 ExpressionStatement scope_init;
1571 ArrayList scope_initializers;
1573 ArrayList anonymous_children;
1575 protected static int id;
1579 int assignable_slots;
1580 protected ScopeInfo scope_info;
1581 bool unreachable_shown;
1584 public Block (Block parent)
1585 : this (parent, (Flags) 0, Location.Null, Location.Null)
1588 public Block (Block parent, Flags flags)
1589 : this (parent, flags, Location.Null, Location.Null)
1592 public Block (Block parent, Location start, Location end)
1593 : this (parent, (Flags) 0, start, end)
1596 public Block (Block parent, Flags flags, Location start, Location end)
1598 if (parent != null) {
1599 parent.AddChild (this);
1601 // the appropriate constructors will fixup these fields
1602 Toplevel = parent.Toplevel;
1603 Explicit = parent.Explicit;
1606 this.Parent = parent;
1608 this.StartLocation = start;
1609 this.EndLocation = end;
1612 statements = new ArrayList (4);
1615 public Block CreateSwitchBlock (Location start)
1617 // FIXME: should this be implicit?
1618 Block new_block = new ExplicitBlock (this, start, start);
1619 new_block.switch_block = this;
1624 get { return this_id; }
1627 public IDictionary Variables {
1629 if (variables == null)
1630 variables = new ListDictionary ();
1635 void AddChild (Block b)
1637 if (children == null)
1638 children = new ArrayList (1);
1643 public void SetEndLocation (Location loc)
1648 protected static void Error_158 (string name, Location loc)
1650 Report.Error (158, loc, "The label `{0}' shadows another label " +
1651 "by the same name in a contained scope", name);
1655 /// Adds a label to the current block.
1659 /// false if the name already exists in this block. true
1663 public bool AddLabel (LabeledStatement target)
1665 if (switch_block != null)
1666 return switch_block.AddLabel (target);
1668 string name = target.Name;
1671 while (cur != null) {
1672 LabeledStatement s = cur.DoLookupLabel (name);
1674 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1675 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1679 if (this == Explicit)
1685 while (cur != null) {
1686 if (cur.DoLookupLabel (name) != null) {
1687 Error_158 (name, target.loc);
1691 if (children != null) {
1692 foreach (Block b in children) {
1693 LabeledStatement s = b.DoLookupLabel (name);
1697 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1698 Error_158 (name, target.loc);
1706 Toplevel.CheckError158 (name, target.loc);
1709 labels = new HybridDictionary();
1711 labels.Add (name, target);
1715 public LabeledStatement LookupLabel (string name)
1717 LabeledStatement s = DoLookupLabel (name);
1721 if (children == null)
1724 foreach (Block child in children) {
1725 if (Explicit != child.Explicit)
1728 s = child.LookupLabel (name);
1736 LabeledStatement DoLookupLabel (string name)
1738 if (switch_block != null)
1739 return switch_block.LookupLabel (name);
1742 if (labels.Contains (name))
1743 return ((LabeledStatement) labels [name]);
1748 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1751 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1752 while (kvi == null) {
1753 b = b.Explicit.Parent;
1756 kvi = b.Explicit.GetKnownVariable (name);
1762 // Is kvi.Block nested inside 'b'
1763 if (b.Explicit != kvi.Block.Explicit) {
1765 // If a variable by the same name it defined in a nested block of this
1766 // block, we violate the invariant meaning in a block.
1769 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1770 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1775 // It's ok if the definition is in a nested subblock of b, but not
1776 // nested inside this block -- a definition in a sibling block
1777 // should not affect us.
1783 // Block 'b' and kvi.Block are the same textual block.
1784 // However, different variables are extant.
1786 // Check if the variable is in scope in both blocks. We use
1787 // an indirect check that depends on AddVariable doing its
1788 // part in maintaining the invariant-meaning-in-block property.
1790 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1794 // Even though we detected the error when the name is used, we
1795 // treat it as if the variable declaration was in error.
1797 Report.SymbolRelatedToPreviousError (loc, name);
1798 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1802 public LocalInfo AddVariable (Expression type, string name, Location l)
1804 LocalInfo vi = GetLocalInfo (name);
1806 Report.SymbolRelatedToPreviousError (vi.Location, name);
1807 if (Explicit == vi.Block.Explicit)
1808 Error_AlreadyDeclared (l, name, null);
1810 Error_AlreadyDeclared (l, name, "parent");
1814 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1816 Report.SymbolRelatedToPreviousError (pi.Location, name);
1817 Error_AlreadyDeclared (loc, name,
1818 pi.Block == Toplevel ? "method argument" : "parent or current");
1822 if (Toplevel.GenericMethod != null) {
1823 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1824 if (tp.Name == name) {
1825 Report.SymbolRelatedToPreviousError (tp);
1826 Error_AlreadyDeclaredTypeParameter (loc, name);
1832 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1834 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1835 Error_AlreadyDeclared (l, name, "child");
1839 vi = new LocalInfo (type, name, this, l);
1842 if ((flags & Flags.VariablesInitialized) != 0)
1843 throw new InternalErrorException ("block has already been resolved");
1848 protected virtual void AddVariable (LocalInfo li)
1850 Variables.Add (li.Name, li);
1851 Explicit.AddKnownVariable (li.Name, li);
1854 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1856 if (reason == null) {
1857 Error_AlreadyDeclared (loc, var);
1861 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1862 "in this scope because it would give a different meaning " +
1863 "to `{0}', which is already used in a `{1}' scope " +
1864 "to denote something else", var, reason);
1867 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1869 Report.Error (128, loc,
1870 "A local variable named `{0}' is already defined in this scope", name);
1873 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1875 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1878 public bool AddConstant (Expression type, string name, Expression value, Location l)
1880 if (AddVariable (type, name, l) == null)
1883 if (constants == null)
1884 constants = new HybridDictionary();
1886 constants.Add (name, value);
1888 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1893 static int next_temp_id = 0;
1895 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1897 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1899 if (temporary_variables == null)
1900 temporary_variables = new ArrayList ();
1902 int id = ++next_temp_id;
1903 string name = "$s_" + id.ToString ();
1905 LocalInfo li = new LocalInfo (te, name, this, loc);
1906 li.CompilerGenerated = true;
1907 temporary_variables.Add (li);
1911 public LocalInfo GetLocalInfo (string name)
1913 for (Block b = this; b != null; b = b.Parent) {
1914 if (b.variables != null) {
1915 LocalInfo ret = b.variables [name] as LocalInfo;
1923 public Expression GetVariableType (string name)
1925 LocalInfo vi = GetLocalInfo (name);
1926 return vi == null ? null : vi.Type;
1929 public Expression GetConstantExpression (string name)
1931 for (Block b = this; b != null; b = b.Parent) {
1932 if (b.constants != null) {
1933 Expression ret = b.constants [name] as Expression;
1942 // It should be used by expressions which require to
1943 // register a statement during resolve process.
1945 public void AddScopeStatement (StatementExpression s)
1947 if (scope_initializers == null)
1948 scope_initializers = new ArrayList ();
1950 scope_initializers.Add (s);
1953 public void AddStatement (Statement s)
1956 flags |= Flags.BlockUsed;
1960 get { return (flags & Flags.BlockUsed) != 0; }
1965 flags |= Flags.BlockUsed;
1968 public bool HasRet {
1969 get { return (flags & Flags.HasRet) != 0; }
1972 public bool IsDestructor {
1973 get { return (flags & Flags.IsDestructor) != 0; }
1976 public void SetDestructor ()
1978 flags |= Flags.IsDestructor;
1981 public int AssignableSlots {
1983 if ((flags & Flags.VariablesInitialized) == 0)
1984 throw new Exception ("Variables have not been initialized yet");
1985 return assignable_slots;
1989 public ScopeInfo ScopeInfo {
1990 get { return scope_info; }
1993 public ScopeInfo CreateScopeInfo ()
1995 if (scope_info == null)
1996 scope_info = ScopeInfo.CreateScope (this);
2001 public ArrayList AnonymousChildren {
2002 get { return anonymous_children; }
2005 public void AddAnonymousChild (ToplevelBlock b)
2007 if (anonymous_children == null)
2008 anonymous_children = new ArrayList ();
2010 anonymous_children.Add (b);
2013 void DoResolveConstants (EmitContext ec)
2015 if (constants == null)
2018 if (variables == null)
2019 throw new InternalErrorException ("cannot happen");
2021 foreach (DictionaryEntry de in variables) {
2022 string name = (string) de.Key;
2023 LocalInfo vi = (LocalInfo) de.Value;
2024 Type variable_type = vi.VariableType;
2026 if (variable_type == null) {
2027 if (vi.Type is VarExpr)
2028 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2033 Expression cv = (Expression) constants [name];
2037 // Don't let 'const int Foo = Foo;' succeed.
2038 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2039 // which in turn causes the 'must be constant' error to be triggered.
2040 constants.Remove (name);
2042 if (!Const.IsConstantTypeValid (variable_type)) {
2043 Const.Error_InvalidConstantType (variable_type, loc);
2047 ec.CurrentBlock = this;
2049 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2050 e = cv.Resolve (ec);
2055 Constant ce = e as Constant;
2057 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2061 e = ce.ConvertImplicitly (variable_type);
2063 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2064 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2066 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2070 constants.Add (name, e);
2071 vi.IsConstant = true;
2075 protected void ResolveMeta (EmitContext ec, int offset)
2077 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2079 // If some parent block was unsafe, we remain unsafe even if this block
2080 // isn't explicitly marked as such.
2081 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2082 flags |= Flags.VariablesInitialized;
2084 if (variables != null) {
2085 foreach (LocalInfo li in variables.Values) {
2086 if (!li.Resolve (ec))
2088 li.VariableInfo = new VariableInfo (li, offset);
2089 offset += li.VariableInfo.Length;
2092 assignable_slots = offset;
2094 DoResolveConstants (ec);
2096 if (children == null)
2098 foreach (Block b in children)
2099 b.ResolveMeta (ec, offset);
2104 // Emits the local variable declarations for a block
2106 public virtual void EmitMeta (EmitContext ec)
2108 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2109 if (ScopeInfo != null) {
2110 scope_init = ScopeInfo.GetScopeInitializer (ec);
2111 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2115 if (variables != null){
2116 foreach (LocalInfo vi in variables.Values)
2117 vi.ResolveVariable (ec);
2120 if (temporary_variables != null) {
2121 for (int i = 0; i < temporary_variables.Count; i++)
2122 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2125 if (children != null) {
2126 for (int i = 0; i < children.Count; i++)
2127 ((Block)children[i]).EmitMeta(ec);
2131 void UsageWarning ()
2133 if (variables == null || Report.WarningLevel < 3)
2136 foreach (DictionaryEntry de in variables) {
2137 LocalInfo vi = (LocalInfo) de.Value;
2140 string name = (string) de.Key;
2142 // vi.VariableInfo can be null for 'catch' variables
2143 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2144 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2146 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2151 private void CheckPossibleMistakenEmptyStatement (Statement s)
2155 // Some statements are wrapped by a Block. Since
2156 // others' internal could be changed, here I treat
2157 // them as possibly wrapped by Block equally.
2158 Block b = s as Block;
2159 if (b != null && b.statements.Count == 1)
2160 s = (Statement) b.statements [0];
2163 body = ((Lock) s).Statement;
2165 body = ((For) s).Statement;
2166 else if (s is Foreach)
2167 body = ((Foreach) s).Statement;
2168 else if (s is While)
2169 body = ((While) s).Statement;
2170 else if (s is Fixed)
2171 body = ((Fixed) s).Statement;
2172 else if (s is Using)
2173 body = ((Using) s).EmbeddedStatement;
2174 else if (s is UsingTemporary)
2175 body = ((UsingTemporary) 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; }
2757 // This is used by non-static `struct' constructors which do not have an
2758 // initializer - in this case, the constructor must initialize all of the
2759 // struct's fields. To do this, we add a "this" variable and use the flow
2760 // analysis code to ensure that it's been fully initialized before control
2761 // leaves the constructor.
2763 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2765 if (this_variable == null) {
2766 this_variable = new LocalInfo (ds, this, l);
2767 this_variable.Used = true;
2768 this_variable.IsThis = true;
2770 Variables.Add ("this", this_variable);
2773 return this_variable;
2776 public bool IsThisAssigned (EmitContext ec)
2778 return this_variable == null || this_variable.IsThisAssigned (ec);
2781 public bool ResolveMeta (EmitContext ec, Parameters ip)
2783 int errors = Report.Errors;
2784 int orig_count = parameters.Count;
2786 if (top_level_branching != null)
2792 // Assert: orig_count != parameter.Count => orig_count == 0
2793 if (orig_count != 0 && orig_count != parameters.Count)
2794 throw new InternalErrorException ("parameter information mismatch");
2796 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2798 for (int i = 0; i < orig_count; ++i) {
2799 Parameter.Modifier mod = parameters.ParameterModifier (i);
2801 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2804 VariableInfo vi = new VariableInfo (ip, i, offset);
2805 parameter_info [i].VariableInfo = vi;
2806 offset += vi.Length;
2809 ResolveMeta (ec, offset);
2811 top_level_branching = ec.StartFlowBranching (this);
2813 return Report.Errors == errors;
2817 // Check whether all `out' parameters have been assigned.
2819 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2821 if (vector.IsUnreachable)
2824 int n = parameter_info == null ? 0 : parameter_info.Length;
2826 for (int i = 0; i < n; i++) {
2827 VariableInfo var = parameter_info [i].VariableInfo;
2832 if (vector.IsAssigned (var, false))
2835 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2840 public override void EmitMeta (EmitContext ec)
2842 // Avoid declaring an IL variable for this_variable since it is not accessed
2843 // from the generated IL
2844 if (this_variable != null)
2845 Variables.Remove ("this");
2847 parameters.ResolveVariable (this);
2850 protected override void EmitSymbolInfo (EmitContext ec)
2852 if ((AnonymousContainer != null) && (AnonymousContainer.Scope != null))
2853 SymbolWriter.DefineScopeVariable (AnonymousContainer.Scope.ID);
2855 base.EmitSymbolInfo (ec);
2858 public void MakeIterator (Iterator iterator)
2860 flags |= Flags.IsIterator;
2862 Block block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2863 foreach (Statement stmt in statements)
2864 block.AddStatement (stmt);
2865 statements.Clear ();
2866 statements.Add (new MoveNextStatement (iterator, block));
2869 protected class MoveNextStatement : Statement {
2873 public MoveNextStatement (Iterator iterator, Block block)
2875 this.iterator = iterator;
2877 this.loc = iterator.Location;
2880 public override bool Resolve (EmitContext ec)
2882 ec.StartFlowBranching (iterator);
2883 bool ok = block.Resolve (ec);
2884 ec.EndFlowBranching ();
2888 protected override void DoEmit (EmitContext ec)
2890 iterator.EmitMoveNext (ec, block);
2894 public override string ToString ()
2896 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2897 root_scope, anonymous_container != null ?
2898 anonymous_container.Scope : null);
2902 public class SwitchLabel {
2909 Label il_label_code;
2910 bool il_label_code_set;
2912 public static readonly object NullStringCase = new object ();
2915 // if expr == null, then it is the default case.
2917 public SwitchLabel (Expression expr, Location l)
2923 public Expression Label {
2929 public object Converted {
2935 public Label GetILLabel (EmitContext ec)
2938 il_label = ec.ig.DefineLabel ();
2939 il_label_set = true;
2944 public Label GetILLabelCode (EmitContext ec)
2946 if (!il_label_code_set){
2947 il_label_code = ec.ig.DefineLabel ();
2948 il_label_code_set = true;
2950 return il_label_code;
2954 // Resolves the expression, reduces it to a literal if possible
2955 // and then converts it to the requested type.
2957 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2959 Expression e = label.Resolve (ec);
2964 Constant c = e as Constant;
2966 Report.Error (150, loc, "A constant value is expected");
2970 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2971 converted = NullStringCase;
2975 if (allow_nullable && c.GetValue () == null) {
2976 converted = NullStringCase;
2980 c = c.ImplicitConversionRequired (required_type, loc);
2984 converted = c.GetValue ();
2988 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2991 if (converted == null)
2993 else if (converted == NullStringCase)
2995 else if (TypeManager.IsEnumType (switch_type))
2996 label = TypeManager.CSharpEnumValue (switch_type, converted);
2998 label = converted.ToString ();
3000 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3001 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3004 public SwitchLabel Clone (CloneContext clonectx)
3006 return new SwitchLabel (label.Clone (clonectx), loc);
3010 public class SwitchSection {
3011 // An array of SwitchLabels.
3012 public readonly ArrayList Labels;
3013 public readonly Block Block;
3015 public SwitchSection (ArrayList labels, Block block)
3021 public SwitchSection Clone (CloneContext clonectx)
3023 ArrayList cloned_labels = new ArrayList ();
3025 foreach (SwitchLabel sl in cloned_labels)
3026 cloned_labels.Add (sl.Clone (clonectx));
3028 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3032 public class Switch : Statement {
3033 public ArrayList Sections;
3034 public Expression Expr;
3037 /// Maps constants whose type type SwitchType to their SwitchLabels.
3039 public IDictionary Elements;
3042 /// The governing switch type
3044 public Type SwitchType;
3049 Label default_target;
3051 Expression new_expr;
3053 SwitchSection constant_section;
3054 SwitchSection default_section;
3058 // Nullable Types support for GMCS.
3060 Nullable.Unwrap unwrap;
3062 protected bool HaveUnwrap {
3063 get { return unwrap != null; }
3066 protected bool HaveUnwrap {
3067 get { return false; }
3072 // The types allowed to be implicitly cast from
3073 // on the governing type
3075 static Type [] allowed_types;
3077 public Switch (Expression e, ArrayList sects, Location l)
3084 public bool GotDefault {
3086 return default_section != null;
3090 public Label DefaultTarget {
3092 return default_target;
3097 // Determines the governing type for a switch. The returned
3098 // expression might be the expression from the switch, or an
3099 // expression that includes any potential conversions to the
3100 // integral types or to string.
3102 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3106 if (t == TypeManager.byte_type ||
3107 t == TypeManager.sbyte_type ||
3108 t == TypeManager.ushort_type ||
3109 t == TypeManager.short_type ||
3110 t == TypeManager.uint32_type ||
3111 t == TypeManager.int32_type ||
3112 t == TypeManager.uint64_type ||
3113 t == TypeManager.int64_type ||
3114 t == TypeManager.char_type ||
3115 t == TypeManager.string_type ||
3116 t == TypeManager.bool_type ||
3117 TypeManager.IsEnumType (t))
3120 if (allowed_types == null){
3121 allowed_types = new Type [] {
3122 TypeManager.sbyte_type,
3123 TypeManager.byte_type,
3124 TypeManager.short_type,
3125 TypeManager.ushort_type,
3126 TypeManager.int32_type,
3127 TypeManager.uint32_type,
3128 TypeManager.int64_type,
3129 TypeManager.uint64_type,
3130 TypeManager.char_type,
3131 TypeManager.string_type,
3132 TypeManager.bool_type
3137 // Try to find a *user* defined implicit conversion.
3139 // If there is no implicit conversion, or if there are multiple
3140 // conversions, we have to report an error
3142 Expression converted = null;
3143 foreach (Type tt in allowed_types){
3146 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3151 // Ignore over-worked ImplicitUserConversions that do
3152 // an implicit conversion in addition to the user conversion.
3154 if (!(e is UserCast))
3157 if (converted != null){
3158 Report.ExtraInformation (
3160 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3161 TypeManager.CSharpName (expr.Type)));
3171 // Performs the basic sanity checks on the switch statement
3172 // (looks for duplicate keys and non-constant expressions).
3174 // It also returns a hashtable with the keys that we will later
3175 // use to compute the switch tables
3177 bool CheckSwitch (EmitContext ec)
3180 Elements = Sections.Count > 10 ?
3181 (IDictionary)new Hashtable () :
3182 (IDictionary)new ListDictionary ();
3184 foreach (SwitchSection ss in Sections){
3185 foreach (SwitchLabel sl in ss.Labels){
3186 if (sl.Label == null){
3187 if (default_section != null){
3188 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3191 default_section = ss;
3195 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3200 object key = sl.Converted;
3202 Elements.Add (key, sl);
3203 } catch (ArgumentException) {
3204 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3212 void EmitObjectInteger (ILGenerator ig, object k)
3215 IntConstant.EmitInt (ig, (int) k);
3216 else if (k is Constant) {
3217 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3220 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3223 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3225 IntConstant.EmitInt (ig, (int) (long) k);
3226 ig.Emit (OpCodes.Conv_I8);
3229 LongConstant.EmitLong (ig, (long) k);
3231 else if (k is ulong)
3233 ulong ul = (ulong) k;
3236 IntConstant.EmitInt (ig, unchecked ((int) ul));
3237 ig.Emit (OpCodes.Conv_U8);
3241 LongConstant.EmitLong (ig, unchecked ((long) ul));
3245 IntConstant.EmitInt (ig, (int) ((char) k));
3246 else if (k is sbyte)
3247 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3249 IntConstant.EmitInt (ig, (int) ((byte) k));
3250 else if (k is short)
3251 IntConstant.EmitInt (ig, (int) ((short) k));
3252 else if (k is ushort)
3253 IntConstant.EmitInt (ig, (int) ((ushort) k));
3255 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3257 throw new Exception ("Unhandled case");
3260 // structure used to hold blocks of keys while calculating table switch
3261 class KeyBlock : IComparable
3263 public KeyBlock (long _first)
3265 first = last = _first;
3269 public ArrayList element_keys = null;
3270 // how many items are in the bucket
3271 public int Size = 1;
3274 get { return (int) (last - first + 1); }
3276 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3278 return kb_last.last - kb_first.first + 1;
3280 public int CompareTo (object obj)
3282 KeyBlock kb = (KeyBlock) obj;
3283 int nLength = Length;
3284 int nLengthOther = kb.Length;
3285 if (nLengthOther == nLength)
3286 return (int) (kb.first - first);
3287 return nLength - nLengthOther;
3292 /// This method emits code for a lookup-based switch statement (non-string)
3293 /// Basically it groups the cases into blocks that are at least half full,
3294 /// and then spits out individual lookup opcodes for each block.
3295 /// It emits the longest blocks first, and short blocks are just
3296 /// handled with direct compares.
3298 /// <param name="ec"></param>
3299 /// <param name="val"></param>
3300 /// <returns></returns>
3301 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3303 int element_count = Elements.Count;
3304 object [] element_keys = new object [element_count];
3305 Elements.Keys.CopyTo (element_keys, 0);
3306 Array.Sort (element_keys);
3308 // initialize the block list with one element per key
3309 ArrayList key_blocks = new ArrayList ();
3310 foreach (object key in element_keys)
3311 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3313 KeyBlock current_kb;
3314 // iteratively merge the blocks while they are at least half full
3315 // there's probably a really cool way to do this with a tree...
3316 while (key_blocks.Count > 1)
3318 ArrayList key_blocks_new = new ArrayList ();
3319 current_kb = (KeyBlock) key_blocks [0];
3320 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3322 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3323 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3326 current_kb.last = kb.last;
3327 current_kb.Size += kb.Size;
3331 // start a new block
3332 key_blocks_new.Add (current_kb);
3336 key_blocks_new.Add (current_kb);
3337 if (key_blocks.Count == key_blocks_new.Count)
3339 key_blocks = key_blocks_new;
3342 // initialize the key lists
3343 foreach (KeyBlock kb in key_blocks)
3344 kb.element_keys = new ArrayList ();
3346 // fill the key lists
3348 if (key_blocks.Count > 0) {
3349 current_kb = (KeyBlock) key_blocks [0];
3350 foreach (object key in element_keys)
3352 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3353 System.Convert.ToInt64 (key) > current_kb.last;
3355 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3356 current_kb.element_keys.Add (key);
3360 // sort the blocks so we can tackle the largest ones first
3363 // okay now we can start...
3364 ILGenerator ig = ec.ig;
3365 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3366 Label lbl_default = ig.DefineLabel ();
3368 Type type_keys = null;
3369 if (element_keys.Length > 0)
3370 type_keys = element_keys [0].GetType (); // used for conversions
3374 if (TypeManager.IsEnumType (SwitchType))
3375 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3377 compare_type = SwitchType;
3379 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3381 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3382 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3385 foreach (object key in kb.element_keys)
3387 ig.Emit (OpCodes.Ldloc, val);
3388 EmitObjectInteger (ig, key);
3389 SwitchLabel sl = (SwitchLabel) Elements [key];
3390 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3395 // TODO: if all the keys in the block are the same and there are
3396 // no gaps/defaults then just use a range-check.
3397 if (compare_type == TypeManager.int64_type ||
3398 compare_type == TypeManager.uint64_type)
3400 // TODO: optimize constant/I4 cases
3402 // check block range (could be > 2^31)
3403 ig.Emit (OpCodes.Ldloc, val);
3404 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3405 ig.Emit (OpCodes.Blt, lbl_default);
3406 ig.Emit (OpCodes.Ldloc, val);
3407 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3408 ig.Emit (OpCodes.Bgt, lbl_default);
3411 ig.Emit (OpCodes.Ldloc, val);
3414 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3415 ig.Emit (OpCodes.Sub);
3417 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3422 ig.Emit (OpCodes.Ldloc, val);
3423 int first = (int) kb.first;
3426 IntConstant.EmitInt (ig, first);
3427 ig.Emit (OpCodes.Sub);
3431 IntConstant.EmitInt (ig, -first);
3432 ig.Emit (OpCodes.Add);
3436 // first, build the list of labels for the switch
3438 int cJumps = kb.Length;
3439 Label [] switch_labels = new Label [cJumps];
3440 for (int iJump = 0; iJump < cJumps; iJump++)
3442 object key = kb.element_keys [iKey];
3443 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3445 SwitchLabel sl = (SwitchLabel) Elements [key];
3446 switch_labels [iJump] = sl.GetILLabel (ec);
3450 switch_labels [iJump] = lbl_default;
3452 // emit the switch opcode
3453 ig.Emit (OpCodes.Switch, switch_labels);
3456 // mark the default for this block
3458 ig.MarkLabel (lbl_default);
3461 // TODO: find the default case and emit it here,
3462 // to prevent having to do the following jump.
3463 // make sure to mark other labels in the default section
3465 // the last default just goes to the end
3466 ig.Emit (OpCodes.Br, lbl_default);
3468 // now emit the code for the sections
3469 bool found_default = false;
3470 bool found_null = false;
3471 foreach (SwitchSection ss in Sections)
3473 foreach (SwitchLabel sl in ss.Labels)
3474 if (sl.Converted == SwitchLabel.NullStringCase)
3478 foreach (SwitchSection ss in Sections)
3480 foreach (SwitchLabel sl in ss.Labels)
3482 ig.MarkLabel (sl.GetILLabel (ec));
3483 ig.MarkLabel (sl.GetILLabelCode (ec));
3484 if (sl.Converted == SwitchLabel.NullStringCase)
3485 ig.MarkLabel (null_target);
3486 else if (sl.Label == null) {
3487 ig.MarkLabel (lbl_default);
3488 found_default = true;
3490 ig.MarkLabel (null_target);
3496 if (!found_default) {
3497 ig.MarkLabel (lbl_default);
3498 if (HaveUnwrap && !found_null) {
3499 ig.MarkLabel (null_target);
3503 ig.MarkLabel (lbl_end);
3506 // This simple emit switch works, but does not take advantage of the
3508 // TODO: remove non-string logic from here
3509 // TODO: binary search strings?
3511 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3513 ILGenerator ig = ec.ig;
3514 Label end_of_switch = ig.DefineLabel ();
3515 Label next_test = ig.DefineLabel ();
3516 bool first_test = true;
3517 bool pending_goto_end = false;
3518 bool null_marked = false;
3520 int section_count = Sections.Count;
3522 // TODO: implement switch optimization for string by using Hashtable
3523 //if (SwitchType == TypeManager.string_type && section_count > 7)
3524 // Console.WriteLine ("Switch optimization possible " + loc);
3526 ig.Emit (OpCodes.Ldloc, val);
3528 if (Elements.Contains (SwitchLabel.NullStringCase)){
3529 ig.Emit (OpCodes.Brfalse, null_target);
3531 ig.Emit (OpCodes.Brfalse, default_target);
3533 ig.Emit (OpCodes.Ldloc, val);
3534 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3535 ig.Emit (OpCodes.Stloc, val);
3537 for (int section = 0; section < section_count; section++){
3538 SwitchSection ss = (SwitchSection) Sections [section];
3540 if (ss == default_section)
3543 Label sec_begin = ig.DefineLabel ();
3545 ig.Emit (OpCodes.Nop);
3547 if (pending_goto_end)
3548 ig.Emit (OpCodes.Br, end_of_switch);
3550 int label_count = ss.Labels.Count;
3552 for (int label = 0; label < label_count; label++){
3553 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3554 ig.MarkLabel (sl.GetILLabel (ec));
3557 ig.MarkLabel (next_test);
3558 next_test = ig.DefineLabel ();
3561 // If we are the default target
3563 if (sl.Label != null){
3564 object lit = sl.Converted;
3566 if (lit == SwitchLabel.NullStringCase){
3568 if (label + 1 == label_count)
3569 ig.Emit (OpCodes.Br, next_test);
3573 ig.Emit (OpCodes.Ldloc, val);
3574 ig.Emit (OpCodes.Ldstr, (string)lit);
3575 if (label_count == 1)
3576 ig.Emit (OpCodes.Bne_Un, next_test);
3578 if (label+1 == label_count)
3579 ig.Emit (OpCodes.Bne_Un, next_test);
3581 ig.Emit (OpCodes.Beq, sec_begin);
3586 ig.MarkLabel (null_target);
3589 ig.MarkLabel (sec_begin);
3590 foreach (SwitchLabel sl in ss.Labels)
3591 ig.MarkLabel (sl.GetILLabelCode (ec));
3594 pending_goto_end = !ss.Block.HasRet;
3597 ig.MarkLabel (next_test);
3598 ig.MarkLabel (default_target);
3600 ig.MarkLabel (null_target);
3601 if (default_section != null)
3602 default_section.Block.Emit (ec);
3603 ig.MarkLabel (end_of_switch);
3606 SwitchSection FindSection (SwitchLabel label)
3608 foreach (SwitchSection ss in Sections){
3609 foreach (SwitchLabel sl in ss.Labels){
3618 public override bool Resolve (EmitContext ec)
3620 Expr = Expr.Resolve (ec);
3624 new_expr = SwitchGoverningType (ec, Expr);
3627 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3628 unwrap = Nullable.Unwrap.Create (Expr, ec);
3632 new_expr = SwitchGoverningType (ec, unwrap);
3636 if (new_expr == null){
3637 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3642 SwitchType = new_expr.Type;
3644 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3645 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3649 if (!CheckSwitch (ec))
3653 Elements.Remove (SwitchLabel.NullStringCase);
3655 Switch old_switch = ec.Switch;
3657 ec.Switch.SwitchType = SwitchType;
3659 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3660 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3662 is_constant = new_expr is Constant;
3664 object key = ((Constant) new_expr).GetValue ();
3665 SwitchLabel label = (SwitchLabel) Elements [key];
3667 constant_section = FindSection (label);
3668 if (constant_section == null)
3669 constant_section = default_section;
3674 foreach (SwitchSection ss in Sections){
3676 ec.CurrentBranching.CreateSibling (
3677 null, FlowBranching.SiblingType.SwitchSection);
3681 if (is_constant && (ss != constant_section)) {
3682 // If we're a constant switch, we're only emitting
3683 // one single section - mark all the others as
3685 ec.CurrentBranching.CurrentUsageVector.Goto ();
3686 if (!ss.Block.ResolveUnreachable (ec, true)) {
3690 if (!ss.Block.Resolve (ec))
3695 if (default_section == null)
3696 ec.CurrentBranching.CreateSibling (
3697 null, FlowBranching.SiblingType.SwitchSection);
3699 ec.EndFlowBranching ();
3700 ec.Switch = old_switch;
3702 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3704 if (TypeManager.string_isinterned_string == null) {
3705 TypeManager.string_isinterned_string = TypeManager.GetPredefinedMethod (TypeManager.string_type,
3706 "IsInterned", loc, TypeManager.string_type);
3712 protected override void DoEmit (EmitContext ec)
3714 ILGenerator ig = ec.ig;
3716 default_target = ig.DefineLabel ();
3717 null_target = ig.DefineLabel ();
3719 // Store variable for comparission purposes
3722 value = ig.DeclareLocal (SwitchType);
3724 unwrap.EmitCheck (ec);
3725 ig.Emit (OpCodes.Brfalse, null_target);
3727 ig.Emit (OpCodes.Stloc, value);
3729 } else if (!is_constant) {
3730 value = ig.DeclareLocal (SwitchType);
3732 ig.Emit (OpCodes.Stloc, value);
3737 // Setup the codegen context
3739 Label old_end = ec.LoopEnd;
3740 Switch old_switch = ec.Switch;
3742 ec.LoopEnd = ig.DefineLabel ();
3747 if (constant_section != null)
3748 constant_section.Block.Emit (ec);
3749 } else if (SwitchType == TypeManager.string_type)
3750 SimpleSwitchEmit (ec, value);
3752 TableSwitchEmit (ec, value);
3754 // Restore context state.
3755 ig.MarkLabel (ec.LoopEnd);
3758 // Restore the previous context
3760 ec.LoopEnd = old_end;
3761 ec.Switch = old_switch;
3764 protected override void CloneTo (CloneContext clonectx, Statement t)
3766 Switch target = (Switch) t;
3768 target.Expr = Expr.Clone (clonectx);
3769 target.Sections = new ArrayList ();
3770 foreach (SwitchSection ss in Sections){
3771 target.Sections.Add (ss.Clone (clonectx));
3776 // A place where execution can restart in an iterator
3777 public abstract class ResumableStatement : Statement
3780 protected Label resume_point;
3782 public Label PrepareForEmit (EmitContext ec)
3786 resume_point = ec.ig.DefineLabel ();
3788 return resume_point;
3791 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3795 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3800 public abstract class ExceptionStatement : ResumableStatement
3804 protected abstract void EmitPreTryBody (EmitContext ec);
3805 protected abstract void EmitTryBody (EmitContext ec);
3806 protected abstract void EmitFinallyBody (EmitContext ec);
3808 protected sealed override void DoEmit (EmitContext ec)
3810 ILGenerator ig = ec.ig;
3812 EmitPreTryBody (ec);
3814 if (resume_points != null) {
3815 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3816 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3819 ig.BeginExceptionBlock ();
3821 if (resume_points != null) {
3822 ig.MarkLabel (resume_point);
3824 // For normal control flow, we want to fall-through the Switch
3825 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3826 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3827 IntConstant.EmitInt (ig, first_resume_pc);
3828 ig.Emit (OpCodes.Sub);
3830 Label [] labels = new Label [resume_points.Count];
3831 for (int i = 0; i < resume_points.Count; ++i)
3832 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3833 ig.Emit (OpCodes.Switch, labels);
3838 ig.BeginFinallyBlock ();
3840 Label start_finally = ec.ig.DefineLabel ();
3841 if (resume_points != null) {
3842 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3843 ig.Emit (OpCodes.Brfalse_S, start_finally);
3844 ig.Emit (OpCodes.Endfinally);
3847 ig.MarkLabel (start_finally);
3848 EmitFinallyBody (ec);
3850 ig.EndExceptionBlock ();
3853 public void SomeCodeFollows ()
3855 code_follows = true;
3858 protected void ResolveReachability (EmitContext ec)
3860 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3861 // So, ensure there's some IL code after this statement.
3862 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3863 ec.NeedReturnLabel ();
3867 ArrayList resume_points;
3868 int first_resume_pc;
3869 public void AddResumePoint (ResumableStatement stmt, Location loc, int pc)
3871 if (resume_points == null) {
3872 resume_points = new ArrayList ();
3873 first_resume_pc = pc;
3876 if (pc != first_resume_pc + resume_points.Count)
3877 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3879 resume_points.Add (stmt);
3882 Label dispose_try_block;
3883 bool prepared_for_dispose, emitted_dispose;
3884 public override Label PrepareForDispose (EmitContext ec, Label end)
3886 if (!prepared_for_dispose) {
3887 prepared_for_dispose = true;
3888 dispose_try_block = ec.ig.DefineLabel ();
3890 return dispose_try_block;
3893 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3895 if (emitted_dispose)
3898 emitted_dispose = true;
3900 ILGenerator ig = ec.ig;
3902 Label end_of_try = ig.DefineLabel ();
3904 // Ensure that the only way we can get into this code is through a dispatcher
3905 if (have_dispatcher)
3906 ig.Emit (OpCodes.Br, end);
3908 ig.BeginExceptionBlock ();
3910 ig.MarkLabel (dispose_try_block);
3912 Label [] labels = null;
3913 for (int i = 0; i < resume_points.Count; ++i) {
3914 ResumableStatement s = (ResumableStatement) resume_points [i];
3915 Label ret = s.PrepareForDispose (ec, end_of_try);
3916 if (ret.Equals (end_of_try) && labels == null)
3918 if (labels == null) {
3919 labels = new Label [resume_points.Count];
3920 for (int j = 0; j < i; ++j)
3921 labels [j] = end_of_try;
3926 if (labels != null) {
3928 for (j = 1; j < labels.Length; ++j)
3929 if (!labels [0].Equals (labels [j]))
3931 bool emit_dispatcher = j < labels.Length;
3933 if (emit_dispatcher) {
3934 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3935 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3936 IntConstant.EmitInt (ig, first_resume_pc);
3937 ig.Emit (OpCodes.Sub);
3938 ig.Emit (OpCodes.Switch, labels);
3939 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3942 foreach (ResumableStatement s in resume_points)
3943 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3946 ig.MarkLabel (end_of_try);
3948 ig.BeginFinallyBlock ();
3950 EmitFinallyBody (ec);
3952 ig.EndExceptionBlock ();
3956 public class Lock : ExceptionStatement {
3958 public Statement Statement;
3959 TemporaryVariable temp;
3961 public Lock (Expression expr, Statement stmt, Location l)
3968 public override bool Resolve (EmitContext ec)
3970 expr = expr.Resolve (ec);
3974 if (expr.Type.IsValueType){
3975 Report.Error (185, loc,
3976 "`{0}' is not a reference type as required by the lock statement",
3977 TypeManager.CSharpName (expr.Type));
3981 ec.StartFlowBranching (this);
3982 bool ok = Statement.Resolve (ec);
3983 ec.EndFlowBranching ();
3985 ResolveReachability (ec);
3987 // Avoid creating libraries that reference the internal
3990 if (t == TypeManager.null_type)
3991 t = TypeManager.object_type;
3993 temp = new TemporaryVariable (t, loc);
3996 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
3997 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
3998 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
3999 monitor_type, "Enter", loc, TypeManager.object_type);
4000 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4001 monitor_type, "Exit", loc, TypeManager.object_type);
4007 protected override void EmitPreTryBody (EmitContext ec)
4009 ILGenerator ig = ec.ig;
4011 temp.Store (ec, expr);
4013 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4016 protected override void EmitTryBody (EmitContext ec)
4018 Statement.Emit (ec);
4021 protected override void EmitFinallyBody (EmitContext ec)
4024 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4027 protected override void CloneTo (CloneContext clonectx, Statement t)
4029 Lock target = (Lock) t;
4031 target.expr = expr.Clone (clonectx);
4032 target.Statement = Statement.Clone (clonectx);
4036 public class Unchecked : Statement {
4039 public Unchecked (Block b)
4045 public override bool Resolve (EmitContext ec)
4047 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4048 return Block.Resolve (ec);
4051 protected override void DoEmit (EmitContext ec)
4053 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4057 protected override void CloneTo (CloneContext clonectx, Statement t)
4059 Unchecked target = (Unchecked) t;
4061 target.Block = clonectx.LookupBlock (Block);
4065 public class Checked : Statement {
4068 public Checked (Block b)
4071 b.Unchecked = false;
4074 public override bool Resolve (EmitContext ec)
4076 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4077 return Block.Resolve (ec);
4080 protected override void DoEmit (EmitContext ec)
4082 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4086 protected override void CloneTo (CloneContext clonectx, Statement t)
4088 Checked target = (Checked) t;
4090 target.Block = clonectx.LookupBlock (Block);
4094 public class Unsafe : Statement {
4097 public Unsafe (Block b)
4100 Block.Unsafe = true;
4103 public override bool Resolve (EmitContext ec)
4105 using (ec.With (EmitContext.Flags.InUnsafe, true))
4106 return Block.Resolve (ec);
4109 protected override void DoEmit (EmitContext ec)
4111 using (ec.With (EmitContext.Flags.InUnsafe, true))
4114 protected override void CloneTo (CloneContext clonectx, Statement t)
4116 Unsafe target = (Unsafe) t;
4118 target.Block = clonectx.LookupBlock (Block);
4125 public class Fixed : Statement {
4127 ArrayList declarators;
4128 Statement statement;
4133 abstract class Emitter
4135 protected LocalInfo vi;
4136 protected Expression converted;
4138 protected Emitter (Expression expr, LocalInfo li)
4144 public abstract void Emit (EmitContext ec);
4145 public abstract void EmitExit (EmitContext ec);
4148 class ExpressionEmitter : Emitter {
4149 public ExpressionEmitter (Expression converted, LocalInfo li) :
4150 base (converted, li)
4154 public override void Emit (EmitContext ec) {
4156 // Store pointer in pinned location
4158 converted.Emit (ec);
4159 vi.Variable.EmitAssign (ec);
4162 public override void EmitExit (EmitContext ec)
4164 ec.ig.Emit (OpCodes.Ldc_I4_0);
4165 ec.ig.Emit (OpCodes.Conv_U);
4166 vi.Variable.EmitAssign (ec);
4170 class StringEmitter : Emitter {
4171 LocalBuilder pinned_string;
4174 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4180 public override void Emit (EmitContext ec)
4182 ILGenerator ig = ec.ig;
4183 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4185 converted.Emit (ec);
4186 ig.Emit (OpCodes.Stloc, pinned_string);
4188 Expression sptr = new StringPtr (pinned_string, loc);
4189 converted = Convert.ImplicitConversionRequired (
4190 ec, sptr, vi.VariableType, loc);
4192 if (converted == null)
4195 converted.Emit (ec);
4196 vi.Variable.EmitAssign (ec);
4199 public override void EmitExit (EmitContext ec)
4201 ec.ig.Emit (OpCodes.Ldnull);
4202 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4206 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4209 declarators = decls;
4214 public Statement Statement {
4215 get { return statement; }
4218 public override bool Resolve (EmitContext ec)
4221 Expression.UnsafeError (loc);
4225 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4226 if (texpr == null) {
4227 if (type is VarExpr)
4228 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4233 expr_type = texpr.Type;
4235 data = new Emitter [declarators.Count];
4237 if (!expr_type.IsPointer){
4238 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4243 foreach (Pair p in declarators){
4244 LocalInfo vi = (LocalInfo) p.First;
4245 Expression e = (Expression) p.Second;
4247 vi.VariableInfo.SetAssigned (ec);
4248 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4251 // The rules for the possible declarators are pretty wise,
4252 // but the production on the grammar is more concise.
4254 // So we have to enforce these rules here.
4256 // We do not resolve before doing the case 1 test,
4257 // because the grammar is explicit in that the token &
4258 // is present, so we need to test for this particular case.
4262 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4267 // Case 1: & object.
4269 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4270 Expression child = ((Unary) e).Expr;
4272 if (child is ParameterReference || child is LocalVariableReference){
4275 "No need to use fixed statement for parameters or " +
4276 "local variable declarations (address is already " +
4281 ec.InFixedInitializer = true;
4283 ec.InFixedInitializer = false;
4287 child = ((Unary) e).Expr;
4289 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4292 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4293 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4297 data [i] = new ExpressionEmitter (e, vi);
4303 ec.InFixedInitializer = true;
4305 ec.InFixedInitializer = false;
4312 if (e.Type.IsArray){
4313 Type array_type = TypeManager.GetElementType (e.Type);
4316 // Provided that array_type is unmanaged,
4318 if (!TypeManager.VerifyUnManaged (array_type, loc))
4322 // and T* is implicitly convertible to the
4323 // pointer type given in the fixed statement.
4325 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4327 Expression converted = Convert.ImplicitConversionRequired (
4328 ec, array_ptr, vi.VariableType, loc);
4329 if (converted == null)
4333 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4335 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4336 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4337 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4341 converted = converted.Resolve (ec);
4343 data [i] = new ExpressionEmitter (converted, vi);
4352 if (e.Type == TypeManager.string_type){
4353 data [i] = new StringEmitter (e, vi, loc);
4358 // Case 4: fixed buffer
4359 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4360 if (fixed_buffer_ptr != null) {
4361 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4366 // For other cases, flag a `this is already fixed expression'
4368 if (e is LocalVariableReference || e is ParameterReference ||
4369 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4371 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4375 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4379 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4380 bool ok = statement.Resolve (ec);
4381 bool flow_unreachable = ec.EndFlowBranching ();
4382 has_ret = flow_unreachable;
4387 protected override void DoEmit (EmitContext ec)
4389 for (int i = 0; i < data.Length; i++) {
4393 statement.Emit (ec);
4399 // Clear the pinned variable
4401 for (int i = 0; i < data.Length; i++) {
4402 data [i].EmitExit (ec);
4406 protected override void CloneTo (CloneContext clonectx, Statement t)
4408 Fixed target = (Fixed) t;
4410 target.type = type.Clone (clonectx);
4411 target.declarators = new ArrayList (declarators.Count);
4412 foreach (Pair p in declarators) {
4413 LocalInfo vi = (LocalInfo) p.First;
4414 Expression e = (Expression) p.Second;
4416 target.declarators.Add (
4417 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4420 target.statement = statement.Clone (clonectx);
4424 public class Catch : Statement {
4425 public readonly string Name;
4427 public Block VarBlock;
4429 Expression type_expr;
4432 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4437 VarBlock = var_block;
4441 public Type CatchType {
4447 public bool IsGeneral {
4449 return type_expr == null;
4453 protected override void DoEmit (EmitContext ec)
4455 ILGenerator ig = ec.ig;
4457 if (CatchType != null)
4458 ig.BeginCatchBlock (CatchType);
4460 ig.BeginCatchBlock (TypeManager.object_type);
4462 if (VarBlock != null)
4466 LocalInfo vi = Block.GetLocalInfo (Name);
4468 throw new Exception ("Variable does not exist in this block");
4470 if (vi.Variable.NeedsTemporary) {
4471 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
4472 ig.Emit (OpCodes.Stloc, e);
4474 vi.Variable.EmitInstance (ec);
4475 ig.Emit (OpCodes.Ldloc, e);
4476 vi.Variable.EmitAssign (ec);
4478 vi.Variable.EmitAssign (ec);
4480 ig.Emit (OpCodes.Pop);
4485 public override bool Resolve (EmitContext ec)
4487 using (ec.With (EmitContext.Flags.InCatch, true)) {
4488 if (type_expr != null) {
4489 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4495 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4496 Error (155, "The type caught or thrown must be derived from System.Exception");
4502 if (!Block.Resolve (ec))
4505 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4506 // emit the "unused variable" warnings.
4507 if (VarBlock != null)
4508 return VarBlock.Resolve (ec);
4514 protected override void CloneTo (CloneContext clonectx, Statement t)
4516 Catch target = (Catch) t;
4518 if (type_expr != null)
4519 target.type_expr = type_expr.Clone (clonectx);
4520 if (VarBlock != null)
4521 target.VarBlock = clonectx.LookupBlock (VarBlock);
4522 target.Block = clonectx.LookupBlock (Block);
4526 public class TryFinally : ExceptionStatement {
4530 public TryFinally (Statement stmt, Block fini, Location l)
4537 public override bool Resolve (EmitContext ec)
4541 ec.StartFlowBranching (this);
4543 if (!stmt.Resolve (ec))
4547 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4548 using (ec.With (EmitContext.Flags.InFinally, true)) {
4549 if (!fini.Resolve (ec))
4553 ec.EndFlowBranching ();
4555 ResolveReachability (ec);
4560 protected override void EmitPreTryBody (EmitContext ec)
4564 protected override void EmitTryBody (EmitContext ec)
4569 protected override void EmitFinallyBody (EmitContext ec)
4574 protected override void CloneTo (CloneContext clonectx, Statement t)
4576 TryFinally target = (TryFinally) t;
4578 target.stmt = (Statement) stmt.Clone (clonectx);
4580 target.fini = clonectx.LookupBlock (fini);
4584 public class TryCatch : Statement {
4586 public ArrayList Specific;
4587 public Catch General;
4588 bool inside_try_finally, code_follows;
4590 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4593 this.Specific = catch_clauses;
4594 this.General = null;
4595 this.inside_try_finally = inside_try_finally;
4597 for (int i = 0; i < catch_clauses.Count; ++i) {
4598 Catch c = (Catch) catch_clauses [i];
4600 if (i != catch_clauses.Count - 1)
4601 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4603 catch_clauses.RemoveAt (i);
4611 public override bool Resolve (EmitContext ec)
4615 ec.StartFlowBranching (this);
4617 if (!Block.Resolve (ec))
4620 Type[] prev_catches = new Type [Specific.Count];
4622 foreach (Catch c in Specific){
4623 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4625 if (c.Name != null) {
4626 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4628 throw new Exception ();
4630 vi.VariableInfo = null;
4633 if (!c.Resolve (ec))
4636 Type resolved_type = c.CatchType;
4637 for (int ii = 0; ii < last_index; ++ii) {
4638 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4639 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4644 prev_catches [last_index++] = resolved_type;
4647 if (General != null) {
4648 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4649 foreach (Catch c in Specific){
4650 if (c.CatchType == TypeManager.exception_type) {
4651 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'");
4656 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4658 if (!General.Resolve (ec))
4662 ec.EndFlowBranching ();
4664 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4665 // So, ensure there's some IL code after this statement
4666 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4667 ec.NeedReturnLabel ();
4672 public void SomeCodeFollows ()
4674 code_follows = true;
4677 protected override void DoEmit (EmitContext ec)
4679 ILGenerator ig = ec.ig;
4681 if (!inside_try_finally)
4682 ig.BeginExceptionBlock ();
4686 foreach (Catch c in Specific)
4689 if (General != null)
4692 if (!inside_try_finally)
4693 ig.EndExceptionBlock ();
4696 protected override void CloneTo (CloneContext clonectx, Statement t)
4698 TryCatch target = (TryCatch) t;
4700 target.Block = clonectx.LookupBlock (Block);
4701 if (General != null)
4702 target.General = (Catch) General.Clone (clonectx);
4703 if (Specific != null){
4704 target.Specific = new ArrayList ();
4705 foreach (Catch c in Specific)
4706 target.Specific.Add (c.Clone (clonectx));
4711 public class UsingTemporary : ExceptionStatement {
4712 TemporaryVariable local_copy;
4713 public Statement Statement;
4717 public UsingTemporary (Expression expr, Statement stmt, Location l)
4724 public override bool Resolve (EmitContext ec)
4726 expr = expr.Resolve (ec);
4730 expr_type = expr.Type;
4732 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4733 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4734 Using.Error_IsNotConvertibleToIDisposable (expr);
4739 local_copy = new TemporaryVariable (expr_type, loc);
4740 local_copy.Resolve (ec);
4742 ec.StartFlowBranching (this);
4744 bool ok = Statement.Resolve (ec);
4746 ec.EndFlowBranching ();
4748 ResolveReachability (ec);
4750 if (TypeManager.void_dispose_void == null) {
4751 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4752 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4758 protected override void EmitPreTryBody (EmitContext ec)
4760 local_copy.Store (ec, expr);
4763 protected override void EmitTryBody (EmitContext ec)
4765 Statement.Emit (ec);
4768 protected override void EmitFinallyBody (EmitContext ec)
4770 ILGenerator ig = ec.ig;
4771 if (!expr_type.IsValueType) {
4772 Label skip = ig.DefineLabel ();
4773 local_copy.Emit (ec);
4774 ig.Emit (OpCodes.Brfalse, skip);
4775 local_copy.Emit (ec);
4776 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4777 ig.MarkLabel (skip);
4781 Expression ml = Expression.MemberLookup (
4782 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4783 "Dispose", Location.Null);
4785 if (!(ml is MethodGroupExpr)) {
4786 local_copy.Emit (ec);
4787 ig.Emit (OpCodes.Box, expr_type);
4788 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4792 MethodInfo mi = null;
4794 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4795 if (TypeManager.GetParameterData (mk).Count == 0) {
4802 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4806 local_copy.AddressOf (ec, AddressOp.Load);
4807 ig.Emit (OpCodes.Call, mi);
4810 protected override void CloneTo (CloneContext clonectx, Statement t)
4812 UsingTemporary target = (UsingTemporary) t;
4814 target.expr = expr.Clone (clonectx);
4815 target.Statement = Statement.Clone (clonectx);
4819 public class Using : ExceptionStatement {
4821 public Statement EmbeddedStatement {
4822 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4828 Expression converted_var;
4831 public Using (Expression var, Expression init, Statement stmt, Location l)
4839 bool ResolveVariable (EmitContext ec)
4841 Expression a = new Assign (var, init, loc);
4848 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4849 converted_var = var;
4853 a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4855 Error_IsNotConvertibleToIDisposable (var);
4864 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4866 Report.SymbolRelatedToPreviousError (expr.Type);
4867 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4868 expr.GetSignatureForError ());
4871 protected override void EmitPreTryBody (EmitContext ec)
4873 ExpressionStatement es = assign as ExpressionStatement;
4875 es.EmitStatement (ec);
4878 ec.ig.Emit (OpCodes.Pop);
4882 protected override void EmitTryBody (EmitContext ec)
4887 protected override void EmitFinallyBody (EmitContext ec)
4889 ILGenerator ig = ec.ig;
4891 if (!var.Type.IsValueType) {
4892 Label skip = ig.DefineLabel ();
4894 ig.Emit (OpCodes.Brfalse, skip);
4895 converted_var.Emit (ec);
4896 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4897 ig.MarkLabel (skip);
4899 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4901 if (!(ml is MethodGroupExpr)) {
4903 ig.Emit (OpCodes.Box, var.Type);
4904 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4906 MethodInfo mi = null;
4908 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4909 if (TypeManager.GetParameterData (mk).Count == 0) {
4916 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4920 IMemoryLocation mloc = (IMemoryLocation) var;
4922 mloc.AddressOf (ec, AddressOp.Load);
4923 ig.Emit (OpCodes.Call, mi);
4928 public override bool Resolve (EmitContext ec)
4930 if (!ResolveVariable (ec))
4933 ec.StartFlowBranching (this);
4935 bool ok = stmt.Resolve (ec);
4937 ec.EndFlowBranching ();
4939 ResolveReachability (ec);
4941 if (TypeManager.void_dispose_void == null) {
4942 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4943 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4949 protected override void CloneTo (CloneContext clonectx, Statement t)
4951 Using target = (Using) t;
4953 target.var = var.Clone (clonectx);
4954 target.init = init.Clone (clonectx);
4955 target.stmt = stmt.Clone (clonectx);
4960 /// Implementation of the foreach C# statement
4962 public class Foreach : Statement {
4964 Expression variable;
4966 Statement statement;
4968 CollectionForeach collection;
4970 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4971 Statement stmt, Location l)
4974 this.variable = var;
4980 public Statement Statement {
4981 get { return statement; }
4984 public override bool Resolve (EmitContext ec)
4986 expr = expr.Resolve (ec);
4991 Report.Error (186, loc, "Use of null is not valid in this context");
4995 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4996 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4997 expr.ExprClassName);
5002 // We need an instance variable. Not sure this is the best
5003 // way of doing this.
5005 // FIXME: When we implement propertyaccess, will those turn
5006 // out to return values in ExprClass? I think they should.
5008 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
5009 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
5010 collection.Error_Enumerator ();
5014 if (expr.Type.IsArray) {
5015 array = new ArrayForeach (type, variable, expr, statement, loc);
5016 return array.Resolve (ec);
5019 collection = new CollectionForeach (type, variable, expr, statement, loc);
5020 return collection.Resolve (ec);
5023 protected override void DoEmit (EmitContext ec)
5025 ILGenerator ig = ec.ig;
5027 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5028 ec.LoopBegin = ig.DefineLabel ();
5029 ec.LoopEnd = ig.DefineLabel ();
5031 if (collection != null)
5032 collection.Emit (ec);
5036 ec.LoopBegin = old_begin;
5037 ec.LoopEnd = old_end;
5040 protected class ArrayCounter : TemporaryVariable
5042 public ArrayCounter (Location loc)
5043 : base (TypeManager.int32_type, loc)
5046 public void Initialize (EmitContext ec)
5049 ec.ig.Emit (OpCodes.Ldc_I4_0);
5053 public void Increment (EmitContext ec)
5057 ec.ig.Emit (OpCodes.Ldc_I4_1);
5058 ec.ig.Emit (OpCodes.Add);
5063 protected class ArrayForeach : Statement
5065 Expression variable, expr, conv;
5066 Statement statement;
5068 Expression var_type;
5069 TemporaryVariable[] lengths;
5070 ArrayCounter[] counter;
5073 TemporaryVariable copy;
5076 public ArrayForeach (Expression var_type, Expression var,
5077 Expression expr, Statement stmt, Location l)
5079 this.var_type = var_type;
5080 this.variable = var;
5086 public override bool Resolve (EmitContext ec)
5088 array_type = expr.Type;
5089 rank = array_type.GetArrayRank ();
5091 copy = new TemporaryVariable (array_type, loc);
5094 counter = new ArrayCounter [rank];
5095 lengths = new TemporaryVariable [rank];
5097 ArrayList list = new ArrayList ();
5098 for (int i = 0; i < rank; i++) {
5099 counter [i] = new ArrayCounter (loc);
5100 counter [i].Resolve (ec);
5102 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5103 lengths [i].Resolve (ec);
5105 list.Add (counter [i]);
5108 access = new ElementAccess (copy, list).Resolve (ec);
5112 VarExpr ve = var_type as VarExpr;
5114 // Infer implicitly typed local variable from foreach array type
5115 var_type = new TypeExpression (access.Type, ve.Location);
5118 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5119 if (var_type == null)
5122 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5128 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5129 ec.CurrentBranching.CreateSibling ();
5131 variable = variable.ResolveLValue (ec, conv, loc);
5132 if (variable == null)
5135 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5136 if (!statement.Resolve (ec))
5138 ec.EndFlowBranching ();
5140 // There's no direct control flow from the end of the embedded statement to the end of the loop
5141 ec.CurrentBranching.CurrentUsageVector.Goto ();
5143 ec.EndFlowBranching ();
5148 protected override void DoEmit (EmitContext ec)
5150 ILGenerator ig = ec.ig;
5152 copy.Store (ec, expr);
5154 Label[] test = new Label [rank];
5155 Label[] loop = new Label [rank];
5157 for (int i = 0; i < rank; i++) {
5158 test [i] = ig.DefineLabel ();
5159 loop [i] = ig.DefineLabel ();
5161 lengths [i].EmitThis (ec);
5162 ((ArrayAccess) access).EmitGetLength (ec, i);
5163 lengths [i].EmitStore (ec);
5166 for (int i = 0; i < rank; i++) {
5167 counter [i].Initialize (ec);
5169 ig.Emit (OpCodes.Br, test [i]);
5170 ig.MarkLabel (loop [i]);
5173 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5175 statement.Emit (ec);
5177 ig.MarkLabel (ec.LoopBegin);
5179 for (int i = rank - 1; i >= 0; i--){
5180 counter [i].Increment (ec);
5182 ig.MarkLabel (test [i]);
5183 counter [i].Emit (ec);
5184 lengths [i].Emit (ec);
5185 ig.Emit (OpCodes.Blt, loop [i]);
5188 ig.MarkLabel (ec.LoopEnd);
5192 protected class CollectionForeach : Statement
5194 Expression variable, expr;
5195 Statement statement;
5197 TemporaryVariable enumerator;
5202 MethodGroupExpr get_enumerator;
5203 PropertyExpr get_current;
5204 MethodInfo move_next;
5205 Expression var_type;
5206 Type enumerator_type;
5207 bool enumerator_found;
5209 public CollectionForeach (Expression var_type, Expression var,
5210 Expression expr, Statement stmt, Location l)
5212 this.var_type = var_type;
5213 this.variable = var;
5219 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5221 Type return_type = mi.ReturnType;
5223 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5225 // Apply the same optimization as MS: skip the GetEnumerator
5226 // returning an IEnumerator, and use the one returning a
5227 // CharEnumerator instead. This allows us to avoid the
5228 // try-finally block and the boxing.
5233 // Ok, we can access it, now make sure that we can do something
5234 // with this `GetEnumerator'
5237 if (return_type == TypeManager.ienumerator_type ||
5238 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5239 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5241 // If it is not an interface, lets try to find the methods ourselves.
5242 // For example, if we have:
5243 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5244 // We can avoid the iface call. This is a runtime perf boost.
5245 // even bigger if we have a ValueType, because we avoid the cost
5248 // We have to make sure that both methods exist for us to take
5249 // this path. If one of the methods does not exist, we will just
5250 // use the interface. Sadly, this complex if statement is the only
5251 // way I could do this without a goto
5254 if (TypeManager.bool_movenext_void == null) {
5255 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5256 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5259 if (TypeManager.ienumerator_getcurrent == null) {
5260 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5261 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5266 // Prefer a generic enumerator over a non-generic one.
5268 if (return_type.IsInterface && return_type.IsGenericType) {
5269 enumerator_type = return_type;
5270 if (!FetchGetCurrent (ec, return_type))
5271 get_current = new PropertyExpr (
5272 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5273 if (!FetchMoveNext (return_type))
5274 move_next = TypeManager.bool_movenext_void;
5279 if (return_type.IsInterface ||
5280 !FetchMoveNext (return_type) ||
5281 !FetchGetCurrent (ec, return_type)) {
5282 enumerator_type = return_type;
5283 move_next = TypeManager.bool_movenext_void;
5284 get_current = new PropertyExpr (
5285 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5290 // Ok, so they dont return an IEnumerable, we will have to
5291 // find if they support the GetEnumerator pattern.
5294 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5295 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",
5296 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5301 enumerator_type = return_type;
5307 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5309 bool FetchMoveNext (Type t)
5311 MemberList move_next_list;
5313 move_next_list = TypeContainer.FindMembers (
5314 t, MemberTypes.Method,
5315 BindingFlags.Public | BindingFlags.Instance,
5316 Type.FilterName, "MoveNext");
5317 if (move_next_list.Count == 0)
5320 foreach (MemberInfo m in move_next_list){
5321 MethodInfo mi = (MethodInfo) m;
5323 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5324 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5334 // Retrieves a `public T get_Current ()' method from the Type `t'
5336 bool FetchGetCurrent (EmitContext ec, Type t)
5338 PropertyExpr pe = Expression.MemberLookup (
5339 ec.ContainerType, t, "Current", MemberTypes.Property,
5340 Expression.AllBindingFlags, loc) as PropertyExpr;
5349 // Retrieves a `public void Dispose ()' method from the Type `t'
5351 static MethodInfo FetchMethodDispose (Type t)
5353 MemberList dispose_list;
5355 dispose_list = TypeContainer.FindMembers (
5356 t, MemberTypes.Method,
5357 BindingFlags.Public | BindingFlags.Instance,
5358 Type.FilterName, "Dispose");
5359 if (dispose_list.Count == 0)
5362 foreach (MemberInfo m in dispose_list){
5363 MethodInfo mi = (MethodInfo) m;
5365 if (TypeManager.GetParameterData (mi).Count == 0){
5366 if (mi.ReturnType == TypeManager.void_type)
5373 public void Error_Enumerator ()
5375 if (enumerator_found) {
5379 Report.Error (1579, loc,
5380 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5381 TypeManager.CSharpName (expr.Type));
5384 bool IsOverride (MethodInfo m)
5386 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5388 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5390 if (m is MethodBuilder)
5393 MethodInfo base_method = m.GetBaseDefinition ();
5394 return base_method != m;
5397 bool TryType (EmitContext ec, Type t)
5399 MethodGroupExpr mg = Expression.MemberLookup (
5400 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5401 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5405 MethodInfo result = null;
5406 MethodInfo tmp_move_next = null;
5407 PropertyExpr tmp_get_cur = null;
5408 Type tmp_enumerator_type = enumerator_type;
5409 foreach (MethodInfo mi in mg.Methods) {
5410 if (TypeManager.GetParameterData (mi).Count != 0)
5413 // Check whether GetEnumerator is public
5414 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5417 if (IsOverride (mi))
5420 enumerator_found = true;
5422 if (!GetEnumeratorFilter (ec, mi))
5425 if (result != null) {
5426 if (TypeManager.IsGenericType (result.ReturnType)) {
5427 if (!TypeManager.IsGenericType (mi.ReturnType))
5430 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5431 Report.SymbolRelatedToPreviousError (t);
5432 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5433 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5434 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5438 // Always prefer generics enumerators
5439 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5440 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5441 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5444 Report.SymbolRelatedToPreviousError (result);
5445 Report.SymbolRelatedToPreviousError (mi);
5446 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5447 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5452 tmp_move_next = move_next;
5453 tmp_get_cur = get_current;
5454 tmp_enumerator_type = enumerator_type;
5455 if (mi.DeclaringType == t)
5459 if (result != null) {
5460 move_next = tmp_move_next;
5461 get_current = tmp_get_cur;
5462 enumerator_type = tmp_enumerator_type;
5463 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5464 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5466 if (t != expr.Type) {
5467 expr = Convert.ExplicitConversion (
5470 throw new InternalErrorException ();
5473 get_enumerator.InstanceExpression = expr;
5474 get_enumerator.IsBase = t != expr.Type;
5482 bool ProbeCollectionType (EmitContext ec, Type t)
5484 int errors = Report.Errors;
5485 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5486 if (TryType (ec, tt))
5491 if (Report.Errors > errors)
5495 // Now try to find the method in the interfaces
5497 Type [] ifaces = TypeManager.GetInterfaces (t);
5498 foreach (Type i in ifaces){
5499 if (TryType (ec, i))
5506 public override bool Resolve (EmitContext ec)
5508 enumerator_type = TypeManager.ienumerator_type;
5510 if (!ProbeCollectionType (ec, expr.Type)) {
5511 Error_Enumerator ();
5515 bool is_disposable = !enumerator_type.IsSealed ||
5516 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5518 VarExpr ve = var_type as VarExpr;
5520 // Infer implicitly typed local variable from foreach enumerable type
5521 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5524 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5525 if (var_type == null)
5528 enumerator = new TemporaryVariable (enumerator_type, loc);
5529 enumerator.Resolve (ec);
5531 init = new Invocation (get_enumerator, null);
5532 init = init.Resolve (ec);
5536 Expression move_next_expr;
5538 MemberInfo[] mi = new MemberInfo[] { move_next };
5539 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5540 mg.InstanceExpression = enumerator;
5542 move_next_expr = new Invocation (mg, null);
5545 get_current.InstanceExpression = enumerator;
5547 Statement block = new CollectionForeachStatement (
5548 var_type.Type, variable, get_current, statement, loc);
5550 loop = new While (move_next_expr, block, loc);
5552 wrapper = is_disposable ?
5553 (Statement) new DisposableWrapper (this) :
5554 (Statement) new NonDisposableWrapper (this);
5555 return wrapper.Resolve (ec);
5558 protected override void DoEmit (EmitContext ec)
5563 class NonDisposableWrapper : Statement {
5564 CollectionForeach parent;
5566 internal NonDisposableWrapper (CollectionForeach parent)
5568 this.parent = parent;
5571 public override bool Resolve (EmitContext ec)
5573 return parent.ResolveLoop (ec);
5576 protected override void DoEmit (EmitContext ec)
5578 parent.EmitLoopInit (ec);
5579 parent.EmitLoopBody (ec);
5583 class DisposableWrapper : ExceptionStatement {
5584 CollectionForeach parent;
5586 internal DisposableWrapper (CollectionForeach parent)
5588 this.parent = parent;
5591 public override bool Resolve (EmitContext ec)
5595 ec.StartFlowBranching (this);
5597 if (!parent.ResolveLoop (ec))
5600 ec.EndFlowBranching ();
5602 ResolveReachability (ec);
5604 if (TypeManager.void_dispose_void == null) {
5605 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5606 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5611 protected override void EmitPreTryBody (EmitContext ec)
5613 parent.EmitLoopInit (ec);
5616 protected override void EmitTryBody (EmitContext ec)
5618 parent.EmitLoopBody (ec);
5621 protected override void EmitFinallyBody (EmitContext ec)
5623 parent.EmitFinallyBody (ec);
5627 bool ResolveLoop (EmitContext ec)
5629 return loop.Resolve (ec);
5632 void EmitLoopInit (EmitContext ec)
5634 enumerator.Store (ec, init);
5637 void EmitLoopBody (EmitContext ec)
5642 void EmitFinallyBody (EmitContext ec)
5644 ILGenerator ig = ec.ig;
5646 if (enumerator_type.IsValueType) {
5647 MethodInfo mi = FetchMethodDispose (enumerator_type);
5649 enumerator.EmitLoadAddress (ec);
5650 ig.Emit (OpCodes.Call, mi);
5652 enumerator.Emit (ec);
5653 ig.Emit (OpCodes.Box, enumerator_type);
5654 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5657 Label call_dispose = ig.DefineLabel ();
5659 enumerator.Emit (ec);
5660 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5661 ig.Emit (OpCodes.Dup);
5662 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5664 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5665 // (Partition III, Section 3.35)
5666 ig.Emit (OpCodes.Endfinally);
5668 ig.MarkLabel (call_dispose);
5669 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5674 protected class CollectionForeachStatement : Statement
5677 Expression variable, current, conv;
5678 Statement statement;
5681 public CollectionForeachStatement (Type type, Expression variable,
5682 Expression current, Statement statement,
5686 this.variable = variable;
5687 this.current = current;
5688 this.statement = statement;
5692 public override bool Resolve (EmitContext ec)
5694 current = current.Resolve (ec);
5695 if (current == null)
5698 conv = Convert.ExplicitConversion (ec, current, type, loc);
5702 assign = new Assign (variable, conv, loc);
5703 if (assign.Resolve (ec) == null)
5706 if (!statement.Resolve (ec))
5712 protected override void DoEmit (EmitContext ec)
5714 assign.EmitStatement (ec);
5715 statement.Emit (ec);
5719 protected override void CloneTo (CloneContext clonectx, Statement t)
5721 Foreach target = (Foreach) t;
5723 target.type = type.Clone (clonectx);
5724 target.variable = variable.Clone (clonectx);
5725 target.expr = expr.Clone (clonectx);
5726 target.statement = statement.Clone (clonectx);