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 public sealed class EmptyStatement : Statement {
93 private EmptyStatement () {}
95 public static readonly EmptyStatement Value = new EmptyStatement ();
97 public override bool Resolve (EmitContext ec)
102 public override bool ResolveUnreachable (EmitContext ec, bool warn)
107 protected override void DoEmit (EmitContext ec)
112 public class If : Statement {
114 public Statement TrueStatement;
115 public Statement FalseStatement;
119 public If (Expression expr, Statement trueStatement, Location l)
122 TrueStatement = trueStatement;
126 public If (Expression expr,
127 Statement trueStatement,
128 Statement falseStatement,
132 TrueStatement = trueStatement;
133 FalseStatement = falseStatement;
137 public override bool Resolve (EmitContext ec)
141 Report.Debug (1, "START IF BLOCK", loc);
143 expr = Expression.ResolveBoolean (ec, expr, loc);
149 Assign ass = expr as Assign;
150 if (ass != null && ass.Source is Constant) {
151 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
155 // Dead code elimination
157 if (expr is BoolConstant){
158 bool take = ((BoolConstant) expr).Value;
161 if (!TrueStatement.Resolve (ec))
164 if ((FalseStatement != null) &&
165 !FalseStatement.ResolveUnreachable (ec, true))
167 FalseStatement = null;
169 if (!TrueStatement.ResolveUnreachable (ec, true))
171 TrueStatement = null;
173 if ((FalseStatement != null) &&
174 !FalseStatement.Resolve (ec))
181 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
183 ok &= TrueStatement.Resolve (ec);
185 is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
187 ec.CurrentBranching.CreateSibling ();
189 if (FalseStatement != null)
190 ok &= FalseStatement.Resolve (ec);
192 ec.EndFlowBranching ();
194 Report.Debug (1, "END IF BLOCK", loc);
199 protected override void DoEmit (EmitContext ec)
201 ILGenerator ig = ec.ig;
202 Label false_target = ig.DefineLabel ();
206 // If we're a boolean expression, Resolve() already
207 // eliminated dead code for us.
209 if (expr is BoolConstant){
210 bool take = ((BoolConstant) expr).Value;
213 TrueStatement.Emit (ec);
214 else if (FalseStatement != null)
215 FalseStatement.Emit (ec);
220 expr.EmitBranchable (ec, false_target, false);
222 TrueStatement.Emit (ec);
224 if (FalseStatement != null){
225 bool branch_emitted = false;
227 end = ig.DefineLabel ();
229 ig.Emit (OpCodes.Br, end);
230 branch_emitted = true;
233 ig.MarkLabel (false_target);
234 FalseStatement.Emit (ec);
239 ig.MarkLabel (false_target);
244 public class Do : Statement {
245 public Expression expr;
246 public readonly Statement EmbeddedStatement;
249 public Do (Statement statement, Expression boolExpr, Location l)
252 EmbeddedStatement = statement;
256 public override bool Resolve (EmitContext ec)
260 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
262 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
264 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
265 if (!EmbeddedStatement.Resolve (ec))
267 ec.EndFlowBranching ();
269 if (ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable && !was_unreachable)
270 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
272 expr = Expression.ResolveBoolean (ec, expr, loc);
275 else if (expr is BoolConstant){
276 bool res = ((BoolConstant) expr).Value;
282 ec.CurrentBranching.CurrentUsageVector.Goto ();
284 ec.EndFlowBranching ();
289 protected override void DoEmit (EmitContext ec)
291 ILGenerator ig = ec.ig;
292 Label loop = ig.DefineLabel ();
293 Label old_begin = ec.LoopBegin;
294 Label old_end = ec.LoopEnd;
296 ec.LoopBegin = ig.DefineLabel ();
297 ec.LoopEnd = ig.DefineLabel ();
300 EmbeddedStatement.Emit (ec);
301 ig.MarkLabel (ec.LoopBegin);
304 // Dead code elimination
306 if (expr is BoolConstant){
307 bool res = ((BoolConstant) expr).Value;
310 ec.ig.Emit (OpCodes.Br, loop);
312 expr.EmitBranchable (ec, loop, true);
314 ig.MarkLabel (ec.LoopEnd);
316 ec.LoopBegin = old_begin;
317 ec.LoopEnd = old_end;
321 public class While : Statement {
322 public Expression expr;
323 public readonly Statement Statement;
324 bool infinite, empty;
326 public While (Expression boolExpr, Statement statement, Location l)
328 this.expr = boolExpr;
329 Statement = statement;
333 public override bool Resolve (EmitContext ec)
337 expr = Expression.ResolveBoolean (ec, expr, loc);
342 // Inform whether we are infinite or not
344 if (expr is BoolConstant){
345 BoolConstant bc = (BoolConstant) expr;
347 if (bc.Value == false){
348 if (!Statement.ResolveUnreachable (ec, true))
356 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
358 ec.CurrentBranching.CreateSibling ();
360 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
361 if (!Statement.Resolve (ec))
363 ec.EndFlowBranching ();
365 // There's no direct control flow from the end of the embedded statement to the end of the loop
366 ec.CurrentBranching.CurrentUsageVector.Goto ();
368 ec.EndFlowBranching ();
373 protected override void DoEmit (EmitContext ec)
378 ILGenerator ig = ec.ig;
379 Label old_begin = ec.LoopBegin;
380 Label old_end = ec.LoopEnd;
382 ec.LoopBegin = ig.DefineLabel ();
383 ec.LoopEnd = ig.DefineLabel ();
386 // Inform whether we are infinite or not
388 if (expr is BoolConstant){
389 ig.MarkLabel (ec.LoopBegin);
391 ig.Emit (OpCodes.Br, ec.LoopBegin);
394 // Inform that we are infinite (ie, `we return'), only
395 // if we do not `break' inside the code.
397 ig.MarkLabel (ec.LoopEnd);
399 Label while_loop = ig.DefineLabel ();
401 ig.Emit (OpCodes.Br, ec.LoopBegin);
402 ig.MarkLabel (while_loop);
406 ig.MarkLabel (ec.LoopBegin);
408 expr.EmitBranchable (ec, while_loop, true);
410 ig.MarkLabel (ec.LoopEnd);
413 ec.LoopBegin = old_begin;
414 ec.LoopEnd = old_end;
418 public class For : Statement {
420 readonly Statement InitStatement;
421 readonly Statement Increment;
422 public readonly Statement Statement;
423 bool infinite, empty;
425 public For (Statement initStatement,
431 InitStatement = initStatement;
433 Increment = increment;
434 Statement = statement;
438 public override bool Resolve (EmitContext ec)
442 if (InitStatement != null){
443 if (!InitStatement.Resolve (ec))
448 Test = Expression.ResolveBoolean (ec, Test, loc);
451 else if (Test is BoolConstant){
452 BoolConstant bc = (BoolConstant) Test;
454 if (bc.Value == false){
455 if (!Statement.ResolveUnreachable (ec, true))
457 if ((Increment != null) &&
458 !Increment.ResolveUnreachable (ec, false))
468 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
470 ec.CurrentBranching.CreateSibling ();
472 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
474 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
475 if (!Statement.Resolve (ec))
477 ec.EndFlowBranching ();
479 if (Increment != null){
480 if (ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable) {
481 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
484 if (!Increment.Resolve (ec))
489 // There's no direct control flow from the end of the embedded statement to the end of the loop
490 ec.CurrentBranching.CurrentUsageVector.Goto ();
492 ec.EndFlowBranching ();
497 protected override void DoEmit (EmitContext ec)
502 ILGenerator ig = ec.ig;
503 Label old_begin = ec.LoopBegin;
504 Label old_end = ec.LoopEnd;
505 Label loop = ig.DefineLabel ();
506 Label test = ig.DefineLabel ();
508 if (InitStatement != null && InitStatement != EmptyStatement.Value)
509 InitStatement.Emit (ec);
511 ec.LoopBegin = ig.DefineLabel ();
512 ec.LoopEnd = ig.DefineLabel ();
514 ig.Emit (OpCodes.Br, test);
518 ig.MarkLabel (ec.LoopBegin);
519 if (Increment != EmptyStatement.Value)
524 // If test is null, there is no test, and we are just
529 // The Resolve code already catches the case for
530 // Test == BoolConstant (false) so we know that
533 if (Test is BoolConstant)
534 ig.Emit (OpCodes.Br, loop);
536 Test.EmitBranchable (ec, loop, true);
539 ig.Emit (OpCodes.Br, loop);
540 ig.MarkLabel (ec.LoopEnd);
542 ec.LoopBegin = old_begin;
543 ec.LoopEnd = old_end;
547 public class StatementExpression : Statement {
548 ExpressionStatement expr;
550 public StatementExpression (ExpressionStatement expr)
556 public override bool Resolve (EmitContext ec)
559 expr = expr.ResolveStatement (ec);
563 protected override void DoEmit (EmitContext ec)
565 expr.EmitStatement (ec);
568 public override string ToString ()
570 return "StatementExpression (" + expr + ")";
575 /// Implements the return statement
577 public class Return : Statement {
578 public Expression Expr;
580 public Return (Expression expr, Location l)
588 public override bool Resolve (EmitContext ec)
590 AnonymousContainer am = ec.CurrentAnonymousMethod;
591 if ((am != null) && am.IsIterator && ec.InIterator) {
592 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
593 "statement to return a value, or yield break to end the iteration");
597 if (ec.ReturnType == null){
599 if (ec.CurrentAnonymousMethod != null){
600 Report.Error (1662, loc,
601 "Cannot convert anonymous method block to delegate type `{0}' because some of the return types in the block are not implicitly convertible to the delegate return type",
602 ec.CurrentAnonymousMethod.GetSignatureForError ());
604 Error (127, "A return keyword must not be followed by any expression when method returns void");
609 Error (126, "An object of a type convertible to `{0}' is required " +
610 "for the return statement",
611 TypeManager.CSharpName (ec.ReturnType));
615 Expr = Expr.Resolve (ec);
619 if (Expr.Type != ec.ReturnType) {
620 Expr = Convert.ImplicitConversionRequired (
621 ec, Expr, ec.ReturnType, loc);
627 int errors = Report.Errors;
628 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
630 ec.NeedReturnLabel ();
631 ec.CurrentBranching.CurrentUsageVector.Return ();
632 return errors == Report.Errors;
635 protected override void DoEmit (EmitContext ec)
641 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
645 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
647 ec.ig.Emit (OpCodes.Ret);
651 public class Goto : Statement {
653 LabeledStatement label;
656 public override bool Resolve (EmitContext ec)
658 int errors = Report.Errors;
659 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
660 ec.CurrentBranching.CurrentUsageVector.Goto ();
661 return errors == Report.Errors;
664 public Goto (string label, Location l)
670 public string Target {
671 get { return target; }
674 public void SetResolvedTarget (LabeledStatement label)
677 label.AddReference ();
680 protected override void DoEmit (EmitContext ec)
683 throw new InternalErrorException ("goto emitted before target resolved");
684 Label l = label.LabelTarget (ec);
685 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
689 public class LabeledStatement : Statement {
696 FlowBranching.UsageVector vectors;
698 public LabeledStatement (string name, Location l)
704 public Label LabelTarget (EmitContext ec)
709 label = ec.ig.DefineLabel ();
719 public bool IsDefined {
720 get { return defined; }
723 public bool HasBeenReferenced {
724 get { return referenced; }
727 public FlowBranching.UsageVector JumpOrigins {
728 get { return vectors; }
731 public void AddUsageVector (FlowBranching.UsageVector vector)
733 vector = vector.Clone ();
734 vector.Next = vectors;
738 public override bool Resolve (EmitContext ec)
740 // this flow-branching will be terminated when the surrounding block ends
741 ec.StartFlowBranching (this);
745 protected override void DoEmit (EmitContext ec)
747 if (ig != null && ig != ec.ig)
748 throw new InternalErrorException ("cannot happen");
750 ec.ig.MarkLabel (label);
753 public void AddReference ()
761 /// `goto default' statement
763 public class GotoDefault : Statement {
765 public GotoDefault (Location l)
770 public override bool Resolve (EmitContext ec)
772 ec.CurrentBranching.CurrentUsageVector.Goto ();
776 protected override void DoEmit (EmitContext ec)
778 if (ec.Switch == null){
779 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
783 if (!ec.Switch.GotDefault){
784 Report.Error (159, loc, "No such label `default:' within the scope of the goto statement");
787 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
792 /// `goto case' statement
794 public class GotoCase : Statement {
798 public GotoCase (Expression e, Location l)
804 public override bool Resolve (EmitContext ec)
806 if (ec.Switch == null){
807 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
811 expr = expr.Resolve (ec);
815 Constant c = expr as Constant;
817 Error (150, "A constant value is expected");
821 Type type = ec.Switch.SwitchType;
822 if (!Convert.ImplicitStandardConversionExists (c, type))
823 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
824 "convertible to type `{0}'", TypeManager.CSharpName (type));
827 object val = c.GetValue ();
828 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
829 val = TypeManager.ChangeType (val, type, out fail);
832 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
833 c.GetSignatureForError (), TypeManager.CSharpName (type));
838 val = SwitchLabel.NullStringCase;
840 sl = (SwitchLabel) ec.Switch.Elements [val];
843 Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", c.GetValue () == null ? "null" : val.ToString ());
847 ec.CurrentBranching.CurrentUsageVector.Goto ();
851 protected override void DoEmit (EmitContext ec)
853 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
857 public class Throw : Statement {
860 public Throw (Expression expr, Location l)
866 public override bool Resolve (EmitContext ec)
868 ec.CurrentBranching.CurrentUsageVector.Throw ();
871 expr = expr.Resolve (ec);
875 ExprClass eclass = expr.eclass;
877 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
878 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
879 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
885 if ((t != TypeManager.exception_type) &&
886 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
887 !(expr is NullLiteral)) {
889 "The type caught or thrown must be derived " +
890 "from System.Exception");
897 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
902 Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
908 protected override void DoEmit (EmitContext ec)
911 ec.ig.Emit (OpCodes.Rethrow);
915 ec.ig.Emit (OpCodes.Throw);
920 public class Break : Statement {
922 public Break (Location l)
929 public override bool Resolve (EmitContext ec)
931 int errors = Report.Errors;
932 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
933 ec.CurrentBranching.CurrentUsageVector.Goto ();
934 return errors == Report.Errors;
937 protected override void DoEmit (EmitContext ec)
939 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
943 public class Continue : Statement {
945 public Continue (Location l)
952 public override bool Resolve (EmitContext ec)
954 int errors = Report.Errors;
955 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
956 ec.CurrentBranching.CurrentUsageVector.Goto ();
957 return errors == Report.Errors;
960 protected override void DoEmit (EmitContext ec)
962 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
966 public abstract class Variable
968 public abstract Type Type {
972 public abstract bool HasInstance {
976 public abstract bool NeedsTemporary {
980 public abstract void EmitInstance (EmitContext ec);
982 public abstract void Emit (EmitContext ec);
984 public abstract void EmitAssign (EmitContext ec);
986 public abstract void EmitAddressOf (EmitContext ec);
990 // The information about a user-perceived local variable
992 public class LocalInfo {
993 public Expression Type;
995 public Type VariableType;
996 public readonly string Name;
997 public readonly Location Location;
998 public readonly Block Block;
1000 public VariableInfo VariableInfo;
1003 public Variable Variable {
1015 CompilerGenerated = 64,
1019 public enum ReadOnlyContext: byte {
1026 ReadOnlyContext ro_context;
1027 LocalBuilder builder;
1029 public LocalInfo (Expression type, string name, Block block, Location l)
1037 public LocalInfo (DeclSpace ds, Block block, Location l)
1039 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1044 public void ResolveVariable (EmitContext ec)
1046 Block theblock = Block;
1047 if (theblock.ScopeInfo != null)
1048 var = theblock.ScopeInfo.GetCapturedVariable (this);
1053 // This is needed to compile on both .NET 1.x and .NET 2.x
1054 // the later introduced `DeclareLocal (Type t, bool pinned)'
1056 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1058 builder = ec.ig.DeclareLocal (VariableType);
1060 var = new LocalVariable (this, builder);
1064 public void EmitSymbolInfo (EmitContext ec, string name)
1066 if (builder != null)
1067 ec.DefineLocalVariable (name, builder);
1070 public bool IsThisAssigned (EmitContext ec, Location loc)
1072 if (VariableInfo == null)
1073 throw new Exception ();
1075 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1078 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
1081 public bool IsAssigned (EmitContext ec)
1083 if (VariableInfo == null)
1084 throw new Exception ();
1086 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1089 public bool Resolve (EmitContext ec)
1091 if (VariableType == null) {
1092 TypeExpr texpr = Type.ResolveAsTypeTerminal (ec, false);
1096 VariableType = texpr.Type;
1099 if (VariableType == TypeManager.void_type) {
1100 Expression.Error_VoidInvalidInTheContext (Location);
1104 if (VariableType.IsAbstract && VariableType.IsSealed) {
1105 FieldMember.Error_VariableOfStaticClass (Location, Name, VariableType);
1109 if (VariableType.IsPointer && !ec.InUnsafe)
1110 Expression.UnsafeError (Location);
1115 public bool IsCaptured {
1117 return (flags & Flags.Captured) != 0;
1121 flags |= Flags.Captured;
1125 public bool IsConstant {
1127 return (flags & Flags.IsConstant) != 0;
1130 flags |= Flags.IsConstant;
1134 public bool AddressTaken {
1136 return (flags & Flags.AddressTaken) != 0;
1140 flags |= Flags.AddressTaken;
1144 public bool CompilerGenerated {
1146 return (flags & Flags.CompilerGenerated) != 0;
1150 flags |= Flags.CompilerGenerated;
1154 public override string ToString ()
1156 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1157 Name, Type, VariableInfo, Location);
1162 return (flags & Flags.Used) != 0;
1165 flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
1169 public bool ReadOnly {
1171 return (flags & Flags.ReadOnly) != 0;
1175 public void SetReadOnlyContext (ReadOnlyContext context)
1177 flags |= Flags.ReadOnly;
1178 ro_context = context;
1181 public string GetReadOnlyContext ()
1184 throw new InternalErrorException ("Variable is not readonly");
1186 switch (ro_context) {
1187 case ReadOnlyContext.Fixed:
1188 return "fixed variable";
1189 case ReadOnlyContext.Foreach:
1190 return "foreach iteration variable";
1191 case ReadOnlyContext.Using:
1192 return "using variable";
1194 throw new NotImplementedException ();
1198 // Whether the variable is pinned, if Pinned the variable has been
1199 // allocated in a pinned slot with DeclareLocal.
1201 public bool Pinned {
1203 return (flags & Flags.Pinned) != 0;
1206 flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned);
1210 public bool IsThis {
1212 return (flags & Flags.IsThis) != 0;
1215 flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis);
1219 protected class LocalVariable : Variable
1221 public readonly LocalInfo LocalInfo;
1222 LocalBuilder builder;
1224 public LocalVariable (LocalInfo local, LocalBuilder builder)
1226 this.LocalInfo = local;
1227 this.builder = builder;
1230 public override Type Type {
1231 get { return LocalInfo.VariableType; }
1234 public override bool HasInstance {
1235 get { return false; }
1238 public override bool NeedsTemporary {
1239 get { return false; }
1242 public override void EmitInstance (EmitContext ec)
1247 public override void Emit (EmitContext ec)
1249 ec.ig.Emit (OpCodes.Ldloc, builder);
1252 public override void EmitAssign (EmitContext ec)
1254 ec.ig.Emit (OpCodes.Stloc, builder);
1257 public override void EmitAddressOf (EmitContext ec)
1259 ec.ig.Emit (OpCodes.Ldloca, builder);
1265 /// Block represents a C# block.
1269 /// This class is used in a number of places: either to represent
1270 /// explicit blocks that the programmer places or implicit blocks.
1272 /// Implicit blocks are used as labels or to introduce variable
1275 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1276 /// they contain extra information that is not necessary on normal blocks.
1278 public class Block : Statement {
1279 public Block Parent;
1280 public readonly Location StartLocation;
1281 public Location EndLocation = Location.Null;
1283 public readonly ToplevelBlock Toplevel;
1286 public enum Flags : ushort {
1290 VariablesInitialized = 8,
1295 HasVarargs = 256, // Used in ToplevelBlock
1299 protected Flags flags;
1301 public bool Implicit {
1302 get { return (flags & Flags.Implicit) != 0; }
1305 public bool Unchecked {
1306 get { return (flags & Flags.Unchecked) != 0; }
1307 set { flags |= Flags.Unchecked; }
1310 public bool Unsafe {
1311 get { return (flags & Flags.Unsafe) != 0; }
1312 set { flags |= Flags.Unsafe; }
1316 // The statements in this block
1318 protected ArrayList statements;
1322 // An array of Blocks. We keep track of children just
1323 // to generate the local variable declarations.
1325 // Statements and child statements are handled through the
1331 // Labels. (label, block) pairs.
1336 // Keeps track of (name, type) pairs
1338 IDictionary variables;
1341 // Keeps track of constants
1342 Hashtable constants;
1345 // Temporary variables.
1347 ArrayList temporary_variables;
1350 // If this is a switch section, the enclosing switch block.
1354 ExpressionStatement scope_init;
1356 ArrayList anonymous_children;
1358 protected static int id;
1362 public Block (Block parent)
1363 : this (parent, (Flags) 0, Location.Null, Location.Null)
1366 public Block (Block parent, Flags flags)
1367 : this (parent, flags, Location.Null, Location.Null)
1370 public Block (Block parent, Location start, Location end)
1371 : this (parent, (Flags) 0, start, end)
1374 public Block (Block parent, Flags flags, Location start, Location end)
1377 parent.AddChild (this);
1379 this.Parent = parent;
1381 this.StartLocation = start;
1382 this.EndLocation = end;
1385 statements = new ArrayList ();
1387 if ((flags & Flags.IsToplevel) != 0)
1388 Toplevel = (ToplevelBlock) this;
1390 Toplevel = parent.Toplevel;
1392 if (parent != null && Implicit) {
1393 if (parent.known_variables == null)
1394 parent.known_variables = new Hashtable ();
1395 // share with parent
1396 known_variables = parent.known_variables;
1400 public Block CreateSwitchBlock (Location start)
1402 Block new_block = new Block (this, start, start);
1403 new_block.switch_block = this;
1408 get { return this_id; }
1411 public IDictionary Variables {
1413 if (variables == null)
1414 variables = new ListDictionary ();
1419 void AddChild (Block b)
1421 if (children == null)
1422 children = new ArrayList ();
1427 public void SetEndLocation (Location loc)
1432 protected static void Error_158 (string name, Location loc)
1434 Report.Error (158, loc, "The label `{0}' shadows another label " +
1435 "by the same name in a contained scope.", name);
1439 /// Adds a label to the current block.
1443 /// false if the name already exists in this block. true
1447 public bool AddLabel (LabeledStatement target)
1449 if (switch_block != null)
1450 return switch_block.AddLabel (target);
1452 string name = target.Name;
1455 while (cur != null) {
1456 if (cur.DoLookupLabel (name) != null) {
1457 Report.Error (140, target.loc,
1458 "The label `{0}' is a duplicate", name);
1468 while (cur != null) {
1469 if (cur.DoLookupLabel (name) != null) {
1470 Error_158 (name, target.loc);
1474 if (children != null) {
1475 foreach (Block b in children) {
1476 LabeledStatement s = b.DoLookupLabel (name);
1480 Error_158 (name, target.loc);
1488 Toplevel.CheckError158 (name, target.loc);
1491 labels = new Hashtable ();
1493 labels.Add (name, target);
1497 public LabeledStatement LookupLabel (string name)
1499 LabeledStatement s = DoLookupLabel (name);
1503 if (children == null)
1506 foreach (Block child in children) {
1507 if (!child.Implicit)
1510 s = child.LookupLabel (name);
1518 LabeledStatement DoLookupLabel (string name)
1520 if (switch_block != null)
1521 return switch_block.LookupLabel (name);
1524 if (labels.Contains (name))
1525 return ((LabeledStatement) labels [name]);
1530 Hashtable known_variables;
1533 // Marks a variable with name @name as being used in this or a child block.
1534 // If a variable name has been used in a child block, it's illegal to
1535 // declare a variable with the same name in the current block.
1537 void AddKnownVariable (string name, LocalInfo info)
1539 if (known_variables == null)
1540 known_variables = new Hashtable ();
1542 known_variables [name] = info;
1545 LocalInfo GetKnownVariableInfo (string name, bool recurse)
1547 if (known_variables != null) {
1548 LocalInfo vi = (LocalInfo) known_variables [name];
1553 if (!recurse || (children == null))
1556 foreach (Block block in children) {
1557 LocalInfo vi = block.GetKnownVariableInfo (name, true);
1565 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1568 LocalInfo kvi = b.GetKnownVariableInfo (name, true);
1569 while (kvi == null) {
1575 kvi = b.GetKnownVariableInfo (name, false);
1581 // Is kvi.Block nested inside 'b'
1582 if (b.known_variables != kvi.Block.known_variables) {
1584 // If a variable by the same name it defined in a nested block of this
1585 // block, we violate the invariant meaning in a block.
1588 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1589 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1594 // It's ok if the definition is in a nested subblock of b, but not
1595 // nested inside this block -- a definition in a sibling block
1596 // should not affect us.
1602 // Block 'b' and kvi.Block are the same textual block.
1603 // However, different variables are extant.
1605 // Check if the variable is in scope in both blocks. We use
1606 // an indirect check that depends on AddVariable doing its
1607 // part in maintaining the invariant-meaning-in-block property.
1609 if (e is LocalVariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1613 // Even though we detected the error when the name is used, we
1614 // treat it as if the variable declaration was in error.
1616 Report.SymbolRelatedToPreviousError (loc, name);
1617 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1621 public bool CheckError136_InParents (string name, Location loc)
1623 for (Block b = Parent; b != null; b = b.Parent) {
1624 if (!b.DoCheckError136 (name, "parent or current", loc))
1628 for (Block b = Toplevel.ContainerBlock; b != null; b = b.Toplevel.ContainerBlock) {
1629 if (!b.CheckError136_InParents (name, loc))
1636 public bool CheckError136_InChildren (string name, Location loc)
1638 if (!DoCheckError136_InChildren (name, loc))
1642 while (b.Implicit) {
1643 if (!b.Parent.DoCheckError136_InChildren (name, loc))
1651 protected bool DoCheckError136_InChildren (string name, Location loc)
1653 if (!DoCheckError136 (name, "child", loc))
1656 if (AnonymousChildren != null) {
1657 foreach (ToplevelBlock child in AnonymousChildren) {
1658 if (!child.DoCheckError136_InChildren (name, loc))
1663 if (children != null) {
1664 foreach (Block child in children) {
1665 if (!child.DoCheckError136_InChildren (name, loc))
1673 public bool CheckError136 (string name, string scope, bool check_parents,
1674 bool check_children, Location loc)
1676 if (!DoCheckError136 (name, scope, loc))
1679 if (check_parents) {
1680 if (!CheckError136_InParents (name, loc))
1684 if (check_children) {
1685 if (!CheckError136_InChildren (name, loc))
1689 for (Block c = Toplevel.ContainerBlock; c != null; c = c.Toplevel.ContainerBlock) {
1690 if (!c.DoCheckError136 (name, "parent or current", loc))
1697 protected bool DoCheckError136 (string name, string scope, Location loc)
1699 LocalInfo vi = GetKnownVariableInfo (name, false);
1701 Report.SymbolRelatedToPreviousError (vi.Location, name);
1702 Error_AlreadyDeclared (loc, name, scope != null ? scope : "child");
1707 Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx);
1709 Report.SymbolRelatedToPreviousError (p.Location, name);
1710 Error_AlreadyDeclared (
1711 loc, name, scope != null ? scope : "method argument");
1718 public LocalInfo AddVariable (Expression type, string name, Location l)
1720 LocalInfo vi = GetLocalInfo (name);
1722 Report.SymbolRelatedToPreviousError (vi.Location, name);
1723 if (known_variables == vi.Block.known_variables)
1724 Report.Error (128, l,
1725 "A local variable named `{0}' is already defined in this scope", name);
1727 Error_AlreadyDeclared (l, name, "parent");
1731 if (!CheckError136 (name, null, true, true, l))
1734 vi = new LocalInfo (type, name, this, l);
1735 Variables.Add (name, vi);
1736 AddKnownVariable (name, vi);
1738 if ((flags & Flags.VariablesInitialized) != 0)
1739 throw new Exception ();
1744 void Error_AlreadyDeclared (Location loc, string var, string reason)
1746 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1747 "in this scope because it would give a different meaning " +
1748 "to `{0}', which is already used in a `{1}' scope " +
1749 "to denote something else", var, reason);
1752 public bool AddConstant (Expression type, string name, Expression value, Location l)
1754 if (AddVariable (type, name, l) == null)
1757 if (constants == null)
1758 constants = new Hashtable ();
1760 constants.Add (name, value);
1762 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1767 static int next_temp_id = 0;
1769 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1771 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1773 if (temporary_variables == null)
1774 temporary_variables = new ArrayList ();
1776 int id = ++next_temp_id;
1777 string name = "$s_" + id.ToString ();
1779 LocalInfo li = new LocalInfo (te, name, this, loc);
1780 li.CompilerGenerated = true;
1781 temporary_variables.Add (li);
1785 public LocalInfo GetLocalInfo (string name)
1787 for (Block b = this; b != null; b = b.Parent) {
1788 if (b.variables != null) {
1789 LocalInfo ret = b.variables [name] as LocalInfo;
1797 public Expression GetVariableType (string name)
1799 LocalInfo vi = GetLocalInfo (name);
1800 return vi == null ? null : vi.Type;
1803 public Expression GetConstantExpression (string name)
1805 for (Block b = this; b != null; b = b.Parent) {
1806 if (b.constants != null) {
1807 Expression ret = b.constants [name] as Expression;
1815 public void AddStatement (Statement s)
1818 flags |= Flags.BlockUsed;
1822 get { return (flags & Flags.BlockUsed) != 0; }
1827 flags |= Flags.BlockUsed;
1830 public bool HasRet {
1831 get { return (flags & Flags.HasRet) != 0; }
1834 public bool IsDestructor {
1835 get { return (flags & Flags.IsDestructor) != 0; }
1838 public void SetDestructor ()
1840 flags |= Flags.IsDestructor;
1843 VariableMap param_map, local_map;
1845 public VariableMap ParameterMap {
1847 if ((flags & Flags.VariablesInitialized) == 0)
1848 throw new Exception ("Variables have not been initialized yet");
1854 public VariableMap LocalMap {
1856 if ((flags & Flags.VariablesInitialized) == 0)
1857 throw new Exception ("Variables have not been initialized yet");
1863 public ScopeInfo ScopeInfo;
1865 public ScopeInfo CreateScopeInfo ()
1867 if (ScopeInfo == null)
1868 ScopeInfo = ScopeInfo.CreateScope (this);
1873 public ArrayList AnonymousChildren {
1874 get { return anonymous_children; }
1877 public void AddAnonymousChild (ToplevelBlock b)
1879 if (anonymous_children == null)
1880 anonymous_children = new ArrayList ();
1882 anonymous_children.Add (b);
1886 /// Emits the variable declarations and labels.
1889 /// tc: is our typecontainer (to resolve type references)
1890 /// ig: is the code generator:
1892 public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, Parameters ip)
1894 Report.Debug (64, "BLOCK RESOLVE META", this, Parent, toplevel);
1896 // If some parent block was unsafe, we remain unsafe even if this block
1897 // isn't explicitly marked as such.
1898 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
1900 // Compute the VariableMap's.
1902 // Unfortunately, we don't know the type when adding variables with
1903 // AddVariable(), so we need to compute this info here.
1907 if (variables != null) {
1908 foreach (LocalInfo li in variables.Values)
1911 locals = new LocalInfo [variables.Count];
1912 variables.Values.CopyTo (locals, 0);
1914 locals = new LocalInfo [0];
1917 local_map = new VariableMap (Parent.LocalMap, locals);
1919 local_map = new VariableMap (locals);
1921 param_map = new VariableMap (ip);
1922 flags |= Flags.VariablesInitialized;
1925 // Process this block variables
1927 if (variables != null) {
1928 foreach (DictionaryEntry de in variables) {
1929 string name = (string) de.Key;
1930 LocalInfo vi = (LocalInfo) de.Value;
1931 Type variable_type = vi.VariableType;
1933 if (variable_type == null)
1936 if (variable_type.IsPointer) {
1938 // Am not really convinced that this test is required (Microsoft does it)
1939 // but the fact is that you would not be able to use the pointer variable
1942 if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
1947 if (constants == null)
1950 Expression cv = (Expression) constants [name];
1954 // Don't let 'const int Foo = Foo;' succeed.
1955 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1956 // which in turn causes the 'must be constant' error to be triggered.
1957 constants.Remove (name);
1959 if (!Const.IsConstantTypeValid (variable_type)) {
1960 Const.Error_InvalidConstantType (variable_type, loc);
1964 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1965 ec.CurrentBlock = this;
1966 Expression e = cv.Resolve (ec);
1970 Constant ce = e as Constant;
1972 Const.Error_ExpressionMustBeConstant (vi.Location, name);
1976 e = ce.ImplicitConversionRequired (variable_type, vi.Location);
1980 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue) {
1981 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
1985 constants.Add (name, e);
1986 vi.IsConstant = true;
1992 // Now, handle the children
1994 if (children != null) {
1995 foreach (Block b in children)
1996 b.ResolveMeta (toplevel, ec, ip);
2002 // Emits the local variable declarations for a block
2004 public virtual void EmitMeta (EmitContext ec)
2006 Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
2007 if (ScopeInfo != null) {
2008 scope_init = ScopeInfo.GetScopeInitializer (ec);
2009 Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
2013 if (variables != null){
2014 foreach (LocalInfo vi in variables.Values)
2015 vi.ResolveVariable (ec);
2018 if (temporary_variables != null) {
2019 foreach (LocalInfo vi in temporary_variables)
2020 vi.ResolveVariable (ec);
2023 if (children != null){
2024 foreach (Block b in children)
2029 void UsageWarning (FlowBranching.UsageVector vector)
2033 if ((variables != null) && (RootContext.WarningLevel >= 3)) {
2034 foreach (DictionaryEntry de in variables){
2035 LocalInfo vi = (LocalInfo) de.Value;
2040 name = (string) de.Key;
2042 // vi.VariableInfo can be null for 'catch' variables
2043 if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
2044 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2046 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2052 bool unreachable_shown;
2055 private void CheckPossibleMistakenEmptyStatement (Statement s)
2059 // Some statements are wrapped by a Block. Since
2060 // others' internal could be changed, here I treat
2061 // them as possibly wrapped by Block equally.
2062 Block b = s as Block;
2063 if (b != null && b.statements.Count == 1)
2064 s = (Statement) b.statements [0];
2067 body = ((Lock) s).Statement;
2069 body = ((For) s).Statement;
2070 else if (s is Foreach)
2071 body = ((Foreach) s).Statement;
2072 else if (s is While)
2073 body = ((While) s).Statement;
2074 else if (s is Using)
2075 body = ((Using) s).Statement;
2076 else if (s is Fixed)
2077 body = ((Fixed) s).Statement;
2081 if (body == null || body is EmptyStatement)
2082 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2085 public override bool Resolve (EmitContext ec)
2087 Block prev_block = ec.CurrentBlock;
2090 int errors = Report.Errors;
2092 ec.CurrentBlock = this;
2093 ec.StartFlowBranching (this);
2095 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2098 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2099 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2100 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2101 // responsible for handling the situation.
2103 int statement_count = statements.Count;
2104 for (int ix = 0; ix < statement_count; ix++){
2105 Statement s = (Statement) statements [ix];
2106 // Check possible empty statement (CS0642)
2107 if (RootContext.WarningLevel >= 3 &&
2108 ix + 1 < statement_count &&
2109 statements [ix + 1] is Block)
2110 CheckPossibleMistakenEmptyStatement (s);
2113 // Warn if we detect unreachable code.
2116 if (s is EmptyStatement)
2120 ((Block) s).unreachable = true;
2122 if (!unreachable_shown && !(s is LabeledStatement)) {
2123 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2124 unreachable_shown = true;
2129 // Note that we're not using ResolveUnreachable() for unreachable
2130 // statements here. ResolveUnreachable() creates a temporary
2131 // flow branching and kills it afterwards. This leads to problems
2132 // if you have two unreachable statements where the first one
2133 // assigns a variable and the second one tries to access it.
2136 if (!s.Resolve (ec)) {
2138 statements [ix] = EmptyStatement.Value;
2142 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2143 statements [ix] = EmptyStatement.Value;
2145 num_statements = ix + 1;
2147 unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
2148 if (unreachable && s is LabeledStatement)
2149 throw new InternalErrorException ("should not happen");
2152 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2153 ec.CurrentBranching, statement_count, num_statements);
2155 while (ec.CurrentBranching is FlowBranchingLabeled)
2156 ec.EndFlowBranching ();
2158 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
2160 ec.CurrentBlock = prev_block;
2162 // If we're a non-static `struct' constructor which doesn't have an
2163 // initializer, then we must initialize all of the struct's fields.
2164 if ((flags & Flags.IsToplevel) != 0 &&
2165 !Toplevel.IsThisAssigned (ec) &&
2166 !vector.Reachability.AlwaysThrows)
2169 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
2170 foreach (LabeledStatement label in labels.Values)
2171 if (!label.HasBeenReferenced)
2172 Report.Warning (164, 2, label.loc,
2173 "This label has not been referenced");
2176 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2178 if (vector.Reachability.IsUnreachable)
2179 flags |= Flags.HasRet;
2181 if (ok && (errors == Report.Errors)) {
2182 if (RootContext.WarningLevel >= 3)
2183 UsageWarning (vector);
2189 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2191 unreachable_shown = true;
2195 Report.Warning (162, 2, loc, "Unreachable code detected");
2197 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2198 bool ok = Resolve (ec);
2199 ec.KillFlowBranching ();
2204 protected override void DoEmit (EmitContext ec)
2206 for (int ix = 0; ix < num_statements; ix++){
2207 Statement s = (Statement) statements [ix];
2209 // Check whether we are the last statement in a
2212 if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block))
2213 ec.IsLastStatement = true;
2215 ec.IsLastStatement = false;
2221 public override void Emit (EmitContext ec)
2223 Block prev_block = ec.CurrentBlock;
2225 ec.CurrentBlock = this;
2227 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2228 bool is_lexical_block = !Implicit && (Parent != null);
2230 if (emit_debug_info) {
2231 if (is_lexical_block)
2234 ec.Mark (StartLocation, true);
2235 if (scope_init != null)
2236 scope_init.EmitStatement (ec);
2238 ec.Mark (EndLocation, true);
2240 if (emit_debug_info) {
2241 if (is_lexical_block)
2244 if (variables != null) {
2245 foreach (DictionaryEntry de in variables) {
2246 string name = (string) de.Key;
2247 LocalInfo vi = (LocalInfo) de.Value;
2249 vi.EmitSymbolInfo (ec, name);
2254 ec.CurrentBlock = prev_block;
2258 // Returns true if we ar ea child of `b'.
2260 public bool IsChildOf (Block b)
2262 Block current = this;
2265 if (current.Parent == b)
2267 current = current.Parent;
2268 } while (current != null);
2272 public override string ToString ()
2274 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2279 // A toplevel block contains extra information, the split is done
2280 // only to separate information that would otherwise bloat the more
2281 // lightweight Block.
2283 // In particular, this was introduced when the support for Anonymous
2284 // Methods was implemented.
2286 public class ToplevelBlock : Block {
2288 // Pointer to the host of this anonymous method, or null
2289 // if we are the topmost block
2292 ToplevelBlock child;
2293 GenericMethod generic;
2294 FlowBranchingToplevel top_level_branching;
2295 AnonymousContainer anonymous_container;
2296 RootScopeInfo root_scope;
2298 public bool HasVarargs {
2299 get { return (flags & Flags.HasVarargs) != 0; }
2300 set { flags |= Flags.HasVarargs; }
2303 public bool IsIterator {
2304 get { return (flags & Flags.IsIterator) != 0; }
2308 // The parameters for the block.
2310 Parameters parameters;
2311 public Parameters Parameters {
2312 get { return parameters; }
2315 public bool CompleteContexts (EmitContext ec)
2317 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this,
2318 container, root_scope);
2320 if (root_scope != null)
2321 root_scope.LinkScopes ();
2323 if ((container == null) && (root_scope != null)) {
2324 Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this,
2327 if (root_scope.DefineType () == null)
2329 if (!root_scope.ResolveType ())
2331 if (!root_scope.ResolveMembers ())
2333 if (!root_scope.DefineMembers ())
2340 public GenericMethod GenericMethod {
2341 get { return generic; }
2344 public ToplevelBlock Container {
2345 get { return container != null ? container.Toplevel : null; }
2348 public Block ContainerBlock {
2349 get { return container; }
2352 public AnonymousContainer AnonymousContainer {
2353 get { return anonymous_container; }
2354 set { anonymous_container = value; }
2358 // Parent is only used by anonymous blocks to link back to their
2361 public ToplevelBlock (Block container, Parameters parameters, Location start) :
2362 this (container, (Flags) 0, parameters, start)
2366 public ToplevelBlock (Block container, Parameters parameters, GenericMethod generic,
2368 this (container, parameters, start)
2370 this.generic = generic;
2373 public ToplevelBlock (Parameters parameters, Location start) :
2374 this (null, (Flags) 0, parameters, start)
2378 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2379 this (null, flags, parameters, start)
2383 public ToplevelBlock (Block container, Flags flags, Parameters parameters, Location start) :
2384 base (null, flags | Flags.IsToplevel, start, Location.Null)
2386 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2387 this.container = container;
2390 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2394 public bool CheckError158 (string name, Location loc)
2396 if (AnonymousChildren != null) {
2397 foreach (ToplevelBlock child in AnonymousChildren) {
2398 if (!child.CheckError158 (name, loc))
2403 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2404 if (!c.DoCheckError158 (name, loc))
2411 bool DoCheckError158 (string name, Location loc)
2413 LabeledStatement s = LookupLabel (name);
2415 Error_158 (name, loc);
2422 public RootScopeInfo CreateRootScope (TypeContainer host)
2424 if (root_scope != null)
2427 if (Container == null)
2428 root_scope = new RootScopeInfo (
2429 this, host, generic, StartLocation);
2431 ScopeInfo = root_scope;
2435 public void CreateIteratorHost (RootScopeInfo root)
2437 Report.Debug (64, "CREATE ITERATOR HOST", this, root,
2438 container, root_scope);
2440 if ((container != null) || (root_scope != null))
2441 throw new InternalErrorException ();
2443 ScopeInfo = root_scope = root;
2446 public RootScopeInfo RootScope {
2448 if (root_scope != null)
2450 else if (Container != null)
2451 return Container.RootScope;
2457 public FlowBranchingToplevel TopLevelBranching {
2458 get { return top_level_branching; }
2462 // This is used if anonymous methods are used inside an iterator
2463 // (see 2test-22.cs for an example).
2465 // The AnonymousMethod is created while parsing - at a time when we don't
2466 // know yet that we're inside an iterator, so it's `Container' is initially
2467 // null. Later on, when resolving the iterator, we need to move the
2468 // anonymous method into that iterator.
2470 public void ReParent (ToplevelBlock new_parent)
2472 container = new_parent;
2473 Parent = new_parent;
2474 new_parent.child = this;
2478 // Returns a `ParameterReference' for the given name, or null if there
2479 // is no such parameter
2481 public ParameterReference GetParameterReference (string name, Location loc)
2486 for (ToplevelBlock t = this; t != null; t = t.Container) {
2487 Parameters pars = t.Parameters;
2488 par = pars.GetParameterByName (name, out idx);
2490 return new ParameterReference (par, this, idx, loc);
2496 // Whether the parameter named `name' is local to this block,
2497 // or false, if the parameter belongs to an encompassing block.
2499 public bool IsLocalParameter (string name)
2501 return Parameters.GetParameterByName (name) != null;
2505 // Whether the `name' is a parameter reference
2507 public bool IsParameterReference (string name)
2509 for (ToplevelBlock t = this; t != null; t = t.Container) {
2510 if (t.IsLocalParameter (name))
2516 LocalInfo this_variable = null;
2519 // Returns the "this" instance variable of this block.
2520 // See AddThisVariable() for more information.
2522 public LocalInfo ThisVariable {
2523 get { return this_variable; }
2528 // This is used by non-static `struct' constructors which do not have an
2529 // initializer - in this case, the constructor must initialize all of the
2530 // struct's fields. To do this, we add a "this" variable and use the flow
2531 // analysis code to ensure that it's been fully initialized before control
2532 // leaves the constructor.
2534 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2536 if (this_variable == null) {
2537 this_variable = new LocalInfo (ds, this, l);
2538 this_variable.Used = true;
2539 this_variable.IsThis = true;
2541 Variables.Add ("this", this_variable);
2544 return this_variable;
2547 public bool IsThisAssigned (EmitContext ec)
2549 return this_variable == null || this_variable.IsThisAssigned (ec, loc);
2552 public bool ResolveMeta (EmitContext ec, Parameters ip)
2554 int errors = Report.Errors;
2556 if (top_level_branching != null)
2562 if (!IsIterator && (container != null) && (parameters != null)) {
2563 foreach (Parameter p in parameters.FixedParameters) {
2564 if (!CheckError136_InParents (p.Name, loc))
2569 ResolveMeta (this, ec, ip);
2572 child.ResolveMeta (this, ec, ip);
2574 top_level_branching = ec.StartFlowBranching (this);
2576 return Report.Errors == errors;
2579 public override void EmitMeta (EmitContext ec)
2582 parameters.ResolveVariable (this);
2585 public void MakeIterator (Iterator iterator)
2587 flags |= Flags.IsIterator;
2589 Block block = new Block (this);
2590 foreach (Statement stmt in statements)
2591 block.AddStatement (stmt);
2592 statements = new ArrayList ();
2593 statements.Add (new MoveNextStatement (iterator, block));
2596 protected class MoveNextStatement : Statement {
2600 public MoveNextStatement (Iterator iterator, Block block)
2602 this.iterator = iterator;
2604 this.loc = iterator.Location;
2607 public override bool Resolve (EmitContext ec)
2609 return block.Resolve (ec);
2612 protected override void DoEmit (EmitContext ec)
2614 iterator.EmitMoveNext (ec, block);
2618 public override string ToString ()
2620 return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
2621 root_scope, anonymous_container != null ?
2622 anonymous_container.Scope : null);
2626 public class SwitchLabel {
2633 Label il_label_code;
2634 bool il_label_code_set;
2636 public static readonly object NullStringCase = new object ();
2639 // if expr == null, then it is the default case.
2641 public SwitchLabel (Expression expr, Location l)
2647 public Expression Label {
2653 public object Converted {
2659 public Label GetILLabel (EmitContext ec)
2662 il_label = ec.ig.DefineLabel ();
2663 il_label_set = true;
2668 public Label GetILLabelCode (EmitContext ec)
2670 if (!il_label_code_set){
2671 il_label_code = ec.ig.DefineLabel ();
2672 il_label_code_set = true;
2674 return il_label_code;
2678 // Resolves the expression, reduces it to a literal if possible
2679 // and then converts it to the requested type.
2681 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2683 Expression e = label.Resolve (ec);
2688 Constant c = e as Constant;
2690 Report.Error (150, loc, "A constant value is expected");
2694 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2695 converted = NullStringCase;
2699 if (allow_nullable && c.GetValue () == null) {
2700 converted = NullStringCase;
2704 c = c.ImplicitConversionRequired (required_type, loc);
2708 converted = c.GetValue ();
2712 public void Erorr_AlreadyOccurs ()
2715 if (converted == null)
2717 else if (converted == NullStringCase)
2720 label = converted.ToString ();
2722 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2726 public class SwitchSection {
2727 // An array of SwitchLabels.
2728 public readonly ArrayList Labels;
2729 public readonly Block Block;
2731 public SwitchSection (ArrayList labels, Block block)
2738 public class Switch : Statement {
2739 public readonly ArrayList Sections;
2740 public Expression Expr;
2743 /// Maps constants whose type type SwitchType to their SwitchLabels.
2745 public IDictionary Elements;
2748 /// The governing switch type
2750 public Type SwitchType;
2755 Label default_target;
2757 Expression new_expr;
2759 SwitchSection constant_section;
2760 SwitchSection default_section;
2764 // Nullable Types support for GMCS.
2766 Nullable.Unwrap unwrap;
2768 protected bool HaveUnwrap {
2769 get { return unwrap != null; }
2772 protected bool HaveUnwrap {
2773 get { return false; }
2778 // The types allowed to be implicitly cast from
2779 // on the governing type
2781 static Type [] allowed_types;
2783 public Switch (Expression e, ArrayList sects, Location l)
2790 public bool GotDefault {
2792 return default_section != null;
2796 public Label DefaultTarget {
2798 return default_target;
2803 // Determines the governing type for a switch. The returned
2804 // expression might be the expression from the switch, or an
2805 // expression that includes any potential conversions to the
2806 // integral types or to string.
2808 Expression SwitchGoverningType (EmitContext ec, Expression expr)
2812 if (t == TypeManager.byte_type ||
2813 t == TypeManager.sbyte_type ||
2814 t == TypeManager.ushort_type ||
2815 t == TypeManager.short_type ||
2816 t == TypeManager.uint32_type ||
2817 t == TypeManager.int32_type ||
2818 t == TypeManager.uint64_type ||
2819 t == TypeManager.int64_type ||
2820 t == TypeManager.char_type ||
2821 t == TypeManager.string_type ||
2822 t == TypeManager.bool_type ||
2823 t.IsSubclassOf (TypeManager.enum_type))
2826 if (allowed_types == null){
2827 allowed_types = new Type [] {
2828 TypeManager.sbyte_type,
2829 TypeManager.byte_type,
2830 TypeManager.short_type,
2831 TypeManager.ushort_type,
2832 TypeManager.int32_type,
2833 TypeManager.uint32_type,
2834 TypeManager.int64_type,
2835 TypeManager.uint64_type,
2836 TypeManager.char_type,
2837 TypeManager.string_type,
2838 TypeManager.bool_type
2843 // Try to find a *user* defined implicit conversion.
2845 // If there is no implicit conversion, or if there are multiple
2846 // conversions, we have to report an error
2848 Expression converted = null;
2849 foreach (Type tt in allowed_types){
2852 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
2857 // Ignore over-worked ImplicitUserConversions that do
2858 // an implicit conversion in addition to the user conversion.
2860 if (!(e is UserCast))
2863 if (converted != null){
2864 Report.ExtraInformation (
2866 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
2867 TypeManager.CSharpName (expr.Type)));
2877 // Performs the basic sanity checks on the switch statement
2878 // (looks for duplicate keys and non-constant expressions).
2880 // It also returns a hashtable with the keys that we will later
2881 // use to compute the switch tables
2883 bool CheckSwitch (EmitContext ec)
2886 Elements = Sections.Count > 10 ?
2887 (IDictionary)new Hashtable () :
2888 (IDictionary)new ListDictionary ();
2890 foreach (SwitchSection ss in Sections){
2891 foreach (SwitchLabel sl in ss.Labels){
2892 if (sl.Label == null){
2893 if (default_section != null){
2894 sl.Erorr_AlreadyOccurs ();
2897 default_section = ss;
2901 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
2906 object key = sl.Converted;
2908 Elements.Add (key, sl);
2909 } catch (ArgumentException) {
2910 sl.Erorr_AlreadyOccurs ();
2918 void EmitObjectInteger (ILGenerator ig, object k)
2921 IntConstant.EmitInt (ig, (int) k);
2922 else if (k is Constant) {
2923 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2926 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2929 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2931 IntConstant.EmitInt (ig, (int) (long) k);
2932 ig.Emit (OpCodes.Conv_I8);
2935 LongConstant.EmitLong (ig, (long) k);
2937 else if (k is ulong)
2939 ulong ul = (ulong) k;
2942 IntConstant.EmitInt (ig, unchecked ((int) ul));
2943 ig.Emit (OpCodes.Conv_U8);
2947 LongConstant.EmitLong (ig, unchecked ((long) ul));
2951 IntConstant.EmitInt (ig, (int) ((char) k));
2952 else if (k is sbyte)
2953 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2955 IntConstant.EmitInt (ig, (int) ((byte) k));
2956 else if (k is short)
2957 IntConstant.EmitInt (ig, (int) ((short) k));
2958 else if (k is ushort)
2959 IntConstant.EmitInt (ig, (int) ((ushort) k));
2961 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2963 throw new Exception ("Unhandled case");
2966 // structure used to hold blocks of keys while calculating table switch
2967 class KeyBlock : IComparable
2969 public KeyBlock (long _nFirst)
2971 nFirst = nLast = _nFirst;
2975 public ArrayList rgKeys = null;
2976 // how many items are in the bucket
2977 public int Size = 1;
2980 get { return (int) (nLast - nFirst + 1); }
2982 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2984 return kbLast.nLast - kbFirst.nFirst + 1;
2986 public int CompareTo (object obj)
2988 KeyBlock kb = (KeyBlock) obj;
2989 int nLength = Length;
2990 int nLengthOther = kb.Length;
2991 if (nLengthOther == nLength)
2992 return (int) (kb.nFirst - nFirst);
2993 return nLength - nLengthOther;
2998 /// This method emits code for a lookup-based switch statement (non-string)
2999 /// Basically it groups the cases into blocks that are at least half full,
3000 /// and then spits out individual lookup opcodes for each block.
3001 /// It emits the longest blocks first, and short blocks are just
3002 /// handled with direct compares.
3004 /// <param name="ec"></param>
3005 /// <param name="val"></param>
3006 /// <returns></returns>
3007 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3009 int cElements = Elements.Count;
3010 object [] rgKeys = new object [cElements];
3011 Elements.Keys.CopyTo (rgKeys, 0);
3012 Array.Sort (rgKeys);
3014 // initialize the block list with one element per key
3015 ArrayList rgKeyBlocks = new ArrayList ();
3016 foreach (object key in rgKeys)
3017 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3020 // iteratively merge the blocks while they are at least half full
3021 // there's probably a really cool way to do this with a tree...
3022 while (rgKeyBlocks.Count > 1)
3024 ArrayList rgKeyBlocksNew = new ArrayList ();
3025 kbCurr = (KeyBlock) rgKeyBlocks [0];
3026 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3028 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3029 if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
3032 kbCurr.nLast = kb.nLast;
3033 kbCurr.Size += kb.Size;
3037 // start a new block
3038 rgKeyBlocksNew.Add (kbCurr);
3042 rgKeyBlocksNew.Add (kbCurr);
3043 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3045 rgKeyBlocks = rgKeyBlocksNew;
3048 // initialize the key lists
3049 foreach (KeyBlock kb in rgKeyBlocks)
3050 kb.rgKeys = new ArrayList ();
3052 // fill the key lists
3054 if (rgKeyBlocks.Count > 0) {
3055 kbCurr = (KeyBlock) rgKeyBlocks [0];
3056 foreach (object key in rgKeys)
3058 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
3059 System.Convert.ToInt64 (key) > kbCurr.nLast;
3061 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3062 kbCurr.rgKeys.Add (key);
3066 // sort the blocks so we can tackle the largest ones first
3067 rgKeyBlocks.Sort ();
3069 // okay now we can start...
3070 ILGenerator ig = ec.ig;
3071 Label lblEnd = ig.DefineLabel (); // at the end ;-)
3072 Label lblDefault = ig.DefineLabel ();
3074 Type typeKeys = null;
3075 if (rgKeys.Length > 0)
3076 typeKeys = rgKeys [0].GetType (); // used for conversions
3080 if (TypeManager.IsEnumType (SwitchType))
3081 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3083 compare_type = SwitchType;
3085 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3087 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3088 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3091 foreach (object key in kb.rgKeys)
3093 ig.Emit (OpCodes.Ldloc, val);
3094 EmitObjectInteger (ig, key);
3095 SwitchLabel sl = (SwitchLabel) Elements [key];
3096 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3101 // TODO: if all the keys in the block are the same and there are
3102 // no gaps/defaults then just use a range-check.
3103 if (compare_type == TypeManager.int64_type ||
3104 compare_type == TypeManager.uint64_type)
3106 // TODO: optimize constant/I4 cases
3108 // check block range (could be > 2^31)
3109 ig.Emit (OpCodes.Ldloc, val);
3110 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3111 ig.Emit (OpCodes.Blt, lblDefault);
3112 ig.Emit (OpCodes.Ldloc, val);
3113 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
3114 ig.Emit (OpCodes.Bgt, lblDefault);
3117 ig.Emit (OpCodes.Ldloc, val);
3120 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
3121 ig.Emit (OpCodes.Sub);
3123 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3128 ig.Emit (OpCodes.Ldloc, val);
3129 int nFirst = (int) kb.nFirst;
3132 IntConstant.EmitInt (ig, nFirst);
3133 ig.Emit (OpCodes.Sub);
3135 else if (nFirst < 0)
3137 IntConstant.EmitInt (ig, -nFirst);
3138 ig.Emit (OpCodes.Add);
3142 // first, build the list of labels for the switch
3144 int cJumps = kb.Length;
3145 Label [] rgLabels = new Label [cJumps];
3146 for (int iJump = 0; iJump < cJumps; iJump++)
3148 object key = kb.rgKeys [iKey];
3149 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
3151 SwitchLabel sl = (SwitchLabel) Elements [key];
3152 rgLabels [iJump] = sl.GetILLabel (ec);
3156 rgLabels [iJump] = lblDefault;
3158 // emit the switch opcode
3159 ig.Emit (OpCodes.Switch, rgLabels);
3162 // mark the default for this block
3164 ig.MarkLabel (lblDefault);
3167 // TODO: find the default case and emit it here,
3168 // to prevent having to do the following jump.
3169 // make sure to mark other labels in the default section
3171 // the last default just goes to the end
3172 ig.Emit (OpCodes.Br, lblDefault);
3174 // now emit the code for the sections
3175 bool fFoundDefault = false;
3176 bool fFoundNull = false;
3177 foreach (SwitchSection ss in Sections)
3179 foreach (SwitchLabel sl in ss.Labels)
3180 if (sl.Converted == SwitchLabel.NullStringCase)
3184 foreach (SwitchSection ss in Sections)
3186 foreach (SwitchLabel sl in ss.Labels)
3188 ig.MarkLabel (sl.GetILLabel (ec));
3189 ig.MarkLabel (sl.GetILLabelCode (ec));
3190 if (sl.Converted == SwitchLabel.NullStringCase)
3191 ig.MarkLabel (null_target);
3192 else if (sl.Label == null) {
3193 ig.MarkLabel (lblDefault);
3194 fFoundDefault = true;
3196 ig.MarkLabel (null_target);
3202 if (!fFoundDefault) {
3203 ig.MarkLabel (lblDefault);
3205 ig.MarkLabel (lblEnd);
3208 // This simple emit switch works, but does not take advantage of the
3210 // TODO: remove non-string logic from here
3211 // TODO: binary search strings?
3213 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3215 ILGenerator ig = ec.ig;
3216 Label end_of_switch = ig.DefineLabel ();
3217 Label next_test = ig.DefineLabel ();
3218 bool first_test = true;
3219 bool pending_goto_end = false;
3220 bool null_marked = false;
3223 ig.Emit (OpCodes.Ldloc, val);
3225 if (Elements.Contains (SwitchLabel.NullStringCase)){
3226 ig.Emit (OpCodes.Brfalse, null_target);
3228 ig.Emit (OpCodes.Brfalse, default_target);
3230 ig.Emit (OpCodes.Ldloc, val);
3231 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
3232 ig.Emit (OpCodes.Stloc, val);
3234 int section_count = Sections.Count;
3235 for (int section = 0; section < section_count; section++){
3236 SwitchSection ss = (SwitchSection) Sections [section];
3238 if (ss == default_section)
3241 Label sec_begin = ig.DefineLabel ();
3243 ig.Emit (OpCodes.Nop);
3245 if (pending_goto_end)
3246 ig.Emit (OpCodes.Br, end_of_switch);
3248 int label_count = ss.Labels.Count;
3250 for (int label = 0; label < label_count; label++){
3251 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3252 ig.MarkLabel (sl.GetILLabel (ec));
3255 ig.MarkLabel (next_test);
3256 next_test = ig.DefineLabel ();
3259 // If we are the default target
3261 if (sl.Label != null){
3262 object lit = sl.Converted;
3264 if (lit == SwitchLabel.NullStringCase){
3266 if (label + 1 == label_count)
3267 ig.Emit (OpCodes.Br, next_test);
3271 ig.Emit (OpCodes.Ldloc, val);
3272 ig.Emit (OpCodes.Ldstr, (string)lit);
3273 if (label_count == 1)
3274 ig.Emit (OpCodes.Bne_Un, next_test);
3276 if (label+1 == label_count)
3277 ig.Emit (OpCodes.Bne_Un, next_test);
3279 ig.Emit (OpCodes.Beq, sec_begin);
3284 ig.MarkLabel (null_target);
3287 ig.MarkLabel (sec_begin);
3288 foreach (SwitchLabel sl in ss.Labels)
3289 ig.MarkLabel (sl.GetILLabelCode (ec));
3292 pending_goto_end = !ss.Block.HasRet;
3295 ig.MarkLabel (next_test);
3296 ig.MarkLabel (default_target);
3298 ig.MarkLabel (null_target);
3299 if (default_section != null)
3300 default_section.Block.Emit (ec);
3301 ig.MarkLabel (end_of_switch);
3304 SwitchSection FindSection (SwitchLabel label)
3306 foreach (SwitchSection ss in Sections){
3307 foreach (SwitchLabel sl in ss.Labels){
3316 public override bool Resolve (EmitContext ec)
3318 Expr = Expr.Resolve (ec);
3322 new_expr = SwitchGoverningType (ec, Expr);
3325 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3326 unwrap = Nullable.Unwrap.Create (Expr, ec);
3330 new_expr = SwitchGoverningType (ec, unwrap);
3334 if (new_expr == null){
3335 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3340 SwitchType = new_expr.Type;
3342 if (!CheckSwitch (ec))
3346 Elements.Remove (SwitchLabel.NullStringCase);
3348 Switch old_switch = ec.Switch;
3350 ec.Switch.SwitchType = SwitchType;
3352 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3353 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3355 is_constant = new_expr is Constant;
3357 object key = ((Constant) new_expr).GetValue ();
3358 SwitchLabel label = (SwitchLabel) Elements [key];
3360 constant_section = FindSection (label);
3361 if (constant_section == null)
3362 constant_section = default_section;
3366 foreach (SwitchSection ss in Sections){
3368 ec.CurrentBranching.CreateSibling (
3369 null, FlowBranching.SiblingType.SwitchSection);
3373 if (is_constant && (ss != constant_section)) {
3374 // If we're a constant switch, we're only emitting
3375 // one single section - mark all the others as
3377 ec.CurrentBranching.CurrentUsageVector.Goto ();
3378 if (!ss.Block.ResolveUnreachable (ec, true))
3381 if (!ss.Block.Resolve (ec))
3386 if (default_section == null)
3387 ec.CurrentBranching.CreateSibling (
3388 null, FlowBranching.SiblingType.SwitchSection);
3390 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3391 ec.Switch = old_switch;
3393 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching,
3399 protected override void DoEmit (EmitContext ec)
3401 ILGenerator ig = ec.ig;
3403 default_target = ig.DefineLabel ();
3404 null_target = ig.DefineLabel ();
3406 // Store variable for comparission purposes
3409 value = ig.DeclareLocal (SwitchType);
3411 unwrap.EmitCheck (ec);
3412 ig.Emit (OpCodes.Brfalse, null_target);
3414 ig.Emit (OpCodes.Stloc, value);
3416 } else if (!is_constant) {
3417 value = ig.DeclareLocal (SwitchType);
3419 ig.Emit (OpCodes.Stloc, value);
3424 // Setup the codegen context
3426 Label old_end = ec.LoopEnd;
3427 Switch old_switch = ec.Switch;
3429 ec.LoopEnd = ig.DefineLabel ();
3434 if (constant_section != null)
3435 constant_section.Block.Emit (ec);
3436 } else if (SwitchType == TypeManager.string_type)
3437 SimpleSwitchEmit (ec, value);
3439 TableSwitchEmit (ec, value);
3441 // Restore context state.
3442 ig.MarkLabel (ec.LoopEnd);
3445 // Restore the previous context
3447 ec.LoopEnd = old_end;
3448 ec.Switch = old_switch;
3452 public abstract class ExceptionStatement : Statement
3454 public abstract void EmitFinally (EmitContext ec);
3456 protected bool emit_finally = true;
3457 ArrayList parent_vectors;
3459 protected void DoEmitFinally (EmitContext ec)
3462 ec.ig.BeginFinallyBlock ();
3463 else if (ec.InIterator)
3464 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3468 protected void ResolveFinally (FlowBranchingException branching)
3470 emit_finally = branching.EmitFinally;
3472 branching.Parent.StealFinallyClauses (ref parent_vectors);
3476 public class Lock : ExceptionStatement {
3478 public Statement Statement;
3479 TemporaryVariable temp;
3481 public Lock (Expression expr, Statement stmt, Location l)
3488 public override bool Resolve (EmitContext ec)
3490 expr = expr.Resolve (ec);
3494 if (expr.Type.IsValueType){
3495 Report.Error (185, loc,
3496 "`{0}' is not a reference type as required by the lock statement",
3497 TypeManager.CSharpName (expr.Type));
3501 FlowBranchingException branching = ec.StartFlowBranching (this);
3502 bool ok = Statement.Resolve (ec);
3504 ec.KillFlowBranching ();
3508 ResolveFinally (branching);
3510 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3511 if (!reachability.AlwaysReturns) {
3512 // Unfortunately, System.Reflection.Emit automatically emits
3513 // a leave to the end of the finally block.
3514 // This is a problem if `returns' is true since we may jump
3515 // to a point after the end of the method.
3516 // As a workaround, emit an explicit ret here.
3517 ec.NeedReturnLabel ();
3520 // Avoid creating libraries that reference the internal
3523 if (t == TypeManager.null_type)
3524 t = TypeManager.object_type;
3526 temp = new TemporaryVariable (t, loc);
3532 protected override void DoEmit (EmitContext ec)
3534 ILGenerator ig = ec.ig;
3536 temp.Store (ec, expr);
3538 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3542 ig.BeginExceptionBlock ();
3543 Statement.Emit (ec);
3548 ig.EndExceptionBlock ();
3551 public override void EmitFinally (EmitContext ec)
3554 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3558 public class Unchecked : Statement {
3559 public readonly Block Block;
3561 public Unchecked (Block b)
3567 public override bool Resolve (EmitContext ec)
3569 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3570 return Block.Resolve (ec);
3573 protected override void DoEmit (EmitContext ec)
3575 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
3580 public class Checked : Statement {
3581 public readonly Block Block;
3583 public Checked (Block b)
3586 b.Unchecked = false;
3589 public override bool Resolve (EmitContext ec)
3591 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3592 return Block.Resolve (ec);
3595 protected override void DoEmit (EmitContext ec)
3597 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
3602 public class Unsafe : Statement {
3603 public readonly Block Block;
3605 public Unsafe (Block b)
3608 Block.Unsafe = true;
3611 public override bool Resolve (EmitContext ec)
3613 using (ec.With (EmitContext.Flags.InUnsafe, true))
3614 return Block.Resolve (ec);
3617 protected override void DoEmit (EmitContext ec)
3619 using (ec.With (EmitContext.Flags.InUnsafe, true))
3627 public class Fixed : Statement {
3629 ArrayList declarators;
3630 Statement statement;
3635 abstract class Emitter
3637 protected LocalInfo vi;
3638 protected Expression converted;
3640 protected Emitter (Expression expr, LocalInfo li)
3646 public abstract void Emit (EmitContext ec);
3647 public abstract void EmitExit (EmitContext ec);
3650 class ExpressionEmitter : Emitter {
3651 public ExpressionEmitter (Expression converted, LocalInfo li) :
3652 base (converted, li)
3656 public override void Emit (EmitContext ec) {
3658 // Store pointer in pinned location
3660 converted.Emit (ec);
3661 vi.Variable.EmitAssign (ec);
3664 public override void EmitExit (EmitContext ec)
3666 ec.ig.Emit (OpCodes.Ldc_I4_0);
3667 ec.ig.Emit (OpCodes.Conv_U);
3668 vi.Variable.EmitAssign (ec);
3672 class StringEmitter : Emitter {
3673 LocalBuilder pinned_string;
3676 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3682 public override void Emit (EmitContext ec)
3684 ILGenerator ig = ec.ig;
3685 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3687 converted.Emit (ec);
3688 ig.Emit (OpCodes.Stloc, pinned_string);
3690 Expression sptr = new StringPtr (pinned_string, loc);
3691 converted = Convert.ImplicitConversionRequired (
3692 ec, sptr, vi.VariableType, loc);
3694 if (converted == null)
3697 converted.Emit (ec);
3698 vi.Variable.EmitAssign (ec);
3701 public override void EmitExit (EmitContext ec)
3703 ec.ig.Emit (OpCodes.Ldnull);
3704 ec.ig.Emit (OpCodes.Stloc, pinned_string);
3708 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3711 declarators = decls;
3716 public Statement Statement {
3717 get { return statement; }
3720 public override bool Resolve (EmitContext ec)
3723 Expression.UnsafeError (loc);
3727 TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
3731 expr_type = texpr.Type;
3733 data = new Emitter [declarators.Count];
3735 if (!expr_type.IsPointer){
3736 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
3741 foreach (Pair p in declarators){
3742 LocalInfo vi = (LocalInfo) p.First;
3743 Expression e = (Expression) p.Second;
3745 vi.VariableInfo.SetAssigned (ec);
3746 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
3749 // The rules for the possible declarators are pretty wise,
3750 // but the production on the grammar is more concise.
3752 // So we have to enforce these rules here.
3754 // We do not resolve before doing the case 1 test,
3755 // because the grammar is explicit in that the token &
3756 // is present, so we need to test for this particular case.
3760 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
3765 // Case 1: & object.
3767 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3768 Expression child = ((Unary) e).Expr;
3770 if (child is ParameterReference || child is LocalVariableReference){
3773 "No need to use fixed statement for parameters or " +
3774 "local variable declarations (address is already " +
3779 ec.InFixedInitializer = true;
3781 ec.InFixedInitializer = false;
3785 child = ((Unary) e).Expr;
3787 if (!TypeManager.VerifyUnManaged (child.Type, loc))
3790 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
3791 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
3795 data [i] = new ExpressionEmitter (e, vi);
3801 ec.InFixedInitializer = true;
3803 ec.InFixedInitializer = false;
3810 if (e.Type.IsArray){
3811 Type array_type = TypeManager.GetElementType (e.Type);
3814 // Provided that array_type is unmanaged,
3816 if (!TypeManager.VerifyUnManaged (array_type, loc))
3820 // and T* is implicitly convertible to the
3821 // pointer type given in the fixed statement.
3823 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
3825 Expression converted = Convert.ImplicitConversionRequired (
3826 ec, array_ptr, vi.VariableType, loc);
3827 if (converted == null)
3830 data [i] = new ExpressionEmitter (converted, vi);
3839 if (e.Type == TypeManager.string_type){
3840 data [i] = new StringEmitter (e, vi, loc);
3845 // Case 4: fixed buffer
3846 FieldExpr fe = e as FieldExpr;
3848 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
3850 Expression fixed_buffer_ptr = new FixedBufferPtr (fe, ff.ElementType, loc);
3852 Expression converted = Convert.ImplicitConversionRequired (
3853 ec, fixed_buffer_ptr, vi.VariableType, loc);
3854 if (converted == null)
3857 data [i] = new ExpressionEmitter (converted, vi);
3865 // For other cases, flag a `this is already fixed expression'
3867 if (e is LocalVariableReference || e is ParameterReference ||
3868 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
3870 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
3874 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
3878 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
3880 if (!statement.Resolve (ec)) {
3881 ec.KillFlowBranching ();
3885 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3886 has_ret = reachability.IsUnreachable;
3891 protected override void DoEmit (EmitContext ec)
3893 for (int i = 0; i < data.Length; i++) {
3897 statement.Emit (ec);
3903 // Clear the pinned variable
3905 for (int i = 0; i < data.Length; i++) {
3906 data [i].EmitExit (ec);
3911 public class Catch : Statement {
3912 public readonly string Name;
3913 public readonly Block Block;
3914 public readonly Block VarBlock;
3916 Expression type_expr;
3919 public Catch (Expression type, string name, Block block, Block var_block, Location l)
3924 VarBlock = var_block;
3928 public Type CatchType {
3934 public bool IsGeneral {
3936 return type_expr == null;
3940 protected override void DoEmit(EmitContext ec)
3942 ILGenerator ig = ec.ig;
3944 if (CatchType != null)
3945 ig.BeginCatchBlock (CatchType);
3947 ig.BeginCatchBlock (TypeManager.object_type);
3949 if (VarBlock != null)
3953 LocalInfo vi = Block.GetLocalInfo (Name);
3955 throw new Exception ("Variable does not exist in this block");
3957 if (vi.Variable.NeedsTemporary) {
3958 LocalBuilder e = ig.DeclareLocal (vi.VariableType);
3959 ig.Emit (OpCodes.Stloc, e);
3961 vi.Variable.EmitInstance (ec);
3962 ig.Emit (OpCodes.Ldloc, e);
3963 vi.Variable.EmitAssign (ec);
3965 vi.Variable.EmitAssign (ec);
3967 ig.Emit (OpCodes.Pop);
3972 public override bool Resolve (EmitContext ec)
3974 using (ec.With (EmitContext.Flags.InCatch, true)) {
3975 if (type_expr != null) {
3976 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
3982 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
3983 Error (155, "The type caught or thrown must be derived from System.Exception");
3989 if (!Block.Resolve (ec))
3992 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
3993 // emit the "unused variable" warnings.
3994 if (VarBlock != null)
3995 return VarBlock.Resolve (ec);
4002 public class Try : ExceptionStatement {
4003 public readonly Block Fini, Block;
4004 public readonly ArrayList Specific;
4005 public readonly Catch General;
4007 bool need_exc_block;
4010 // specific, general and fini might all be null.
4012 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4014 if (specific == null && general == null){
4015 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4019 this.Specific = specific;
4020 this.General = general;
4025 public override bool Resolve (EmitContext ec)
4029 FlowBranchingException branching = ec.StartFlowBranching (this);
4031 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4033 if (!Block.Resolve (ec))
4036 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4038 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4040 Type[] prevCatches = new Type [Specific.Count];
4042 foreach (Catch c in Specific){
4043 ec.CurrentBranching.CreateSibling (
4044 c.Block, FlowBranching.SiblingType.Catch);
4046 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4048 if (c.Name != null) {
4049 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4051 throw new Exception ();
4053 vi.VariableInfo = null;
4056 if (!c.Resolve (ec))
4059 Type resolvedType = c.CatchType;
4060 for (int ii = 0; ii < last_index; ++ii) {
4061 if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
4062 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches [ii].FullName);
4067 prevCatches [last_index++] = resolvedType;
4068 need_exc_block = true;
4071 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4073 if (General != null){
4074 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4075 foreach (Catch c in Specific){
4076 if (c.CatchType == TypeManager.exception_type) {
4077 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'");
4082 ec.CurrentBranching.CreateSibling (
4083 General.Block, FlowBranching.SiblingType.Catch);
4085 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4087 if (!General.Resolve (ec))
4090 need_exc_block = true;
4093 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4097 ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
4099 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4100 using (ec.With (EmitContext.Flags.InFinally, true)) {
4101 if (!Fini.Resolve (ec))
4106 need_exc_block = true;
4109 if (ec.InIterator) {
4110 ResolveFinally (branching);
4111 need_exc_block |= emit_finally;
4113 emit_finally = Fini != null;
4115 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
4117 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4119 Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
4121 if (!reachability.AlwaysReturns) {
4122 // Unfortunately, System.Reflection.Emit automatically emits
4123 // a leave to the end of the finally block. This is a problem
4124 // if `returns' is true since we may jump to a point after the
4125 // end of the method.
4126 // As a workaround, emit an explicit ret here.
4127 ec.NeedReturnLabel ();
4133 protected override void DoEmit (EmitContext ec)
4135 ILGenerator ig = ec.ig;
4138 ig.BeginExceptionBlock ();
4141 foreach (Catch c in Specific)
4144 if (General != null)
4149 ig.EndExceptionBlock ();
4152 public override void EmitFinally (EmitContext ec)
4158 public bool HasCatch
4161 return General != null || Specific.Count > 0;
4166 public class Using : ExceptionStatement {
4167 object expression_or_block;
4168 public Statement Statement;
4172 Expression [] resolved_vars;
4173 Expression [] converted_vars;
4174 ExpressionStatement [] assign;
4175 TemporaryVariable local_copy;
4177 public Using (object expression_or_block, Statement stmt, Location l)
4179 this.expression_or_block = expression_or_block;
4185 // Resolves for the case of using using a local variable declaration.
4187 bool ResolveLocalVariableDecls (EmitContext ec)
4191 TypeExpr texpr = expr.ResolveAsTypeTerminal (ec, false);
4195 expr_type = texpr.Type;
4198 // The type must be an IDisposable or an implicit conversion
4201 converted_vars = new Expression [var_list.Count];
4202 resolved_vars = new Expression [var_list.Count];
4203 assign = new ExpressionStatement [var_list.Count];
4205 bool need_conv = !TypeManager.ImplementsInterface (
4206 expr_type, TypeManager.idisposable_type);
4208 foreach (DictionaryEntry e in var_list){
4209 Expression var = (Expression) e.Key;
4211 var = var.ResolveLValue (ec, new EmptyExpression (), loc);
4215 resolved_vars [i] = var;
4222 converted_vars [i] = Convert.ImplicitConversion (
4223 ec, var, TypeManager.idisposable_type, loc);
4225 if (converted_vars [i] == null) {
4226 Error_IsNotConvertibleToIDisposable ();
4234 foreach (DictionaryEntry e in var_list){
4235 Expression var = resolved_vars [i];
4236 Expression new_expr = (Expression) e.Value;
4239 a = new Assign (var, new_expr, loc);
4245 converted_vars [i] = var;
4246 assign [i] = (ExpressionStatement) a;
4253 void Error_IsNotConvertibleToIDisposable ()
4255 Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4256 TypeManager.CSharpName (expr_type));
4259 bool ResolveExpression (EmitContext ec)
4261 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4262 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4263 Error_IsNotConvertibleToIDisposable ();
4268 local_copy = new TemporaryVariable (expr_type, loc);
4269 local_copy.Resolve (ec);
4275 // Emits the code for the case of using using a local variable declaration.
4277 void EmitLocalVariableDecls (EmitContext ec)
4279 ILGenerator ig = ec.ig;
4282 for (i = 0; i < assign.Length; i++) {
4283 assign [i].EmitStatement (ec);
4286 ig.BeginExceptionBlock ();
4288 Statement.Emit (ec);
4290 var_list.Reverse ();
4295 void EmitLocalVariableDeclFinally (EmitContext ec)
4297 ILGenerator ig = ec.ig;
4299 int i = assign.Length;
4300 for (int ii = 0; ii < var_list.Count; ++ii){
4301 Expression var = resolved_vars [--i];
4302 Label skip = ig.DefineLabel ();
4305 ig.BeginFinallyBlock ();
4307 if (!var.Type.IsValueType) {
4309 ig.Emit (OpCodes.Brfalse, skip);
4310 converted_vars [i].Emit (ec);
4311 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4313 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4315 if (!(ml is MethodGroupExpr)) {
4317 ig.Emit (OpCodes.Box, var.Type);
4318 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4320 MethodInfo mi = null;
4322 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4323 if (TypeManager.GetParameterData (mk).Count == 0) {
4330 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4334 IMemoryLocation mloc = (IMemoryLocation) var;
4336 mloc.AddressOf (ec, AddressOp.Load);
4337 ig.Emit (OpCodes.Call, mi);
4341 ig.MarkLabel (skip);
4344 ig.EndExceptionBlock ();
4346 ig.BeginFinallyBlock ();
4351 void EmitExpression (EmitContext ec)
4354 // Make a copy of the expression and operate on that.
4356 ILGenerator ig = ec.ig;
4358 local_copy.Store (ec, expr);
4361 ig.BeginExceptionBlock ();
4363 Statement.Emit (ec);
4367 ig.EndExceptionBlock ();
4370 void EmitExpressionFinally (EmitContext ec)
4372 ILGenerator ig = ec.ig;
4373 if (!expr_type.IsValueType) {
4374 Label skip = ig.DefineLabel ();
4375 local_copy.Emit (ec);
4376 ig.Emit (OpCodes.Brfalse, skip);
4377 local_copy.Emit (ec);
4378 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4379 ig.MarkLabel (skip);
4381 Expression ml = Expression.MemberLookup (
4382 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4383 "Dispose", Location.Null);
4385 if (!(ml is MethodGroupExpr)) {
4386 local_copy.Emit (ec);
4387 ig.Emit (OpCodes.Box, expr_type);
4388 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4390 MethodInfo mi = null;
4392 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4393 if (TypeManager.GetParameterData (mk).Count == 0) {
4400 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4404 local_copy.AddressOf (ec, AddressOp.Load);
4405 ig.Emit (OpCodes.Call, mi);
4410 public override bool Resolve (EmitContext ec)
4412 if (expression_or_block is DictionaryEntry){
4413 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4414 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4416 if (!ResolveLocalVariableDecls (ec))
4419 } else if (expression_or_block is Expression){
4420 expr = (Expression) expression_or_block;
4422 expr = expr.Resolve (ec);
4426 expr_type = expr.Type;
4428 if (!ResolveExpression (ec))
4432 FlowBranchingException branching = ec.StartFlowBranching (this);
4434 bool ok = Statement.Resolve (ec);
4437 ec.KillFlowBranching ();
4441 ResolveFinally (branching);
4442 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
4444 if (!reachability.AlwaysReturns) {
4445 // Unfortunately, System.Reflection.Emit automatically emits a leave
4446 // to the end of the finally block. This is a problem if `returns'
4447 // is true since we may jump to a point after the end of the method.
4448 // As a workaround, emit an explicit ret here.
4449 ec.NeedReturnLabel ();
4455 protected override void DoEmit (EmitContext ec)
4457 if (expression_or_block is DictionaryEntry)
4458 EmitLocalVariableDecls (ec);
4459 else if (expression_or_block is Expression)
4460 EmitExpression (ec);
4463 public override void EmitFinally (EmitContext ec)
4465 if (expression_or_block is DictionaryEntry)
4466 EmitLocalVariableDeclFinally (ec);
4467 else if (expression_or_block is Expression)
4468 EmitExpressionFinally (ec);
4473 /// Implementation of the foreach C# statement
4475 public class Foreach : Statement {
4477 Expression variable;
4479 Statement statement;
4481 CollectionForeach collection;
4483 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4484 Statement stmt, Location l)
4487 this.variable = var;
4493 public Statement Statement {
4494 get { return statement; }
4497 public override bool Resolve (EmitContext ec)
4499 expr = expr.Resolve (ec);
4503 Constant c = expr as Constant;
4504 if (c != null && c.GetValue () == null) {
4505 Report.Error (186, loc, "Use of null is not valid in this context");
4509 TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
4513 Type var_type = texpr.Type;
4515 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
4516 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
4517 expr.ExprClassName);
4522 // We need an instance variable. Not sure this is the best
4523 // way of doing this.
4525 // FIXME: When we implement propertyaccess, will those turn
4526 // out to return values in ExprClass? I think they should.
4528 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4529 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4530 collection.Error_Enumerator ();
4534 if (expr.Type.IsArray) {
4535 array = new ArrayForeach (var_type, variable, expr, statement, loc);
4536 return array.Resolve (ec);
4538 collection = new CollectionForeach (
4539 var_type, variable, expr, statement, loc);
4540 return collection.Resolve (ec);
4544 protected override void DoEmit (EmitContext ec)
4546 ILGenerator ig = ec.ig;
4548 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4549 ec.LoopBegin = ig.DefineLabel ();
4550 ec.LoopEnd = ig.DefineLabel ();
4552 if (collection != null)
4553 collection.Emit (ec);
4557 ec.LoopBegin = old_begin;
4558 ec.LoopEnd = old_end;
4561 protected class ArrayCounter : TemporaryVariable
4563 public ArrayCounter (Location loc)
4564 : base (TypeManager.int32_type, loc)
4567 public void Initialize (EmitContext ec)
4570 ec.ig.Emit (OpCodes.Ldc_I4_0);
4574 public void Increment (EmitContext ec)
4578 ec.ig.Emit (OpCodes.Ldc_I4_1);
4579 ec.ig.Emit (OpCodes.Add);
4584 protected class ArrayForeach : Statement
4586 Expression variable, expr, conv;
4587 Statement statement;
4590 TemporaryVariable[] lengths;
4591 ArrayCounter[] counter;
4594 TemporaryVariable copy;
4597 public ArrayForeach (Type var_type, Expression var,
4598 Expression expr, Statement stmt, Location l)
4600 this.var_type = var_type;
4601 this.variable = var;
4607 public override bool Resolve (EmitContext ec)
4609 array_type = expr.Type;
4610 rank = array_type.GetArrayRank ();
4612 copy = new TemporaryVariable (array_type, loc);
4615 counter = new ArrayCounter [rank];
4616 lengths = new TemporaryVariable [rank];
4618 ArrayList list = new ArrayList ();
4619 for (int i = 0; i < rank; i++) {
4620 counter [i] = new ArrayCounter (loc);
4621 counter [i].Resolve (ec);
4623 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4624 lengths [i].Resolve (ec);
4626 list.Add (counter [i]);
4629 access = new ElementAccess (copy, list).Resolve (ec);
4633 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
4639 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4640 ec.CurrentBranching.CreateSibling ();
4642 variable = variable.ResolveLValue (ec, conv, loc);
4643 if (variable == null)
4646 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
4647 if (!statement.Resolve (ec))
4649 ec.EndFlowBranching ();
4651 // There's no direct control flow from the end of the embedded statement to the end of the loop
4652 ec.CurrentBranching.CurrentUsageVector.Goto ();
4654 ec.EndFlowBranching ();
4659 protected override void DoEmit (EmitContext ec)
4661 ILGenerator ig = ec.ig;
4663 copy.Store (ec, expr);
4665 Label[] test = new Label [rank];
4666 Label[] loop = new Label [rank];
4668 for (int i = 0; i < rank; i++) {
4669 test [i] = ig.DefineLabel ();
4670 loop [i] = ig.DefineLabel ();
4672 lengths [i].EmitThis (ec);
4673 ((ArrayAccess) access).EmitGetLength (ec, i);
4674 lengths [i].EmitStore (ec);
4677 for (int i = 0; i < rank; i++) {
4678 counter [i].Initialize (ec);
4680 ig.Emit (OpCodes.Br, test [i]);
4681 ig.MarkLabel (loop [i]);
4684 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4686 statement.Emit (ec);
4688 ig.MarkLabel (ec.LoopBegin);
4690 for (int i = rank - 1; i >= 0; i--){
4691 counter [i].Increment (ec);
4693 ig.MarkLabel (test [i]);
4694 counter [i].Emit (ec);
4695 lengths [i].Emit (ec);
4696 ig.Emit (OpCodes.Blt, loop [i]);
4699 ig.MarkLabel (ec.LoopEnd);
4703 protected class CollectionForeach : ExceptionStatement
4705 Expression variable, expr;
4706 Statement statement;
4708 TemporaryVariable enumerator;
4712 MethodGroupExpr get_enumerator;
4713 PropertyExpr get_current;
4714 MethodInfo move_next;
4715 Type var_type, enumerator_type;
4717 bool enumerator_found;
4719 public CollectionForeach (Type var_type, Expression var,
4720 Expression expr, Statement stmt, Location l)
4722 this.var_type = var_type;
4723 this.variable = var;
4729 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
4731 Type return_type = mi.ReturnType;
4733 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
4735 // Apply the same optimization as MS: skip the GetEnumerator
4736 // returning an IEnumerator, and use the one returning a
4737 // CharEnumerator instead. This allows us to avoid the
4738 // try-finally block and the boxing.
4743 // Ok, we can access it, now make sure that we can do something
4744 // with this `GetEnumerator'
4747 if (return_type == TypeManager.ienumerator_type ||
4748 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
4749 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
4751 // If it is not an interface, lets try to find the methods ourselves.
4752 // For example, if we have:
4753 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4754 // We can avoid the iface call. This is a runtime perf boost.
4755 // even bigger if we have a ValueType, because we avoid the cost
4758 // We have to make sure that both methods exist for us to take
4759 // this path. If one of the methods does not exist, we will just
4760 // use the interface. Sadly, this complex if statement is the only
4761 // way I could do this without a goto
4766 // Prefer a generic enumerator over a non-generic one.
4768 if (return_type.IsInterface && return_type.IsGenericType) {
4769 enumerator_type = return_type;
4770 if (!FetchGetCurrent (ec, return_type))
4771 get_current = new PropertyExpr (
4772 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
4773 if (!FetchMoveNext (return_type))
4774 move_next = TypeManager.bool_movenext_void;
4779 if (return_type.IsInterface ||
4780 !FetchMoveNext (return_type) ||
4781 !FetchGetCurrent (ec, return_type)) {
4782 enumerator_type = return_type;
4783 move_next = TypeManager.bool_movenext_void;
4784 get_current = new PropertyExpr (
4785 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
4790 // Ok, so they dont return an IEnumerable, we will have to
4791 // find if they support the GetEnumerator pattern.
4794 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
4795 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",
4796 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
4801 enumerator_type = return_type;
4802 is_disposable = !enumerator_type.IsSealed ||
4803 TypeManager.ImplementsInterface (
4804 enumerator_type, TypeManager.idisposable_type);
4810 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4812 bool FetchMoveNext (Type t)
4814 MemberList move_next_list;
4816 move_next_list = TypeContainer.FindMembers (
4817 t, MemberTypes.Method,
4818 BindingFlags.Public | BindingFlags.Instance,
4819 Type.FilterName, "MoveNext");
4820 if (move_next_list.Count == 0)
4823 foreach (MemberInfo m in move_next_list){
4824 MethodInfo mi = (MethodInfo) m;
4826 if ((TypeManager.GetParameterData (mi).Count == 0) &&
4827 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
4837 // Retrieves a `public T get_Current ()' method from the Type `t'
4839 bool FetchGetCurrent (EmitContext ec, Type t)
4841 PropertyExpr pe = Expression.MemberLookup (
4842 ec.ContainerType, t, "Current", MemberTypes.Property,
4843 Expression.AllBindingFlags, loc) as PropertyExpr;
4852 // Retrieves a `public void Dispose ()' method from the Type `t'
4854 static MethodInfo FetchMethodDispose (Type t)
4856 MemberList dispose_list;
4858 dispose_list = TypeContainer.FindMembers (
4859 t, MemberTypes.Method,
4860 BindingFlags.Public | BindingFlags.Instance,
4861 Type.FilterName, "Dispose");
4862 if (dispose_list.Count == 0)
4865 foreach (MemberInfo m in dispose_list){
4866 MethodInfo mi = (MethodInfo) m;
4868 if (TypeManager.GetParameterData (mi).Count == 0){
4869 if (mi.ReturnType == TypeManager.void_type)
4876 public void Error_Enumerator ()
4878 if (enumerator_found) {
4882 Report.Error (1579, loc,
4883 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
4884 TypeManager.CSharpName (expr.Type));
4887 bool TryType (EmitContext ec, Type t)
4889 MethodGroupExpr mg = Expression.MemberLookup (
4890 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
4891 Expression.AllBindingFlags, loc) as MethodGroupExpr;
4895 MethodInfo result = null;
4896 MethodInfo tmp_move_next = null;
4897 PropertyExpr tmp_get_cur = null;
4898 Type tmp_enumerator_type = enumerator_type;
4899 foreach (MethodInfo mi in mg.Methods) {
4900 if (TypeManager.GetParameterData (mi).Count != 0)
4903 // Check whether GetEnumerator is public
4904 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
4907 if (TypeManager.IsOverride (mi))
4910 enumerator_found = true;
4912 if (!GetEnumeratorFilter (ec, mi))
4915 if (result != null) {
4916 if (TypeManager.IsGenericType (result.ReturnType)) {
4917 if (!TypeManager.IsGenericType (mi.ReturnType))
4920 Report.SymbolRelatedToPreviousError(t);
4921 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
4922 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
4923 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mi));
4927 // Always prefer generics enumerators
4928 if (TypeManager.IsGenericType(mi.ReturnType))
4931 Report.SymbolRelatedToPreviousError (result);
4932 Report.SymbolRelatedToPreviousError (mi);
4933 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
4934 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
4937 tmp_move_next = move_next;
4938 tmp_get_cur = get_current;
4939 tmp_enumerator_type = enumerator_type;
4940 if (mi.DeclaringType == t)
4944 if (result != null) {
4945 move_next = tmp_move_next;
4946 get_current = tmp_get_cur;
4947 enumerator_type = tmp_enumerator_type;
4948 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
4949 get_enumerator = new MethodGroupExpr (mi, loc);
4951 if (t != expr.Type) {
4952 expr = Convert.ExplicitConversion (
4955 throw new InternalErrorException ();
4958 get_enumerator.InstanceExpression = expr;
4959 get_enumerator.IsBase = t != expr.Type;
4967 bool ProbeCollectionType (EmitContext ec, Type t)
4969 int errors = Report.Errors;
4970 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
4971 if (TryType (ec, tt))
4976 if (Report.Errors > errors)
4980 // Now try to find the method in the interfaces
4983 Type [] ifaces = t.GetInterfaces ();
4985 foreach (Type i in ifaces){
4986 if (TryType (ec, i))
4991 // Since TypeBuilder.GetInterfaces only returns the interface
4992 // types for this type, we have to keep looping, but once
4993 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4994 // done, because it returns all the types
4996 if ((t is TypeBuilder))
5005 public override bool Resolve (EmitContext ec)
5007 enumerator_type = TypeManager.ienumerator_type;
5008 is_disposable = true;
5010 if (!ProbeCollectionType (ec, expr.Type)) {
5011 Error_Enumerator ();
5015 enumerator = new TemporaryVariable (enumerator_type, loc);
5016 enumerator.Resolve (ec);
5018 init = new Invocation (get_enumerator, new ArrayList ());
5019 init = init.Resolve (ec);
5023 Expression move_next_expr;
5025 MemberInfo[] mi = new MemberInfo[] { move_next };
5026 MethodGroupExpr mg = new MethodGroupExpr (mi, loc);
5027 mg.InstanceExpression = enumerator;
5029 move_next_expr = new Invocation (mg, new ArrayList ());
5032 get_current.InstanceExpression = enumerator;
5034 Statement block = new CollectionForeachStatement (
5035 var_type, variable, get_current, statement, loc);
5037 loop = new While (move_next_expr, block, loc);
5041 FlowBranchingException branching = null;
5043 branching = ec.StartFlowBranching (this);
5045 if (!loop.Resolve (ec))
5048 if (is_disposable) {
5049 ResolveFinally (branching);
5050 ec.EndFlowBranching ();
5052 emit_finally = true;
5057 protected override void DoEmit (EmitContext ec)
5059 ILGenerator ig = ec.ig;
5061 enumerator.Store (ec, init);
5064 // Protect the code in a try/finalize block, so that
5065 // if the beast implement IDisposable, we get rid of it
5067 if (is_disposable && emit_finally)
5068 ig.BeginExceptionBlock ();
5073 // Now the finally block
5075 if (is_disposable) {
5078 ig.EndExceptionBlock ();
5083 public override void EmitFinally (EmitContext ec)
5085 ILGenerator ig = ec.ig;
5087 if (enumerator_type.IsValueType) {
5088 MethodInfo mi = FetchMethodDispose (enumerator_type);
5090 enumerator.EmitLoadAddress (ec);
5091 ig.Emit (OpCodes.Call, mi);
5093 enumerator.Emit (ec);
5094 ig.Emit (OpCodes.Box, enumerator_type);
5095 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5098 Label call_dispose = ig.DefineLabel ();
5100 enumerator.Emit (ec);
5101 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5102 ig.Emit (OpCodes.Dup);
5103 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5104 ig.Emit (OpCodes.Pop);
5106 Label end_finally = ig.DefineLabel ();
5107 ig.Emit (OpCodes.Br, end_finally);
5109 ig.MarkLabel (call_dispose);
5110 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5111 ig.MarkLabel (end_finally);
5116 protected class CollectionForeachStatement : Statement
5119 Expression variable, current, conv;
5120 Statement statement;
5123 public CollectionForeachStatement (Type type, Expression variable,
5124 Expression current, Statement statement,
5128 this.variable = variable;
5129 this.current = current;
5130 this.statement = statement;
5134 public override bool Resolve (EmitContext ec)
5136 current = current.Resolve (ec);
5137 if (current == null)
5140 conv = Convert.ExplicitConversion (ec, current, type, loc);
5144 assign = new Assign (variable, conv, loc);
5145 if (assign.Resolve (ec) == null)
5148 if (!statement.Resolve (ec))
5154 protected override void DoEmit (EmitContext ec)
5156 assign.EmitStatement (ec);
5157 statement.Emit (ec);