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;
4829 ExpressionStatement assign;
4831 public Using (Expression var, Expression init, Statement stmt, Location l)
4839 bool ResolveVariable (EmitContext ec)
4841 ExpressionStatement a = new Assign (var, init, loc);
4842 a = a.ResolveStatement (ec);
4848 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4849 converted_var = var;
4853 Expression e = 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 assign.EmitStatement (ec);
4876 protected override void EmitTryBody (EmitContext ec)
4881 protected override void EmitFinallyBody (EmitContext ec)
4883 ILGenerator ig = ec.ig;
4885 if (!var.Type.IsValueType) {
4886 Label skip = ig.DefineLabel ();
4888 ig.Emit (OpCodes.Brfalse, skip);
4889 converted_var.Emit (ec);
4890 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4891 ig.MarkLabel (skip);
4893 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4895 if (!(ml is MethodGroupExpr)) {
4897 ig.Emit (OpCodes.Box, var.Type);
4898 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4900 MethodInfo mi = null;
4902 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4903 if (TypeManager.GetParameterData (mk).Count == 0) {
4910 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4914 IMemoryLocation mloc = (IMemoryLocation) var;
4916 mloc.AddressOf (ec, AddressOp.Load);
4917 ig.Emit (OpCodes.Call, mi);
4922 public override bool Resolve (EmitContext ec)
4924 if (!ResolveVariable (ec))
4927 ec.StartFlowBranching (this);
4929 bool ok = stmt.Resolve (ec);
4931 ec.EndFlowBranching ();
4933 ResolveReachability (ec);
4935 if (TypeManager.void_dispose_void == null) {
4936 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4937 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4943 protected override void CloneTo (CloneContext clonectx, Statement t)
4945 Using target = (Using) t;
4947 target.var = var.Clone (clonectx);
4948 target.init = init.Clone (clonectx);
4949 target.stmt = stmt.Clone (clonectx);
4954 /// Implementation of the foreach C# statement
4956 public class Foreach : Statement {
4958 Expression variable;
4960 Statement statement;
4962 CollectionForeach collection;
4964 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4965 Statement stmt, Location l)
4968 this.variable = var;
4974 public Statement Statement {
4975 get { return statement; }
4978 public override bool Resolve (EmitContext ec)
4980 expr = expr.Resolve (ec);
4985 Report.Error (186, loc, "Use of null is not valid in this context");
4989 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4990 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4991 expr.ExprClassName);
4996 // We need an instance variable. Not sure this is the best
4997 // way of doing this.
4999 // FIXME: When we implement propertyaccess, will those turn
5000 // out to return values in ExprClass? I think they should.
5002 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
5003 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
5004 collection.Error_Enumerator ();
5008 if (expr.Type.IsArray) {
5009 array = new ArrayForeach (type, variable, expr, statement, loc);
5010 return array.Resolve (ec);
5013 collection = new CollectionForeach (type, variable, expr, statement, loc);
5014 return collection.Resolve (ec);
5017 protected override void DoEmit (EmitContext ec)
5019 ILGenerator ig = ec.ig;
5021 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5022 ec.LoopBegin = ig.DefineLabel ();
5023 ec.LoopEnd = ig.DefineLabel ();
5025 if (collection != null)
5026 collection.Emit (ec);
5030 ec.LoopBegin = old_begin;
5031 ec.LoopEnd = old_end;
5034 protected class ArrayCounter : TemporaryVariable
5036 public ArrayCounter (Location loc)
5037 : base (TypeManager.int32_type, loc)
5040 public void Initialize (EmitContext ec)
5043 ec.ig.Emit (OpCodes.Ldc_I4_0);
5047 public void Increment (EmitContext ec)
5051 ec.ig.Emit (OpCodes.Ldc_I4_1);
5052 ec.ig.Emit (OpCodes.Add);
5057 protected class ArrayForeach : Statement
5059 Expression variable, expr, conv;
5060 Statement statement;
5062 Expression var_type;
5063 TemporaryVariable[] lengths;
5064 ArrayCounter[] counter;
5067 TemporaryVariable copy;
5070 public ArrayForeach (Expression var_type, Expression var,
5071 Expression expr, Statement stmt, Location l)
5073 this.var_type = var_type;
5074 this.variable = var;
5080 public override bool Resolve (EmitContext ec)
5082 array_type = expr.Type;
5083 rank = array_type.GetArrayRank ();
5085 copy = new TemporaryVariable (array_type, loc);
5088 counter = new ArrayCounter [rank];
5089 lengths = new TemporaryVariable [rank];
5091 ArrayList list = new ArrayList ();
5092 for (int i = 0; i < rank; i++) {
5093 counter [i] = new ArrayCounter (loc);
5094 counter [i].Resolve (ec);
5096 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5097 lengths [i].Resolve (ec);
5099 list.Add (counter [i]);
5102 access = new ElementAccess (copy, list).Resolve (ec);
5106 VarExpr ve = var_type as VarExpr;
5108 // Infer implicitly typed local variable from foreach array type
5109 var_type = new TypeExpression (access.Type, ve.Location);
5112 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5113 if (var_type == null)
5116 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5122 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5123 ec.CurrentBranching.CreateSibling ();
5125 variable = variable.ResolveLValue (ec, conv, loc);
5126 if (variable == null)
5129 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5130 if (!statement.Resolve (ec))
5132 ec.EndFlowBranching ();
5134 // There's no direct control flow from the end of the embedded statement to the end of the loop
5135 ec.CurrentBranching.CurrentUsageVector.Goto ();
5137 ec.EndFlowBranching ();
5142 protected override void DoEmit (EmitContext ec)
5144 ILGenerator ig = ec.ig;
5146 copy.Store (ec, expr);
5148 Label[] test = new Label [rank];
5149 Label[] loop = new Label [rank];
5151 for (int i = 0; i < rank; i++) {
5152 test [i] = ig.DefineLabel ();
5153 loop [i] = ig.DefineLabel ();
5155 lengths [i].EmitThis (ec);
5156 ((ArrayAccess) access).EmitGetLength (ec, i);
5157 lengths [i].EmitStore (ec);
5160 for (int i = 0; i < rank; i++) {
5161 counter [i].Initialize (ec);
5163 ig.Emit (OpCodes.Br, test [i]);
5164 ig.MarkLabel (loop [i]);
5167 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5169 statement.Emit (ec);
5171 ig.MarkLabel (ec.LoopBegin);
5173 for (int i = rank - 1; i >= 0; i--){
5174 counter [i].Increment (ec);
5176 ig.MarkLabel (test [i]);
5177 counter [i].Emit (ec);
5178 lengths [i].Emit (ec);
5179 ig.Emit (OpCodes.Blt, loop [i]);
5182 ig.MarkLabel (ec.LoopEnd);
5186 protected class CollectionForeach : Statement
5188 Expression variable, expr;
5189 Statement statement;
5191 TemporaryVariable enumerator;
5196 MethodGroupExpr get_enumerator;
5197 PropertyExpr get_current;
5198 MethodInfo move_next;
5199 Expression var_type;
5200 Type enumerator_type;
5201 bool enumerator_found;
5203 public CollectionForeach (Expression var_type, Expression var,
5204 Expression expr, Statement stmt, Location l)
5206 this.var_type = var_type;
5207 this.variable = var;
5213 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5215 Type return_type = mi.ReturnType;
5217 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5219 // Apply the same optimization as MS: skip the GetEnumerator
5220 // returning an IEnumerator, and use the one returning a
5221 // CharEnumerator instead. This allows us to avoid the
5222 // try-finally block and the boxing.
5227 // Ok, we can access it, now make sure that we can do something
5228 // with this `GetEnumerator'
5231 if (return_type == TypeManager.ienumerator_type ||
5232 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5233 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5235 // If it is not an interface, lets try to find the methods ourselves.
5236 // For example, if we have:
5237 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5238 // We can avoid the iface call. This is a runtime perf boost.
5239 // even bigger if we have a ValueType, because we avoid the cost
5242 // We have to make sure that both methods exist for us to take
5243 // this path. If one of the methods does not exist, we will just
5244 // use the interface. Sadly, this complex if statement is the only
5245 // way I could do this without a goto
5248 if (TypeManager.bool_movenext_void == null) {
5249 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5250 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5253 if (TypeManager.ienumerator_getcurrent == null) {
5254 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5255 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5260 // Prefer a generic enumerator over a non-generic one.
5262 if (return_type.IsInterface && return_type.IsGenericType) {
5263 enumerator_type = return_type;
5264 if (!FetchGetCurrent (ec, return_type))
5265 get_current = new PropertyExpr (
5266 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5267 if (!FetchMoveNext (return_type))
5268 move_next = TypeManager.bool_movenext_void;
5273 if (return_type.IsInterface ||
5274 !FetchMoveNext (return_type) ||
5275 !FetchGetCurrent (ec, return_type)) {
5276 enumerator_type = return_type;
5277 move_next = TypeManager.bool_movenext_void;
5278 get_current = new PropertyExpr (
5279 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5284 // Ok, so they dont return an IEnumerable, we will have to
5285 // find if they support the GetEnumerator pattern.
5288 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5289 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",
5290 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5295 enumerator_type = return_type;
5301 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5303 bool FetchMoveNext (Type t)
5305 MemberList move_next_list;
5307 move_next_list = TypeContainer.FindMembers (
5308 t, MemberTypes.Method,
5309 BindingFlags.Public | BindingFlags.Instance,
5310 Type.FilterName, "MoveNext");
5311 if (move_next_list.Count == 0)
5314 foreach (MemberInfo m in move_next_list){
5315 MethodInfo mi = (MethodInfo) m;
5317 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5318 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5328 // Retrieves a `public T get_Current ()' method from the Type `t'
5330 bool FetchGetCurrent (EmitContext ec, Type t)
5332 PropertyExpr pe = Expression.MemberLookup (
5333 ec.ContainerType, t, "Current", MemberTypes.Property,
5334 Expression.AllBindingFlags, loc) as PropertyExpr;
5343 // Retrieves a `public void Dispose ()' method from the Type `t'
5345 static MethodInfo FetchMethodDispose (Type t)
5347 MemberList dispose_list;
5349 dispose_list = TypeContainer.FindMembers (
5350 t, MemberTypes.Method,
5351 BindingFlags.Public | BindingFlags.Instance,
5352 Type.FilterName, "Dispose");
5353 if (dispose_list.Count == 0)
5356 foreach (MemberInfo m in dispose_list){
5357 MethodInfo mi = (MethodInfo) m;
5359 if (TypeManager.GetParameterData (mi).Count == 0){
5360 if (mi.ReturnType == TypeManager.void_type)
5367 public void Error_Enumerator ()
5369 if (enumerator_found) {
5373 Report.Error (1579, loc,
5374 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5375 TypeManager.CSharpName (expr.Type));
5378 bool IsOverride (MethodInfo m)
5380 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5382 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5384 if (m is MethodBuilder)
5387 MethodInfo base_method = m.GetBaseDefinition ();
5388 return base_method != m;
5391 bool TryType (EmitContext ec, Type t)
5393 MethodGroupExpr mg = Expression.MemberLookup (
5394 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5395 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5399 MethodInfo result = null;
5400 MethodInfo tmp_move_next = null;
5401 PropertyExpr tmp_get_cur = null;
5402 Type tmp_enumerator_type = enumerator_type;
5403 foreach (MethodInfo mi in mg.Methods) {
5404 if (TypeManager.GetParameterData (mi).Count != 0)
5407 // Check whether GetEnumerator is public
5408 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5411 if (IsOverride (mi))
5414 enumerator_found = true;
5416 if (!GetEnumeratorFilter (ec, mi))
5419 if (result != null) {
5420 if (TypeManager.IsGenericType (result.ReturnType)) {
5421 if (!TypeManager.IsGenericType (mi.ReturnType))
5424 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5425 Report.SymbolRelatedToPreviousError (t);
5426 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5427 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5428 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5432 // Always prefer generics enumerators
5433 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5434 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5435 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5438 Report.SymbolRelatedToPreviousError (result);
5439 Report.SymbolRelatedToPreviousError (mi);
5440 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5441 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5446 tmp_move_next = move_next;
5447 tmp_get_cur = get_current;
5448 tmp_enumerator_type = enumerator_type;
5449 if (mi.DeclaringType == t)
5453 if (result != null) {
5454 move_next = tmp_move_next;
5455 get_current = tmp_get_cur;
5456 enumerator_type = tmp_enumerator_type;
5457 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5458 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5460 if (t != expr.Type) {
5461 expr = Convert.ExplicitConversion (
5464 throw new InternalErrorException ();
5467 get_enumerator.InstanceExpression = expr;
5468 get_enumerator.IsBase = t != expr.Type;
5476 bool ProbeCollectionType (EmitContext ec, Type t)
5478 int errors = Report.Errors;
5479 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5480 if (TryType (ec, tt))
5485 if (Report.Errors > errors)
5489 // Now try to find the method in the interfaces
5491 Type [] ifaces = TypeManager.GetInterfaces (t);
5492 foreach (Type i in ifaces){
5493 if (TryType (ec, i))
5500 public override bool Resolve (EmitContext ec)
5502 enumerator_type = TypeManager.ienumerator_type;
5504 if (!ProbeCollectionType (ec, expr.Type)) {
5505 Error_Enumerator ();
5509 bool is_disposable = !enumerator_type.IsSealed ||
5510 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5512 VarExpr ve = var_type as VarExpr;
5514 // Infer implicitly typed local variable from foreach enumerable type
5515 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5518 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5519 if (var_type == null)
5522 enumerator = new TemporaryVariable (enumerator_type, loc);
5523 enumerator.Resolve (ec);
5525 init = new Invocation (get_enumerator, null);
5526 init = init.Resolve (ec);
5530 Expression move_next_expr;
5532 MemberInfo[] mi = new MemberInfo[] { move_next };
5533 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5534 mg.InstanceExpression = enumerator;
5536 move_next_expr = new Invocation (mg, null);
5539 get_current.InstanceExpression = enumerator;
5541 Statement block = new CollectionForeachStatement (
5542 var_type.Type, variable, get_current, statement, loc);
5544 loop = new While (move_next_expr, block, loc);
5546 wrapper = is_disposable ?
5547 (Statement) new DisposableWrapper (this) :
5548 (Statement) new NonDisposableWrapper (this);
5549 return wrapper.Resolve (ec);
5552 protected override void DoEmit (EmitContext ec)
5557 class NonDisposableWrapper : Statement {
5558 CollectionForeach parent;
5560 internal NonDisposableWrapper (CollectionForeach parent)
5562 this.parent = parent;
5565 public override bool Resolve (EmitContext ec)
5567 return parent.ResolveLoop (ec);
5570 protected override void DoEmit (EmitContext ec)
5572 parent.EmitLoopInit (ec);
5573 parent.EmitLoopBody (ec);
5577 class DisposableWrapper : ExceptionStatement {
5578 CollectionForeach parent;
5580 internal DisposableWrapper (CollectionForeach parent)
5582 this.parent = parent;
5585 public override bool Resolve (EmitContext ec)
5589 ec.StartFlowBranching (this);
5591 if (!parent.ResolveLoop (ec))
5594 ec.EndFlowBranching ();
5596 ResolveReachability (ec);
5598 if (TypeManager.void_dispose_void == null) {
5599 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5600 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5605 protected override void EmitPreTryBody (EmitContext ec)
5607 parent.EmitLoopInit (ec);
5610 protected override void EmitTryBody (EmitContext ec)
5612 parent.EmitLoopBody (ec);
5615 protected override void EmitFinallyBody (EmitContext ec)
5617 parent.EmitFinallyBody (ec);
5621 bool ResolveLoop (EmitContext ec)
5623 return loop.Resolve (ec);
5626 void EmitLoopInit (EmitContext ec)
5628 enumerator.Store (ec, init);
5631 void EmitLoopBody (EmitContext ec)
5636 void EmitFinallyBody (EmitContext ec)
5638 ILGenerator ig = ec.ig;
5640 if (enumerator_type.IsValueType) {
5641 MethodInfo mi = FetchMethodDispose (enumerator_type);
5643 enumerator.EmitLoadAddress (ec);
5644 ig.Emit (OpCodes.Call, mi);
5646 enumerator.Emit (ec);
5647 ig.Emit (OpCodes.Box, enumerator_type);
5648 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5651 Label call_dispose = ig.DefineLabel ();
5653 enumerator.Emit (ec);
5654 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5655 ig.Emit (OpCodes.Dup);
5656 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5658 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5659 // (Partition III, Section 3.35)
5660 ig.Emit (OpCodes.Endfinally);
5662 ig.MarkLabel (call_dispose);
5663 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5668 protected class CollectionForeachStatement : Statement
5671 Expression variable, current, conv;
5672 Statement statement;
5675 public CollectionForeachStatement (Type type, Expression variable,
5676 Expression current, Statement statement,
5680 this.variable = variable;
5681 this.current = current;
5682 this.statement = statement;
5686 public override bool Resolve (EmitContext ec)
5688 current = current.Resolve (ec);
5689 if (current == null)
5692 conv = Convert.ExplicitConversion (ec, current, type, loc);
5696 assign = new Assign (variable, conv, loc);
5697 if (assign.Resolve (ec) == null)
5700 if (!statement.Resolve (ec))
5706 protected override void DoEmit (EmitContext ec)
5708 assign.EmitStatement (ec);
5709 statement.Emit (ec);
5713 protected override void CloneTo (CloneContext clonectx, Statement t)
5715 Foreach target = (Foreach) t;
5717 target.type = type.Clone (clonectx);
5718 target.variable = variable.Clone (clonectx);
5719 target.expr = expr.Clone (clonectx);
5720 target.statement = statement.Clone (clonectx);