2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Diagnostics;
15 namespace Mono.CSharp {
17 using System.Collections;
19 public abstract class Statement {
22 /// Return value indicates whether all code paths emitted return.
24 public abstract bool Emit (EmitContext ec);
27 /// Emits a bool expression.
29 public static Expression EmitBoolExpression (EmitContext ec, Expression e,
37 if (e.Type != TypeManager.bool_type)
38 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
43 31, "Can not convert the expression to a boolean");
51 if (u.Oper == Unary.Operator.LogicalNot){
54 u.EmitLogicalNot (ec);
63 ec.ig.Emit (OpCodes.Brfalse, l);
65 ec.ig.Emit (OpCodes.Brtrue, l);
68 ec.ig.Emit (OpCodes.Brtrue, l);
70 ec.ig.Emit (OpCodes.Brfalse, l);
78 public class EmptyStatement : Statement {
79 public override bool Emit (EmitContext ec)
85 public class If : Statement {
86 public readonly Expression Expr;
87 public readonly Statement TrueStatement;
88 public readonly Statement FalseStatement;
90 public If (Expression expr, Statement trueStatement)
93 TrueStatement = trueStatement;
96 public If (Expression expr,
97 Statement trueStatement,
98 Statement falseStatement)
101 TrueStatement = trueStatement;
102 FalseStatement = falseStatement;
105 public override bool Emit (EmitContext ec)
107 ILGenerator ig = ec.ig;
108 Label false_target = ig.DefineLabel ();
110 bool is_true_ret, is_false_ret;
112 if (EmitBoolExpression (ec, Expr, false_target, false) == null)
115 is_true_ret = TrueStatement.Emit (ec);
116 is_false_ret = is_true_ret;
118 if (FalseStatement != null){
119 bool branch_emitted = false;
121 end = ig.DefineLabel ();
123 ig.Emit (OpCodes.Br, end);
124 branch_emitted = true;
127 ig.MarkLabel (false_target);
128 is_false_ret = FalseStatement.Emit (ec);
133 ig.MarkLabel (false_target);
134 is_false_ret = false;
137 return is_true_ret && is_false_ret;
141 public class Do : Statement {
142 public readonly Expression Expr;
143 public readonly Statement EmbeddedStatement;
145 public Do (Statement statement, Expression boolExpr)
148 EmbeddedStatement = statement;
151 public override bool Emit (EmitContext ec)
153 ILGenerator ig = ec.ig;
154 Label loop = ig.DefineLabel ();
155 Label old_begin = ec.LoopBegin;
156 Label old_end = ec.LoopEnd;
157 bool old_inloop = ec.InLoop;
160 ec.LoopBegin = ig.DefineLabel ();
161 ec.LoopEnd = ig.DefineLabel ();
165 EmbeddedStatement.Emit (ec);
166 ig.MarkLabel (ec.LoopBegin);
167 e = EmitBoolExpression (ec, Expr, loop, true);
168 ig.MarkLabel (ec.LoopEnd);
170 ec.LoopBegin = old_begin;
171 ec.LoopEnd = old_end;
172 ec.InLoop = old_inloop;
175 // Inform whether we are infinite or not
177 if (e is BoolConstant){
178 BoolConstant bc = (BoolConstant) e;
180 if (bc.Value == true)
188 public class While : Statement {
189 public readonly Expression Expr;
190 public readonly Statement Statement;
192 public While (Expression boolExpr, Statement statement)
195 Statement = statement;
198 public override bool Emit (EmitContext ec)
200 ILGenerator ig = ec.ig;
201 Label old_begin = ec.LoopBegin;
202 Label old_end = ec.LoopEnd;
203 bool old_inloop = ec.InLoop;
206 ec.LoopBegin = ig.DefineLabel ();
207 ec.LoopEnd = ig.DefineLabel ();
210 ig.MarkLabel (ec.LoopBegin);
211 e = EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
213 ig.Emit (OpCodes.Br, ec.LoopBegin);
214 ig.MarkLabel (ec.LoopEnd);
216 ec.LoopBegin = old_begin;
217 ec.LoopEnd = old_end;
218 ec.InLoop = old_inloop;
221 // Inform whether we are infinite or not
223 if (e is BoolConstant){
224 BoolConstant bc = (BoolConstant) e;
226 if (bc.Value == true)
233 public class For : Statement {
234 public readonly Statement InitStatement;
235 public readonly Expression Test;
236 public readonly Statement Increment;
237 public readonly Statement Statement;
239 public For (Statement initStatement,
244 InitStatement = initStatement;
246 Increment = increment;
247 Statement = statement;
250 public override bool Emit (EmitContext ec)
252 ILGenerator ig = ec.ig;
253 Label old_begin = ec.LoopBegin;
254 Label old_end = ec.LoopEnd;
255 bool old_inloop = ec.InLoop;
256 Label loop = ig.DefineLabel ();
259 if (InitStatement != null)
260 if (! (InitStatement is EmptyStatement))
261 InitStatement.Emit (ec);
263 ec.LoopBegin = ig.DefineLabel ();
264 ec.LoopEnd = ig.DefineLabel ();
270 // If test is null, there is no test, and we are just
274 e = EmitBoolExpression (ec, Test, ec.LoopEnd, false);
277 ig.MarkLabel (ec.LoopBegin);
278 if (!(Increment is EmptyStatement))
280 ig.Emit (OpCodes.Br, loop);
281 ig.MarkLabel (ec.LoopEnd);
283 ec.LoopBegin = old_begin;
284 ec.LoopEnd = old_end;
285 ec.InLoop = old_inloop;
288 // Inform whether we are infinite or not
291 if (e is BoolConstant){
292 BoolConstant bc = (BoolConstant) e;
303 public class StatementExpression : Statement {
304 public readonly ExpressionStatement Expr;
306 public StatementExpression (ExpressionStatement expr)
311 public override bool Emit (EmitContext ec)
313 ILGenerator ig = ec.ig;
316 ne = Expr.Resolve (ec);
318 if (ne is ExpressionStatement)
319 ((ExpressionStatement) ne).EmitStatement (ec);
322 ig.Emit (OpCodes.Pop);
329 public override string ToString ()
331 return "StatementExpression (" + Expr + ")";
336 /// Implements the return statement
338 public class Return : Statement {
339 public Expression Expr;
340 public readonly Location loc;
342 public Return (Expression expr, Location l)
348 public override bool Emit (EmitContext ec)
351 Report.Error (157,loc,"Control can not leave the body of the finally block");
355 if (ec.ReturnType == null){
357 Report.Error (127, loc, "Return with a value not allowed here");
362 Report.Error (126, loc, "An object of type `" +
363 TypeManager.CSharpName (ec.ReturnType) + "' is " +
364 "expected for the return statement");
368 Expr = Expr.Resolve (ec);
372 if (Expr.Type != ec.ReturnType)
373 Expr = Expression.ConvertImplicitRequired (
374 ec, Expr, ec.ReturnType, loc);
381 if (ec.InTry || ec.InCatch)
382 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
385 if (ec.InTry || ec.InCatch){
386 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
389 ec.ig.Emit (OpCodes.Ret);
395 public class Goto : Statement {
400 public Goto (Block parent_block, string label, Location l)
402 block = parent_block;
407 public string Target {
413 public override bool Emit (EmitContext ec)
415 LabeledStatement label = block.LookupLabel (target);
419 // Maybe we should catch this before?
423 "No such label `" + target + "' in this scope");
426 Label l = label.LabelTarget (ec);
427 ec.ig.Emit (OpCodes.Br, l);
433 public class LabeledStatement : Statement {
438 public LabeledStatement (string label_name)
440 this.label_name = label_name;
443 public Label LabelTarget (EmitContext ec)
447 label = ec.ig.DefineLabel ();
453 public override bool Emit (EmitContext ec)
456 ec.ig.MarkLabel (label);
464 /// `goto default' statement
466 public class GotoDefault : Statement {
469 public GotoDefault (Location l)
474 public override bool Emit (EmitContext ec)
476 if (ec.Switch == null){
477 Report.Error (153, loc, "goto default is only valid in a switch statement");
481 if (!ec.Switch.GotDefault){
482 Report.Error (159, loc, "No default target on switch statement");
485 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
491 /// `goto case' statement
493 public class GotoCase : Statement {
497 public GotoCase (Expression e, Location l)
503 public override bool Emit (EmitContext ec)
505 if (ec.Switch == null){
506 Report.Error (153, loc, "goto case is only valid in a switch statement");
510 expr = expr.Resolve (ec);
514 if (!(expr is Constant)){
515 Report.Error (159, loc, "Target expression for goto case is not constant");
519 object val = Expression.ConvertIntLiteral (
520 (Constant) expr, ec.Switch.SwitchType, loc);
525 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
530 "No such label 'case " + val + "': for the goto case");
533 ec.ig.Emit (OpCodes.Br, sl.ILLabel);
538 public class Throw : Statement {
539 public readonly Expression Expr;
542 public Throw (Expression expr, Location l)
548 public override bool Emit (EmitContext ec)
552 ec.ig.Emit (OpCodes.Rethrow);
556 "A throw statement with no argument is only " +
557 "allowed in a catch clause");
562 Expression e = Expr.Resolve (ec);
569 ec.ig.Emit (OpCodes.Throw);
575 public class Break : Statement {
578 public Break (Location l)
583 public override bool Emit (EmitContext ec)
585 ILGenerator ig = ec.ig;
587 if (ec.InLoop == false && ec.Switch == null){
588 Report.Error (139, loc, "No enclosing loop or switch to continue to");
592 ig.Emit (OpCodes.Br, ec.LoopEnd);
597 public class Continue : Statement {
600 public Continue (Location l)
605 public override bool Emit (EmitContext ec)
607 Label begin = ec.LoopBegin;
610 Report.Error (139, loc, "No enclosing loop to continue to");
615 // UGH: Non trivial. This Br might cross a try/catch boundary
619 // try { ... } catch { continue; }
623 // try {} catch { while () { continue; }}
625 ec.ig.Emit (OpCodes.Br, begin);
630 public class VariableInfo {
631 public readonly string Type;
632 public LocalBuilder LocalBuilder;
633 public Type VariableType;
634 public readonly Location Location;
638 public bool Assigned;
639 public bool ReadOnly;
641 public VariableInfo (string type, Location l)
652 throw new Exception ("Unassigned idx for variable");
662 public void MakePinned ()
664 TypeManager.MakePinned (LocalBuilder);
669 /// Block represents a C# block.
673 /// This class is used in a number of places: either to represent
674 /// explicit blocks that the programmer places or implicit blocks.
676 /// Implicit blocks are used as labels or to introduce variable
679 public class Block : Statement {
680 public readonly Block Parent;
681 public readonly bool Implicit;
684 // The statements in this block
686 StatementCollection statements;
689 // An array of Blocks. We keep track of children just
690 // to generate the local variable declarations.
692 // Statements and child statements are handled through the
698 // Labels. (label, block) pairs.
703 // Keeps track of (name, type) pairs
708 // Keeps track of constants
712 // Maps variable names to ILGenerator.LocalBuilders
714 Hashtable local_builders;
722 public Block (Block parent)
725 parent.AddChild (this);
727 this.Parent = parent;
728 this.Implicit = false;
733 public Block (Block parent, bool implicit_block)
736 parent.AddChild (this);
738 this.Parent = parent;
739 this.Implicit = true;
749 void AddChild (Block b)
751 if (children == null)
752 children = new ArrayList ();
758 /// Adds a label to the current block.
762 /// false if the name already exists in this block. true
766 public bool AddLabel (string name, LabeledStatement target)
769 labels = new Hashtable ();
770 if (labels.Contains (name))
773 labels.Add (name, target);
777 public LabeledStatement LookupLabel (string name)
780 if (labels.Contains (name))
781 return ((LabeledStatement) labels [name]);
785 return Parent.LookupLabel (name);
790 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
792 if (variables == null)
793 variables = new Hashtable ();
795 if (GetVariableType (name) != null)
800 Parameter p = pars.GetParameterByName (name, out idx);
805 VariableInfo vi = new VariableInfo (type, l);
807 variables.Add (name, vi);
812 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
814 if (AddVariable (type, name, pars, l) == null)
817 if (constants == null)
818 constants = new Hashtable ();
820 constants.Add (name, value);
824 public Hashtable Variables {
830 public VariableInfo GetVariableInfo (string name)
832 if (variables != null) {
834 temp = variables [name];
837 return (VariableInfo) temp;
842 return Parent.GetVariableInfo (name);
847 public string GetVariableType (string name)
849 VariableInfo vi = GetVariableInfo (name);
857 public Expression GetConstantExpression (string name)
859 if (constants != null) {
861 temp = constants [name];
864 return (Expression) temp;
868 return Parent.GetConstantExpression (name);
874 /// True if the variable named @name has been defined
877 public bool IsVariableDefined (string name)
879 if (variables != null) {
880 if (variables.Contains (name))
885 return Parent.IsVariableDefined (name);
891 /// True if the variable named @name is a constant
893 public bool IsConstant (string name)
897 e = GetConstantExpression (name);
903 /// Use to fetch the statement associated with this label
905 public Statement this [string name] {
907 return (Statement) labels [name];
912 /// A list of labels that were not used within this block
914 public string [] GetUnreferenced ()
916 // FIXME: Implement me
920 public StatementCollection Statements {
922 if (statements == null)
923 statements = new StatementCollection ();
929 public void AddStatement (Statement s)
931 if (statements == null)
932 statements = new StatementCollection ();
950 /// Emits the variable declarations and labels.
953 /// tc: is our typecontainer (to resolve type references)
954 /// ig: is the code generator:
955 /// toplevel: the toplevel block. This is used for checking
956 /// that no two labels with the same name are used.
958 public int EmitMeta (EmitContext ec, Block toplevel, int count)
960 TypeContainer tc = ec.TypeContainer;
961 ILGenerator ig = ec.ig;
964 // Process this block variables
966 if (variables != null){
967 local_builders = new Hashtable ();
969 foreach (DictionaryEntry de in variables){
970 string name = (string) de.Key;
971 VariableInfo vi = (VariableInfo) de.Value;
974 t = RootContext.LookupType (tc, vi.Type, false, vi.Location);
979 vi.LocalBuilder = ig.DeclareLocal (t);
982 if (constants == null)
985 Expression cv = (Expression) constants [name];
989 Expression e = cv.Resolve (ec);
993 if (!(e is Constant)){
994 Report.Error (133, vi.Location,
995 "The expression being assigned to `" +
996 name + "' must be constant");
1000 constants.Remove (name);
1001 constants.Add (name, e);
1006 // Now, handle the children
1008 if (children != null){
1009 foreach (Block b in children)
1010 count = b.EmitMeta (ec, toplevel, count);
1016 public void UsageWarning ()
1020 if (variables != null){
1021 foreach (DictionaryEntry de in variables){
1022 VariableInfo vi = (VariableInfo) de.Value;
1027 name = (string) de.Key;
1031 219, vi.Location, "The variable `" + name +
1032 "' is assigned but its value is never used");
1035 168, vi.Location, "The variable `" +
1037 "' is declared but never used");
1042 if (children != null)
1043 foreach (Block b in children)
1047 // static int count;
1049 public override bool Emit (EmitContext ec)
1051 bool is_ret = false;
1052 Block prev_block = ec.CurrentBlock;
1055 ec.CurrentBlock = this;
1057 // throw new Exception ();
1058 foreach (Statement s in Statements)
1059 is_ret = s.Emit (ec);
1062 ec.CurrentBlock = prev_block;
1067 public class SwitchLabel {
1070 public Location loc;
1071 public Label ILLabel;
1074 // if expr == null, then it is the default case.
1076 public SwitchLabel (Expression expr, Location l)
1082 public Expression Label {
1088 public object Converted {
1095 // Resolves the expression, reduces it to a literal if possible
1096 // and then converts it to the requested type.
1098 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1100 ILLabel = ec.ig.DefineLabel ();
1105 Expression e = label.Resolve (ec);
1110 if (!(e is Constant)){
1111 Console.WriteLine ("Value is: " + label);
1112 Report.Error (150, loc, "A constant value is expected");
1116 if (e is StringConstant || e is NullLiteral){
1117 if (required_type == TypeManager.string_type){
1119 ILLabel = ec.ig.DefineLabel ();
1124 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1125 if (converted == null)
1132 public class SwitchSection {
1133 // An array of SwitchLabels.
1134 public readonly ArrayList Labels;
1135 public readonly Block Block;
1137 public SwitchSection (ArrayList labels, Block block)
1144 public class Switch : Statement {
1145 public readonly ArrayList Sections;
1146 public Expression Expr;
1149 /// Maps constants whose type type SwitchType to their SwitchLabels.
1151 public Hashtable Elements;
1154 /// The governing switch type
1156 public Type SwitchType;
1162 Label default_target;
1166 // The types allowed to be implicitly cast from
1167 // on the governing type
1169 static Type [] allowed_types;
1171 public Switch (Expression e, ArrayList sects, Location l)
1178 public bool GotDefault {
1184 public Label DefaultTarget {
1186 return default_target;
1191 // Determines the governing type for a switch. The returned
1192 // expression might be the expression from the switch, or an
1193 // expression that includes any potential conversions to the
1194 // integral types or to string.
1196 Expression SwitchGoverningType (EmitContext ec, Type t)
1198 if (t == TypeManager.int32_type ||
1199 t == TypeManager.uint32_type ||
1200 t == TypeManager.char_type ||
1201 t == TypeManager.byte_type ||
1202 t == TypeManager.sbyte_type ||
1203 t == TypeManager.ushort_type ||
1204 t == TypeManager.short_type ||
1205 t == TypeManager.uint64_type ||
1206 t == TypeManager.int64_type ||
1207 t == TypeManager.string_type ||
1208 t.IsSubclassOf (TypeManager.enum_type))
1211 if (allowed_types == null){
1212 allowed_types = new Type [] {
1213 TypeManager.sbyte_type,
1214 TypeManager.byte_type,
1215 TypeManager.short_type,
1216 TypeManager.ushort_type,
1217 TypeManager.int32_type,
1218 TypeManager.uint32_type,
1219 TypeManager.int64_type,
1220 TypeManager.uint64_type,
1221 TypeManager.char_type,
1222 TypeManager.string_type
1227 // Try to find a *user* defined implicit conversion.
1229 // If there is no implicit conversion, or if there are multiple
1230 // conversions, we have to report an error
1232 Expression converted = null;
1233 foreach (Type tt in allowed_types){
1236 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1240 if (converted != null){
1241 Report.Error (-12, loc, "More than one conversion to an integral " +
1242 " type exists for type `" +
1243 TypeManager.CSharpName (Expr.Type)+"'");
1251 void error152 (string n)
1254 152, "The label `" + n + ":' " +
1255 "is already present on this switch statement");
1259 // Performs the basic sanity checks on the switch statement
1260 // (looks for duplicate keys and non-constant expressions).
1262 // It also returns a hashtable with the keys that we will later
1263 // use to compute the switch tables
1265 bool CheckSwitch (EmitContext ec)
1269 Elements = new Hashtable ();
1271 got_default = false;
1273 if (TypeManager.IsEnumType (SwitchType)){
1274 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1276 compare_type = SwitchType;
1278 foreach (SwitchSection ss in Sections){
1279 foreach (SwitchLabel sl in ss.Labels){
1280 if (!sl.ResolveAndReduce (ec, SwitchType)){
1285 if (sl.Label == null){
1287 error152 ("default");
1294 object key = sl.Converted;
1296 if (key is Constant)
1297 key = ((Constant) key).GetValue ();
1300 key = NullLiteral.Null;
1302 string lname = null;
1303 if (compare_type == TypeManager.uint64_type){
1304 ulong v = (ulong) key;
1306 if (Elements.Contains (v))
1307 lname = v.ToString ();
1309 Elements.Add (v, sl);
1310 } else if (compare_type == TypeManager.int64_type){
1311 long v = (long) key;
1313 if (Elements.Contains (v))
1314 lname = v.ToString ();
1316 Elements.Add (v, sl);
1317 } else if (compare_type == TypeManager.uint32_type){
1318 uint v = (uint) key;
1320 if (Elements.Contains (v))
1321 lname = v.ToString ();
1323 Elements.Add (v, sl);
1324 } else if (compare_type == TypeManager.char_type){
1325 char v = (char) key;
1327 if (Elements.Contains (v))
1328 lname = v.ToString ();
1330 Elements.Add (v, sl);
1331 } else if (compare_type == TypeManager.byte_type){
1332 byte v = (byte) key;
1334 if (Elements.Contains (v))
1335 lname = v.ToString ();
1337 Elements.Add (v, sl);
1338 } else if (compare_type == TypeManager.sbyte_type){
1339 sbyte v = (sbyte) key;
1341 if (Elements.Contains (v))
1342 lname = v.ToString ();
1344 Elements.Add (v, sl);
1345 } else if (compare_type == TypeManager.short_type){
1346 short v = (short) key;
1348 if (Elements.Contains (v))
1349 lname = v.ToString ();
1351 Elements.Add (v, sl);
1352 } else if (compare_type == TypeManager.ushort_type){
1353 ushort v = (ushort) key;
1355 if (Elements.Contains (v))
1356 lname = v.ToString ();
1358 Elements.Add (v, sl);
1359 } else if (compare_type == TypeManager.string_type){
1360 if (key is NullLiteral){
1361 if (Elements.Contains (NullLiteral.Null))
1364 Elements.Add (NullLiteral.Null, null);
1366 string s = (string) key;
1368 if (Elements.Contains (s))
1371 Elements.Add (s, sl);
1373 } else if (compare_type == TypeManager.int32_type) {
1376 if (Elements.Contains (v))
1377 lname = v.ToString ();
1379 Elements.Add (v, sl);
1381 throw new Exception ("Unknown switch type!" +
1382 SwitchType + " " + compare_type);
1386 error152 ("case + " + lname);
1397 void EmitObjectInteger (ILGenerator ig, object k)
1400 IntConstant.EmitInt (ig, (int) k);
1401 else if (k is Constant){
1402 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1403 } else if (k is uint)
1404 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1406 LongConstant.EmitLong (ig, (long) k);
1407 else if (k is ulong)
1408 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1410 IntConstant.EmitInt (ig, (int) ((char) k));
1411 else if (k is sbyte)
1412 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1414 IntConstant.EmitInt (ig, (int) ((byte) k));
1416 throw new Exception ("Unhandled case");
1420 // This simple emit switch works, but does not take advantage of the
1421 // `switch' opcode. The swithc opcode uses a jump table that we are not
1422 // computing at this point
1424 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1426 ILGenerator ig = ec.ig;
1427 Label end_of_switch = ig.DefineLabel ();
1428 Label next_test = ig.DefineLabel ();
1429 Label null_target = ig.DefineLabel ();
1430 bool default_found = false;
1431 bool first_test = true;
1432 bool pending_goto_end = false;
1433 bool all_return = true;
1434 bool is_string = false;
1438 // Special processing for strings: we cant compare
1441 if (SwitchType == TypeManager.string_type){
1442 ig.Emit (OpCodes.Ldloc, val);
1445 if (Elements.Contains (NullLiteral.Null)){
1446 ig.Emit (OpCodes.Brfalse, null_target);
1448 ig.Emit (OpCodes.Brfalse, default_target);
1450 ig.Emit (OpCodes.Ldloc, val);
1451 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1452 ig.Emit (OpCodes.Stloc, val);
1455 foreach (SwitchSection ss in Sections){
1456 Label sec_begin = ig.DefineLabel ();
1458 if (pending_goto_end)
1459 ig.Emit (OpCodes.Br, end_of_switch);
1461 int label_count = ss.Labels.Count;
1463 foreach (SwitchLabel sl in ss.Labels){
1464 ig.MarkLabel (sl.ILLabel);
1467 ig.MarkLabel (next_test);
1468 next_test = ig.DefineLabel ();
1471 // If we are the default target
1473 if (sl.Label == null){
1474 ig.MarkLabel (default_target);
1475 default_found = true;
1477 object lit = sl.Converted;
1479 if (lit is NullLiteral){
1481 if (label_count == 1)
1482 ig.Emit (OpCodes.Br, next_test);
1487 StringConstant str = (StringConstant) lit;
1489 ig.Emit (OpCodes.Ldloc, val);
1490 ig.Emit (OpCodes.Ldstr, str.Value);
1491 if (label_count == 1)
1492 ig.Emit (OpCodes.Bne_Un, next_test);
1494 ig.Emit (OpCodes.Beq, sec_begin);
1496 ig.Emit (OpCodes.Ldloc, val);
1497 EmitObjectInteger (ig, lit);
1498 ig.Emit (OpCodes.Ceq);
1499 if (label_count == 1)
1500 ig.Emit (OpCodes.Brfalse, next_test);
1502 ig.Emit (OpCodes.Brtrue, sec_begin);
1506 if (label_count != 1)
1507 ig.Emit (OpCodes.Br, next_test);
1510 ig.MarkLabel (null_target);
1511 ig.MarkLabel (sec_begin);
1512 if (ss.Block.Emit (ec))
1513 pending_goto_end = false;
1516 pending_goto_end = true;
1521 ig.MarkLabel (default_target);
1522 ig.MarkLabel (next_test);
1523 ig.MarkLabel (end_of_switch);
1528 public override bool Emit (EmitContext ec)
1530 Expr = Expr.Resolve (ec);
1534 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1535 if (new_expr == null){
1536 Report.Error (151, loc, "An integer type or string was expected for switch");
1541 SwitchType = new_expr.Type;
1543 if (!CheckSwitch (ec))
1546 // Store variable for comparission purposes
1547 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1549 ec.ig.Emit (OpCodes.Stloc, value);
1551 ILGenerator ig = ec.ig;
1553 default_target = ig.DefineLabel ();
1556 // Setup the codegen context
1558 Label old_end = ec.LoopEnd;
1559 Switch old_switch = ec.Switch;
1561 ec.LoopEnd = ig.DefineLabel ();
1565 bool all_return = SimpleSwitchEmit (ec, value);
1567 // Restore context state.
1568 ig.MarkLabel (ec.LoopEnd);
1571 // FIXME: I am emitting a nop, because the switch performs
1572 // no analysis on whether something ever reaches the end
1574 // try: b (int a) { switch (a) { default: return 0; } }
1575 ig.Emit (OpCodes.Nop);
1578 // Restore the previous context
1580 ec.LoopEnd = old_end;
1581 ec.Switch = old_switch;
1584 // Because we have a nop at the end
1590 public class Lock : Statement {
1591 public readonly Expression Expr;
1592 public readonly Statement Statement;
1595 public Lock (Expression expr, Statement stmt, Location l)
1602 public override bool Emit (EmitContext ec)
1604 Expression e = Expr.Resolve (ec);
1610 if (type.IsValueType){
1611 Report.Error (185, loc, "lock statement requires the expression to be " +
1612 " a reference type (type is: `" +
1613 TypeManager.CSharpName (type) + "'");
1617 ILGenerator ig = ec.ig;
1618 LocalBuilder temp = ig.DeclareLocal (type);
1621 ig.Emit (OpCodes.Dup);
1622 ig.Emit (OpCodes.Stloc, temp);
1623 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1626 Label end = ig.BeginExceptionBlock ();
1627 bool old_in_try = ec.InTry;
1629 Label finish = ig.DefineLabel ();
1630 Statement.Emit (ec);
1631 ec.InTry = old_in_try;
1632 // ig.Emit (OpCodes.Leave, finish);
1634 ig.MarkLabel (finish);
1637 ig.BeginFinallyBlock ();
1638 ig.Emit (OpCodes.Ldloc, temp);
1639 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1640 ig.EndExceptionBlock ();
1646 public class Unchecked : Statement {
1647 public readonly Block Block;
1649 public Unchecked (Block b)
1654 public override bool Emit (EmitContext ec)
1656 bool previous_state = ec.CheckState;
1659 ec.CheckState = false;
1660 val = Block.Emit (ec);
1661 ec.CheckState = previous_state;
1667 public class Checked : Statement {
1668 public readonly Block Block;
1670 public Checked (Block b)
1675 public override bool Emit (EmitContext ec)
1677 bool previous_state = ec.CheckState;
1680 ec.CheckState = true;
1681 val = Block.Emit (ec);
1682 ec.CheckState = previous_state;
1688 public class Unsafe : Statement {
1689 public readonly Block Block;
1691 public Unsafe (Block b)
1696 public override bool Emit (EmitContext ec)
1698 bool previous_state = ec.InUnsafe;
1702 val = Block.Emit (ec);
1703 ec.InUnsafe = previous_state;
1712 public class Fixed : Statement {
1714 ArrayList declarators;
1715 Statement statement;
1718 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
1721 declarators = decls;
1726 public override bool Emit (EmitContext ec)
1728 ILGenerator ig = ec.ig;
1731 t = RootContext.LookupType (ec.TypeContainer, type, false, loc);
1735 foreach (Pair p in declarators){
1736 VariableInfo vi = (VariableInfo) p.First;
1737 Expression e = (Expression) p.Second;
1740 // The rules for the possible declarators are pretty wise,
1741 // but the production on the grammar is more concise.
1743 // So we have to enforce these rules here.
1745 // We do not resolve before doing the case 1 test,
1746 // because the grammar is explicit in that the token &
1747 // is present, so we need to test for this particular case.
1751 // Case 1: & object.
1753 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
1754 Expression child = ((Unary) e).Expr;
1757 if (child is ParameterReference || child is LocalVariableReference){
1760 "No need to use fixed statement for parameters or " +
1761 "local variable declarations (address is already " +
1770 child = ((Unary) e).Expr;
1772 if (!TypeManager.VerifyUnManaged (child.Type, loc))
1776 // Store pointer in pinned location
1779 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1781 statement.Emit (ec);
1783 // Clear the pinned variable.
1784 ig.Emit (OpCodes.Ldc_I4_0);
1785 ig.Emit (OpCodes.Conv_U);
1786 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1798 if (e.Type.IsArray){
1799 Type array_type = e.Type.GetElementType ();
1803 // Provided that array_type is unmanaged,
1805 if (!TypeManager.VerifyUnManaged (array_type, loc))
1809 // and T* is implicitly convertible to the
1810 // pointer type given in the fixed statement.
1812 ArrayPtr array_ptr = new ArrayPtr (e);
1814 Expression converted = Expression.ConvertImplicitRequired (
1815 ec, array_ptr, vi.VariableType, loc);
1816 if (converted == null)
1820 // Store pointer in pinned location
1822 converted.Emit (ec);
1824 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1826 statement.Emit (ec);
1828 // Clear the pinned variable.
1829 ig.Emit (OpCodes.Ldc_I4_0);
1830 ig.Emit (OpCodes.Conv_U);
1831 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1839 if (e.Type == TypeManager.string_type){
1840 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
1841 TypeManager.MakePinned (pinned_string);
1844 ig.Emit (OpCodes.Stloc, pinned_string);
1846 Expression sptr = new StringPtr (pinned_string);
1847 Expression converted = Expression.ConvertImplicitRequired (
1848 ec, sptr, vi.VariableType, loc);
1850 if (converted == null)
1853 converted.Emit (ec);
1854 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1856 statement.Emit (ec);
1858 // Clear the pinned variable
1859 ig.Emit (OpCodes.Ldnull);
1860 ig.Emit (OpCodes.Stloc, pinned_string);
1868 public class Catch {
1869 public readonly string Type;
1870 public readonly string Name;
1871 public readonly Block Block;
1872 public readonly Location Location;
1874 public Catch (string type, string name, Block block, Location l)
1883 public class Try : Statement {
1884 public readonly Block Fini, Block;
1885 public readonly ArrayList Specific;
1886 public readonly Catch General;
1889 // specific, general and fini might all be null.
1891 public Try (Block block, ArrayList specific, Catch general, Block fini)
1893 if (specific == null && general == null){
1894 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
1898 this.Specific = specific;
1899 this.General = general;
1903 public override bool Emit (EmitContext ec)
1905 ILGenerator ig = ec.ig;
1907 Label finish = ig.DefineLabel ();;
1910 end = ig.BeginExceptionBlock ();
1911 bool old_in_try = ec.InTry;
1913 returns = Block.Emit (ec);
1914 ec.InTry = old_in_try;
1917 // System.Reflection.Emit provides this automatically:
1918 // ig.Emit (OpCodes.Leave, finish);
1920 bool old_in_catch = ec.InCatch;
1922 DeclSpace ds = ec.TypeContainer;
1924 foreach (Catch c in Specific){
1925 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
1928 if (catch_type == null)
1931 ig.BeginCatchBlock (catch_type);
1933 if (c.Name != null){
1934 vi = c.Block.GetVariableInfo (c.Name);
1936 Console.WriteLine ("This should not happen! variable does not exist in this block");
1937 Environment.Exit (0);
1940 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1942 ig.Emit (OpCodes.Pop);
1947 if (General != null){
1948 ig.BeginCatchBlock (TypeManager.object_type);
1949 ig.Emit (OpCodes.Pop);
1950 General.Block.Emit (ec);
1952 ec.InCatch = old_in_catch;
1954 ig.MarkLabel (finish);
1956 ig.BeginFinallyBlock ();
1957 bool old_in_finally = ec.InFinally;
1958 ec.InFinally = true;
1960 ec.InFinally = old_in_finally;
1963 ig.EndExceptionBlock ();
1966 // FIXME: Is this correct?
1967 // Replace with `returns' and check test-18, maybe we can
1968 // perform an optimization here.
1975 // FIXME: We still do not support the expression variant of the using
1978 public class Using : Statement {
1979 object expression_or_block;
1980 Statement Statement;
1983 public Using (object expression_or_block, Statement stmt, Location l)
1985 this.expression_or_block = expression_or_block;
1991 // Emits the code for the case of using using a local variable declaration.
1993 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
1995 ILGenerator ig = ec.ig;
1996 Expression [] converted_vars;
1997 bool need_conv = false;
1998 Type type = RootContext.LookupType (ec.TypeContainer, type_name, false, loc);
2005 // The type must be an IDisposable or an implicit conversion
2008 converted_vars = new Expression [var_list.Count];
2009 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2010 foreach (DictionaryEntry e in var_list){
2011 Expression var = (Expression) e.Key;
2013 var = var.Resolve (ec);
2017 converted_vars [i] = Expression.ConvertImplicit (
2018 ec, var, TypeManager.idisposable_type, loc);
2020 if (converted_vars [i] == null)
2028 bool old_in_try = ec.InTry;
2030 foreach (DictionaryEntry e in var_list){
2031 LocalVariableReference var = (LocalVariableReference) e.Key;
2032 Expression expr = (Expression) e.Value;
2035 a = new Assign (var, expr, loc);
2038 converted_vars [i] = var;
2042 ((ExpressionStatement) a).EmitStatement (ec);
2044 ig.BeginExceptionBlock ();
2047 Statement.Emit (ec);
2048 ec.InTry = old_in_try;
2050 bool old_in_finally = ec.InFinally;
2051 ec.InFinally = true;
2052 var_list.Reverse ();
2053 foreach (DictionaryEntry e in var_list){
2054 LocalVariableReference var = (LocalVariableReference) e.Key;
2055 Label skip = ig.DefineLabel ();
2058 ig.BeginFinallyBlock ();
2061 ig.Emit (OpCodes.Brfalse, skip);
2062 converted_vars [i].Emit (ec);
2063 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2064 ig.MarkLabel (skip);
2065 ig.EndExceptionBlock ();
2067 ec.InFinally = old_in_finally;
2072 bool EmitExpression (EmitContext ec, Expression expr)
2074 Type expr_type = expr.Type;
2075 Expression conv = null;
2077 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2078 conv = Expression.ConvertImplicit (
2079 ec, expr, TypeManager.idisposable_type, loc);
2086 // Make a copy of the expression and operate on that.
2088 ILGenerator ig = ec.ig;
2089 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2094 ig.Emit (OpCodes.Stloc, local_copy);
2096 bool old_in_try = ec.InTry;
2098 ig.BeginExceptionBlock ();
2099 Statement.Emit (ec);
2100 ec.InTry = old_in_try;
2102 Label skip = ig.DefineLabel ();
2103 bool old_in_finally = ec.InFinally;
2104 ig.BeginFinallyBlock ();
2105 ig.Emit (OpCodes.Ldloc, local_copy);
2106 ig.Emit (OpCodes.Brfalse, skip);
2107 ig.Emit (OpCodes.Ldloc, local_copy);
2108 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2109 ig.MarkLabel (skip);
2110 ec.InFinally = old_in_finally;
2111 ig.EndExceptionBlock ();
2116 public override bool Emit (EmitContext ec)
2118 if (expression_or_block is DictionaryEntry){
2119 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2120 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2122 return EmitLocalVariableDecls (ec, t, var_list);
2123 } if (expression_or_block is Expression){
2124 Expression e = (Expression) expression_or_block;
2130 return EmitExpression (ec, e);
2137 /// Implementation of the foreach C# statement
2139 public class Foreach : Statement {
2141 LocalVariableReference variable;
2143 Statement statement;
2146 public Foreach (string type, LocalVariableReference var, Expression expr,
2147 Statement stmt, Location l)
2150 this.variable = var;
2156 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2161 if (!(m is MethodInfo))
2164 if (m.Name != "GetEnumerator")
2167 MethodInfo mi = (MethodInfo) m;
2169 if (mi.ReturnType != TypeManager.ienumerator_type){
2170 if (!TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType))
2174 Type [] args = TypeManager.GetArgumentTypes (mi);
2178 if (args.Length == 0)
2185 /// This filter is used to find the GetEnumerator method
2186 /// on which IEnumerator operates
2188 static MemberFilter FilterEnumerator;
2192 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2195 void error1579 (Type t)
2197 Report.Error (1579, loc,
2198 "foreach statement cannot operate on variables of type `" +
2199 t.FullName + "' because that class does not provide a " +
2200 " GetEnumerator method or it is inaccessible");
2203 MethodInfo ProbeCollectionType (Type t)
2207 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2208 BindingFlags.Public | BindingFlags.Instance,
2209 FilterEnumerator, null);
2216 if (mi.Length == 0){
2221 return (MethodInfo) mi [0];
2225 // FIXME: possible optimization.
2226 // We might be able to avoid creating `empty' if the type is the sam
2228 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
2230 ILGenerator ig = ec.ig;
2231 LocalBuilder enumerator, disposable;
2232 Expression empty = new EmptyExpression ();
2236 // FIXME: maybe we can apply the same trick we do in the
2237 // array handling to avoid creating empty and conv in some cases.
2239 // Although it is not as important in this case, as the type
2240 // will not likely be object (what the enumerator will return).
2242 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2246 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2247 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2250 // Instantiate the enumerator
2252 if (expr.Type.IsValueType){
2253 if (expr is IMemoryLocation){
2254 IMemoryLocation ml = (IMemoryLocation) expr;
2258 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2259 " does not implement IMemoryLocation");
2260 ig.Emit (OpCodes.Call, get_enum);
2263 ig.Emit (OpCodes.Callvirt, get_enum);
2265 ig.Emit (OpCodes.Stloc, enumerator);
2268 // Protect the code in a try/finalize block, so that
2269 // if the beast implement IDisposable, we get rid of it
2271 Label l = ig.BeginExceptionBlock ();
2272 bool old_in_try = ec.InTry;
2275 Label end_try = ig.DefineLabel ();
2277 ig.MarkLabel (ec.LoopBegin);
2278 ig.Emit (OpCodes.Ldloc, enumerator);
2279 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
2280 ig.Emit (OpCodes.Brfalse, end_try);
2281 ig.Emit (OpCodes.Ldloc, enumerator);
2282 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
2283 variable.EmitAssign (ec, conv);
2284 statement.Emit (ec);
2285 ig.Emit (OpCodes.Br, ec.LoopBegin);
2286 ig.MarkLabel (end_try);
2287 ec.InTry = old_in_try;
2289 // The runtime provides this for us.
2290 // ig.Emit (OpCodes.Leave, end);
2293 // Now the finally block
2295 Label end_finally = ig.DefineLabel ();
2296 bool old_in_finally = ec.InFinally;
2297 ec.InFinally = true;
2298 ig.BeginFinallyBlock ();
2300 ig.Emit (OpCodes.Ldloc, enumerator);
2301 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2302 ig.Emit (OpCodes.Stloc, disposable);
2303 ig.Emit (OpCodes.Ldloc, disposable);
2304 ig.Emit (OpCodes.Brfalse, end_finally);
2305 ig.Emit (OpCodes.Ldloc, disposable);
2306 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2307 ig.MarkLabel (end_finally);
2308 ec.InFinally = old_in_finally;
2310 // The runtime generates this anyways.
2311 // ig.Emit (OpCodes.Endfinally);
2313 ig.EndExceptionBlock ();
2315 ig.MarkLabel (ec.LoopEnd);
2320 // FIXME: possible optimization.
2321 // We might be able to avoid creating `empty' if the type is the sam
2323 bool EmitArrayForeach (EmitContext ec, Type var_type)
2325 Type array_type = expr.Type;
2326 Type element_type = array_type.GetElementType ();
2327 Expression conv = null;
2328 Expression empty = new EmptyExpression (element_type);
2330 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2334 int rank = array_type.GetArrayRank ();
2335 ILGenerator ig = ec.ig;
2337 LocalBuilder copy = ig.DeclareLocal (array_type);
2340 // Make our copy of the array
2343 ig.Emit (OpCodes.Stloc, copy);
2346 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2350 ig.Emit (OpCodes.Ldc_I4_0);
2351 ig.Emit (OpCodes.Stloc, counter);
2352 test = ig.DefineLabel ();
2353 ig.Emit (OpCodes.Br, test);
2355 loop = ig.DefineLabel ();
2356 ig.MarkLabel (loop);
2358 ig.Emit (OpCodes.Ldloc, copy);
2359 ig.Emit (OpCodes.Ldloc, counter);
2360 ArrayAccess.EmitLoadOpcode (ig, var_type);
2362 variable.EmitAssign (ec, conv);
2364 statement.Emit (ec);
2366 ig.MarkLabel (ec.LoopBegin);
2367 ig.Emit (OpCodes.Ldloc, counter);
2368 ig.Emit (OpCodes.Ldc_I4_1);
2369 ig.Emit (OpCodes.Add);
2370 ig.Emit (OpCodes.Stloc, counter);
2372 ig.MarkLabel (test);
2373 ig.Emit (OpCodes.Ldloc, counter);
2374 ig.Emit (OpCodes.Ldloc, copy);
2375 ig.Emit (OpCodes.Ldlen);
2376 ig.Emit (OpCodes.Conv_I4);
2377 ig.Emit (OpCodes.Blt, loop);
2379 LocalBuilder [] dim_len = new LocalBuilder [rank];
2380 LocalBuilder [] dim_count = new LocalBuilder [rank];
2381 Label [] loop = new Label [rank];
2382 Label [] test = new Label [rank];
2385 for (dim = 0; dim < rank; dim++){
2386 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2387 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2388 test [dim] = ig.DefineLabel ();
2389 loop [dim] = ig.DefineLabel ();
2392 for (dim = 0; dim < rank; dim++){
2393 ig.Emit (OpCodes.Ldloc, copy);
2394 IntLiteral.EmitInt (ig, dim);
2395 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2396 ig.Emit (OpCodes.Stloc, dim_len [dim]);
2399 for (dim = 0; dim < rank; dim++){
2400 ig.Emit (OpCodes.Ldc_I4_0);
2401 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2402 ig.Emit (OpCodes.Br, test [dim]);
2403 ig.MarkLabel (loop [dim]);
2406 ig.Emit (OpCodes.Ldloc, copy);
2407 for (dim = 0; dim < rank; dim++)
2408 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2411 // FIXME: Maybe we can cache the computation of `get'?
2413 Type [] args = new Type [rank];
2416 for (int i = 0; i < rank; i++)
2417 args [i] = TypeManager.int32_type;
2419 ModuleBuilder mb = RootContext.ModuleBuilder;
2420 get = mb.GetArrayMethod (
2422 CallingConventions.HasThis| CallingConventions.Standard,
2424 ig.Emit (OpCodes.Call, get);
2425 variable.EmitAssign (ec, conv);
2426 statement.Emit (ec);
2427 ig.MarkLabel (ec.LoopBegin);
2428 for (dim = rank - 1; dim >= 0; dim--){
2429 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2430 ig.Emit (OpCodes.Ldc_I4_1);
2431 ig.Emit (OpCodes.Add);
2432 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2434 ig.MarkLabel (test [dim]);
2435 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2436 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2437 ig.Emit (OpCodes.Blt, loop [dim]);
2440 ig.MarkLabel (ec.LoopEnd);
2445 public override bool Emit (EmitContext ec)
2450 expr = expr.Resolve (ec);
2454 var_type = RootContext.LookupType (ec.TypeContainer, type, false, loc);
2455 if (var_type == null)
2459 // We need an instance variable. Not sure this is the best
2460 // way of doing this.
2462 // FIXME: When we implement propertyaccess, will those turn
2463 // out to return values in ExprClass? I think they should.
2465 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2466 expr.eclass == ExprClass.PropertyAccess)){
2467 error1579 (expr.Type);
2471 ILGenerator ig = ec.ig;
2473 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2474 bool old_inloop = ec.InLoop;
2475 ec.LoopBegin = ig.DefineLabel ();
2476 ec.LoopEnd = ig.DefineLabel ();
2479 if (expr.Type.IsArray)
2480 ret_val = EmitArrayForeach (ec, var_type);
2482 MethodInfo get_enum;
2484 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
2487 ret_val = EmitCollectionForeach (ec, var_type, get_enum);
2490 ec.LoopBegin = old_begin;
2491 ec.LoopEnd = old_end;
2492 ec.InLoop = old_inloop;