X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fgmcs%2Fstatement.cs;h=fedda76d8578739d6b3a478625971df49edfc22b;hb=c5332919e18180ebf00eb241352938f17e924858;hp=e1bfe057608307812c780915be3d9740d0813875;hpb=38bf36b08163b2fbeb3242d6e21f009c0f0fbdb3;p=mono.git diff --git a/mcs/gmcs/statement.cs b/mcs/gmcs/statement.cs index e1bfe057608..fedda76d857 100644 --- a/mcs/gmcs/statement.cs +++ b/mcs/gmcs/statement.cs @@ -14,10 +14,10 @@ using System.Text; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics; +using System.Collections; +using System.Collections.Specialized; namespace Mono.CSharp { - - using System.Collections; public abstract class Statement { public Location loc; @@ -55,16 +55,7 @@ namespace Mono.CSharp { return ok; } - - protected void CheckObsolete (Type type) - { - ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type); - if (obsolete_attr == null) - return; - - AttributeTester.Report_ObsoleteMessage (obsolete_attr, type.FullName, loc); - } - + /// /// Return value indicates whether all code paths emitted return. /// @@ -80,7 +71,7 @@ namespace Mono.CSharp { public void Error (int error, string s) { - if (!Location.IsNull (loc)) + if (!loc.IsNull) Report.Error (error, loc, s); else Report.Error (error, s); @@ -410,7 +401,7 @@ namespace Mono.CSharp { Expression Test; readonly Statement InitStatement; readonly Statement Increment; - readonly Statement Statement; + public readonly Statement Statement; bool infinite, empty; public For (Statement initStatement, @@ -527,10 +518,10 @@ namespace Mono.CSharp { public class StatementExpression : Statement { ExpressionStatement expr; - public StatementExpression (ExpressionStatement expr, Location l) + public StatementExpression (ExpressionStatement expr) { this.expr = expr; - loc = l; + loc = expr.Location; } public override bool Resolve (EmitContext ec) @@ -800,21 +791,24 @@ namespace Mono.CSharp { if (expr == null) return false; - if (!(expr is Constant)){ + Constant c = expr as Constant; + if (c == null) { Error (150, "A constant value is expected"); return false; } - object val = Expression.ConvertIntLiteral ( - (Constant) expr, ec.Switch.SwitchType, loc); + c = c.ToType (ec.Switch.SwitchType, loc); + if (c == null) + return false; + object val = c.GetValue (); if (val == null) - return false; + val = SwitchLabel.NullStringCase; sl = (SwitchLabel) ec.Switch.Elements [val]; if (sl == null){ - Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", val); + Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", c.GetValue () == null ? "null" : val); return false; } @@ -947,7 +941,7 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { - if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){ + if (!ec.CurrentBranching.InLoop ()){ Error (139, "No enclosing loop out of which to break or continue"); return false; } else if (ec.InFinally) { @@ -1057,7 +1051,7 @@ namespace Mono.CSharp { if (texpr == null) return false; - VariableType = texpr.Type; + VariableType = texpr.ResolveType (ec); } if (VariableType == TypeManager.void_type) { @@ -1195,7 +1189,7 @@ namespace Mono.CSharp { public readonly ToplevelBlock Toplevel; [Flags] - public enum Flags { + public enum Flags : ushort { Implicit = 1, Unchecked = 2, BlockUsed = 4, @@ -1203,9 +1197,10 @@ namespace Mono.CSharp { HasRet = 16, IsDestructor = 32, IsToplevel = 64, - Unsafe = 128 + Unsafe = 128, + HasVarargs = 256 // Used in ToplevelBlock } - Flags flags; + protected Flags flags; public bool Implicit { get { return (flags & Flags.Implicit) != 0; } @@ -1458,29 +1453,57 @@ namespace Mono.CSharp { public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc) { - LocalInfo kvi = GetKnownVariableInfo (name); - if (kvi == null || kvi.Block == this) + Block b = this; + LocalInfo kvi = b.GetKnownVariableInfo (name); + while (kvi == null) { + while (b.Implicit) + b = b.Parent; + b = b.Parent; + if (b == null) + return true; + kvi = b.GetKnownVariableInfo (name); + } + + if (kvi.Block == b) return true; - if (known_variables != kvi.Block.known_variables) { - Report.SymbolRelatedToPreviousError (kvi.Location, name); - Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name); - return false; + // Is kvi.Block nested inside 'b' + if (b.known_variables != kvi.Block.known_variables) { + // + // If a variable by the same name it defined in a nested block of this + // block, we violate the invariant meaning in a block. + // + if (b == this) { + Report.SymbolRelatedToPreviousError (kvi.Location, name); + Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name); + return false; + } + + // + // It's ok if the definition is in a nested subblock of b, but not + // nested inside this block -- a definition in a sibling block + // should not affect us. + // + return true; } // - // this block and kvi.Block are the same textual block. + // Block 'b' and kvi.Block are the same textual block. // However, different variables are extant. // // Check if the variable is in scope in both blocks. We use // an indirect check that depends on AddVariable doing its // part in maintaining the invariant-meaning-in-block property. // - if (e is LocalVariableReference || (e is Constant && GetLocalInfo (name) != null)) + if (e is LocalVariableReference || (e is Constant && b.GetLocalInfo (name) != null)) return true; - Report.SymbolRelatedToPreviousError (kvi.Location, name); - Error_AlreadyDeclared (loc, name, "parent or current"); + // + // Even though we detected the error when the name is used, we + // treat it as if the variable declaration was in error. + // + Report.SymbolRelatedToPreviousError (loc, name); + Error_AlreadyDeclared (kvi.Location, name, "parent or current"); return false; } @@ -1725,16 +1748,13 @@ namespace Mono.CSharp { Constant ce = e as Constant; if (ce == null){ - Report.Error (133, vi.Location, - "The expression being assigned to `{0}' must be constant", name); + Const.Error_ExpressionMustBeConstant (vi.Location, name); continue; } - if (e.Type != variable_type){ - e = Const.ChangeType (vi.Location, ce, variable_type); - if (e == null) - continue; - } + e = ce.ToType (variable_type, vi.Location); + if (e == null) + continue; constants.Remove (name); constants.Add (name, e); @@ -1828,6 +1848,36 @@ namespace Mono.CSharp { bool unreachable_shown; bool unreachable; + private void CheckPossibleMistakenEmptyStatement (Statement s) + { + Statement body; + + // Some statements are wrapped by a Block. Since + // others' internal could be changed, here I treat + // them as possibly wrapped by Block equally. + Block b = s as Block; + if (b != null && b.statements.Count == 1) + s = (Statement) b.statements [0]; + + if (s is Lock) + body = ((Lock) s).Statement; + else if (s is For) + body = ((For) s).Statement; + else if (s is Foreach) + body = ((Foreach) s).Statement; + else if (s is While) + body = ((While) s).Statement; + else if (s is Using) + body = ((Using) s).Statement; + else if (s is Fixed) + body = ((Fixed) s).Statement; + else + return; + + if (body == null || body is EmptyStatement) + Report.Warning (642, 3, s.loc, "Possible mistaken empty statement"); + } + public override bool Resolve (EmitContext ec) { Block prev_block = ec.CurrentBlock; @@ -1849,6 +1899,11 @@ namespace Mono.CSharp { int statement_count = statements.Count; for (int ix = 0; ix < statement_count; ix++){ Statement s = (Statement) statements [ix]; + // Check possible empty statement (CS0642) + if (RootContext.WarningLevel >= 3 && + ix + 1 < statement_count && + statements [ix + 1] is Block) + CheckPossibleMistakenEmptyStatement (s); if (unreachable) { if (s is Block) @@ -1856,7 +1911,7 @@ namespace Mono.CSharp { if (!unreachable_shown && (RootContext.WarningLevel >= 2)) { Report.Warning ( - 162, loc, "Unreachable code detected"); + 162, s.loc, "Unreachable code detected"); unreachable_shown = true; } } @@ -2022,7 +2077,10 @@ namespace Mono.CSharp { Hashtable capture_contexts; ArrayList children; - public bool HasVarargs = false; + public bool HasVarargs { + get { return (flags & Flags.HasVarargs) != 0; } + set { flags |= Flags.HasVarargs; } + } // // The parameters for the block. @@ -2228,13 +2286,15 @@ namespace Mono.CSharp { public class SwitchLabel { Expression label; object converted; - public Location loc; + Location loc; Label il_label; bool il_label_set; Label il_label_code; bool il_label_code_set; + public static readonly object NullStringCase = new object (); + // // if expr == null, then it is the default case. // @@ -2279,33 +2339,43 @@ namespace Mono.CSharp { // and then converts it to the requested type. // public bool ResolveAndReduce (EmitContext ec, Type required_type) - { - if (label == null) - return true; - + { Expression e = label.Resolve (ec); if (e == null) return false; - if (!(e is Constant)){ - Report.Error (150, loc, "A constant value is expected, got: " + e); + Constant c = e as Constant; + if (c == null){ + Report.Error (150, loc, "A constant value is expected"); return false; } - if (e is StringConstant || e is NullLiteral){ - if (required_type == TypeManager.string_type){ - converted = e; - return true; - } + if (required_type == TypeManager.string_type && e is NullLiteral) { + converted = NullStringCase; + return true; } - converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc); - if (converted == null) + c = c.ToType (required_type, loc); + if (c == null) return false; + converted = c.GetValue (); return true; } + + public void Erorr_AlreadyOccurs () + { + string label; + if (converted == null) + label = "default"; + else if (converted is NullLiteral) + label = "null"; + else + label = converted.ToString (); + + Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label); + } } public class SwitchSection { @@ -2327,7 +2397,7 @@ namespace Mono.CSharp { /// /// Maps constants whose type type SwitchType to their SwitchLabels. /// - public Hashtable Elements; + public IDictionary Elements; /// /// The governing switch type @@ -2440,12 +2510,6 @@ namespace Mono.CSharp { return converted; } - static string Error152 { - get { - return "The label `{0}:' already occurs in this switch statement"; - } - } - // // Performs the basic sanity checks on the switch statement // (looks for duplicate keys and non-constant expressions). @@ -2455,141 +2519,38 @@ namespace Mono.CSharp { // bool CheckSwitch (EmitContext ec) { - Type compare_type; bool error = false; - Elements = new Hashtable (); + Elements = Sections.Count > 10 ? + (IDictionary)new Hashtable () : + (IDictionary)new ListDictionary (); - if (TypeManager.IsEnumType (SwitchType)){ - compare_type = TypeManager.EnumToUnderlying (SwitchType); - } else - compare_type = SwitchType; - foreach (SwitchSection ss in Sections){ foreach (SwitchLabel sl in ss.Labels){ - if (!sl.ResolveAndReduce (ec, SwitchType)){ - error = true; - continue; - } - if (sl.Label == null){ if (default_section != null){ - Report.Error (152, sl.loc, Error152, "default"); + sl.Erorr_AlreadyOccurs (); error = true; } default_section = ss; continue; } - - object key = sl.Converted; - - if (key is Constant) - key = ((Constant) key).GetValue (); - - if (key == null) - key = NullLiteral.Null; - - string lname = null; - if (compare_type == TypeManager.uint64_type){ - ulong v = (ulong) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.int64_type){ - long v = (long) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.uint32_type){ - uint v = (uint) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.char_type){ - char v = (char) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.byte_type){ - byte v = (byte) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.sbyte_type){ - sbyte v = (sbyte) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.short_type){ - short v = (short) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.ushort_type){ - ushort v = (ushort) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.string_type){ - if (key is NullLiteral){ - if (Elements.Contains (NullLiteral.Null)) - lname = "null"; - else - Elements.Add (NullLiteral.Null, null); - } else { - string s = (string) key; - - if (Elements.Contains (s)) - lname = s; - else - Elements.Add (s, sl); - } - } else if (compare_type == TypeManager.int32_type) { - int v = (int) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.bool_type) { - bool v = (bool) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } - else - { - throw new Exception ("Unknown switch type!" + - SwitchType + " " + compare_type); - } - if (lname != null){ - Report.Error (152, sl.loc, Error152, "case " + lname); + if (!sl.ResolveAndReduce (ec, SwitchType)){ error = true; + continue; + } + + object key = sl.Converted; + try { + Elements.Add (key, sl); } + catch (ArgumentException) { + sl.Erorr_AlreadyOccurs (); + error = true; + } } } - if (error) - return false; - - return true; + return !error; } void EmitObjectInteger (ILGenerator ig, object k) @@ -2889,7 +2850,7 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Ldloc, val); - if (Elements.Contains (NullLiteral.Null)){ + if (Elements.Contains (SwitchLabel.NullStringCase)){ ig.Emit (OpCodes.Brfalse, null_target); } else ig.Emit (OpCodes.Brfalse, default_target); @@ -2928,17 +2889,15 @@ namespace Mono.CSharp { if (sl.Label != null){ object lit = sl.Converted; - if (lit is NullLiteral){ + if (lit == SwitchLabel.NullStringCase){ null_found = true; if (label_count == 1) ig.Emit (OpCodes.Br, next_test); continue; - } - StringConstant str = (StringConstant) lit; ig.Emit (OpCodes.Ldloc, val); - ig.Emit (OpCodes.Ldstr, str.Value); + ig.Emit (OpCodes.Ldstr, (string)lit); if (label_count == 1) ig.Emit (OpCodes.Bne_Un, next_test); else { @@ -3121,8 +3080,8 @@ namespace Mono.CSharp { public class Lock : ExceptionStatement { Expression expr; - Statement Statement; - LocalBuilder temp; + public Statement Statement; + TemporaryVariable temp; public Lock (Expression expr, Statement stmt, Location l) { @@ -3139,8 +3098,8 @@ namespace Mono.CSharp { if (expr.Type.IsValueType){ Report.Error (185, loc, - "`{0}' is not a reference type as required by the lock statement", - TypeManager.CSharpName (expr.Type)); + "`{0}' is not a reference type as required by the lock statement", + TypeManager.CSharpName (expr.Type)); return false; } @@ -3163,19 +3122,18 @@ namespace Mono.CSharp { ec.NeedReturnLabel (); } + temp = new TemporaryVariable (expr.Type, loc); + temp.Resolve (ec); + return true; } protected override void DoEmit (EmitContext ec) { - Type type = expr.Type; - ILGenerator ig = ec.ig; - temp = ig.DeclareLocal (type); - - expr.Emit (ec); - ig.Emit (OpCodes.Dup); - ig.Emit (OpCodes.Stloc, temp); + + temp.Store (ec, expr); + temp.Emit (ec); ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object); // try @@ -3191,9 +3149,8 @@ namespace Mono.CSharp { public override void EmitFinally (EmitContext ec) { - ILGenerator ig = ec.ig; - ig.Emit (OpCodes.Ldloc, temp); - ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object); + temp.Emit (ec); + ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object); } } @@ -3326,7 +3283,7 @@ namespace Mono.CSharp { public abstract void EmitExit (ILGenerator ig); } - class ExpressionEmitter: Emitter { + class ExpressionEmitter : Emitter { public ExpressionEmitter (Expression converted, LocalInfo li) : base (converted, li) { @@ -3348,7 +3305,7 @@ namespace Mono.CSharp { } } - class StringEmitter: Emitter { + class StringEmitter : Emitter { LocalBuilder pinned_string; Location loc; @@ -3392,6 +3349,10 @@ namespace Mono.CSharp { loc = l; } + public Statement Statement { + get { return statement; } + } + public override bool Resolve (EmitContext ec) { if (!ec.InUnsafe){ @@ -3403,9 +3364,7 @@ namespace Mono.CSharp { if (texpr == null) return false; - expr_type = texpr.Type; - - CheckObsolete (expr_type); + expr_type = texpr.ResolveType (ec); data = new Emitter [declarators.Count]; @@ -3464,6 +3423,11 @@ namespace Mono.CSharp { if (!TypeManager.VerifyUnManaged (child.Type, loc)) return false; + if (!Convert.ImplicitConversionExists (ec, e, expr_type)) { + e.Error_ValueCannotBeConverted (e.Location, expr_type, false); + return false; + } + data [i] = new ExpressionEmitter (e, vi); i++; @@ -3582,7 +3546,7 @@ namespace Mono.CSharp { } } - public class Catch: Statement { + public class Catch : Statement { public readonly string Name; public readonly Block Block; @@ -3625,8 +3589,6 @@ namespace Mono.CSharp { type = te.ResolveType (ec); - CheckObsolete (type); - if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){ Error (155, "The type caught or thrown must be derived from System.Exception"); return false; @@ -3824,11 +3786,10 @@ namespace Mono.CSharp { public class Using : ExceptionStatement { object expression_or_block; - Statement Statement; + public Statement Statement; ArrayList var_list; Expression expr; Type expr_type; - Expression conv; Expression [] resolved_vars; Expression [] converted_vars; ExpressionStatement [] assign; @@ -3852,7 +3813,7 @@ namespace Mono.CSharp { if (texpr == null) return false; - expr_type = texpr.Type; + expr_type = texpr.ResolveType (ec); // // The type must be an IDisposable or an implicit conversion @@ -3879,11 +3840,13 @@ namespace Mono.CSharp { continue; } - converted_vars [i] = Convert.ImplicitConversionRequired ( + converted_vars [i] = Convert.ImplicitConversion ( ec, var, TypeManager.idisposable_type, loc); - if (converted_vars [i] == null) + if (converted_vars [i] == null) { + Error_IsNotConvertibleToIDisposable (); return false; + } i++; } @@ -3908,12 +3871,17 @@ namespace Mono.CSharp { return true; } + void Error_IsNotConvertibleToIDisposable () + { + Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'", + TypeManager.CSharpName (expr_type)); + } + bool ResolveExpression (EmitContext ec) { if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){ if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) { - Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to 'System.IDisposable'", - TypeManager.CSharpName (expr_type)); + Error_IsNotConvertibleToIDisposable (); return false; } } @@ -4004,10 +3972,8 @@ namespace Mono.CSharp { // ILGenerator ig = ec.ig; local_copy = ig.DeclareLocal (expr_type); - if (conv != null) - conv.Emit (ec); - else - expr.Emit (ec); + + expr.Emit (ec); ig.Emit (OpCodes.Stloc, local_copy); if (emit_finally) @@ -4140,7 +4106,11 @@ namespace Mono.CSharp { statement = stmt; loc = l; } - + + public Statement Statement { + get { return statement; } + } + public override bool Resolve (EmitContext ec) { expr = expr.Resolve (ec); @@ -4158,6 +4128,12 @@ namespace Mono.CSharp { Type var_type = texpr.Type; + if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethod) { + Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'", + expr.ExprClassName); + return false; + } + // // We need an instance variable. Not sure this is the best // way of doing this. @@ -4167,7 +4143,7 @@ namespace Mono.CSharp { // if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value || expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){ - collection.error1579 (); + collection.Error_Enumerator (); return false; } @@ -4198,92 +4174,6 @@ namespace Mono.CSharp { ec.LoopEnd = old_end; } - protected class TemporaryVariable : Expression, IMemoryLocation - { - LocalInfo li; - - public TemporaryVariable (Type type, Location loc) - { - this.type = type; - this.loc = loc; - eclass = ExprClass.Value; - } - - public override Expression DoResolve (EmitContext ec) - { - if (li != null) - return this; - - TypeExpr te = new TypeExpression (type, loc); - li = ec.CurrentBlock.AddTemporaryVariable (te, loc); - if (!li.Resolve (ec)) - return null; - - AnonymousContainer am = ec.CurrentAnonymousMethod; - if ((am != null) && am.IsIterator) - ec.CaptureVariable (li); - - return this; - } - - public override void Emit (EmitContext ec) - { - ILGenerator ig = ec.ig; - - if (li.FieldBuilder != null) { - ig.Emit (OpCodes.Ldarg_0); - ig.Emit (OpCodes.Ldfld, li.FieldBuilder); - } else { - ig.Emit (OpCodes.Ldloc, li.LocalBuilder); - } - } - - public void EmitLoadAddress (EmitContext ec) - { - ILGenerator ig = ec.ig; - - if (li.FieldBuilder != null) { - ig.Emit (OpCodes.Ldarg_0); - ig.Emit (OpCodes.Ldflda, li.FieldBuilder); - } else { - ig.Emit (OpCodes.Ldloca, li.LocalBuilder); - } - } - - public void Store (EmitContext ec, Expression right_side) - { - if (li.FieldBuilder != null) - ec.ig.Emit (OpCodes.Ldarg_0); - - right_side.Emit (ec); - if (li.FieldBuilder != null) { - ec.ig.Emit (OpCodes.Stfld, li.FieldBuilder); - } else { - ec.ig.Emit (OpCodes.Stloc, li.LocalBuilder); - } - } - - public void EmitThis (EmitContext ec) - { - if (li.FieldBuilder != null) { - ec.ig.Emit (OpCodes.Ldarg_0); - } - } - - public void EmitStore (ILGenerator ig) - { - if (li.FieldBuilder != null) - ig.Emit (OpCodes.Stfld, li.FieldBuilder); - else - ig.Emit (OpCodes.Stloc, li.LocalBuilder); - } - - public void AddressOf (EmitContext ec, AddressOp mode) - { - EmitLoadAddress (ec); - } - } - protected class ArrayCounter : TemporaryVariable { public ArrayCounter (Location loc) @@ -4352,7 +4242,7 @@ namespace Mono.CSharp { list.Add (counter [i]); } - access = new ElementAccess (copy, list, loc).Resolve (ec); + access = new ElementAccess (copy, list).Resolve (ec); if (access == null) return false; @@ -4435,6 +4325,7 @@ namespace Mono.CSharp { MethodInfo move_next; Type var_type, enumerator_type; bool is_disposable; + bool enumerator_found; public CollectionForeach (Type var_type, Expression var, Expression expr, Statement stmt, Location l) @@ -4448,20 +4339,9 @@ namespace Mono.CSharp { bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi) { - Type [] args = TypeManager.GetArgumentTypes (mi); - if (args != null){ - if (args.Length != 0) - return false; - } - - if (TypeManager.IsOverride (mi)) - return false; - - // Check whether GetEnumerator is public - if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) - return false; + Type return_type = mi.ReturnType; - if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type)) + if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type)) // // Apply the same optimization as MS: skip the GetEnumerator // returning an IEnumerator, and use the one returning a @@ -4475,8 +4355,7 @@ namespace Mono.CSharp { // with this `GetEnumerator' // - Type return_type = mi.ReturnType; - if (mi.ReturnType == TypeManager.ienumerator_type || + if (return_type == TypeManager.ienumerator_type || TypeManager.ienumerator_type.IsAssignableFrom (return_type) || (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) { // @@ -4493,9 +4372,20 @@ namespace Mono.CSharp { // way I could do this without a goto // + if (return_type.IsInterface && return_type.IsGenericType) { + enumerator_type = return_type; + if (!FetchGetCurrent (ec, return_type)) + get_current = new PropertyExpr ( + ec, TypeManager.ienumerator_getcurrent, loc); + if (!FetchMoveNext (ec, return_type)) + move_next = TypeManager.bool_movenext_void; + return true; + } + if (return_type.IsInterface || !FetchMoveNext (ec, return_type) || !FetchGetCurrent (ec, return_type)) { + enumerator_type = return_type; move_next = TypeManager.bool_movenext_void; get_current = new PropertyExpr ( ec, TypeManager.ienumerator_getcurrent, loc); @@ -4507,11 +4397,11 @@ namespace Mono.CSharp { // find if they support the GetEnumerator pattern. // - if (!FetchMoveNext (ec, return_type)) - return false; - - if (!FetchGetCurrent (ec, return_type)) + if (TypeManager.HasElementType (return_type) || !FetchMoveNext (ec, return_type) || !FetchGetCurrent (ec, return_type)) { + 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", + TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi)); return false; + } } enumerator_type = return_type; @@ -4531,11 +4421,12 @@ namespace Mono.CSharp { move_next_list = TypeContainer.FindMembers ( t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.Instance, + Expression.AllBindingFlags, Type.FilterName, "MoveNext"); if (move_next_list.Count == 0) return false; + bool found = false; foreach (MemberInfo m in move_next_list){ MethodInfo mi = (MethodInfo) m; Type [] args; @@ -4544,11 +4435,13 @@ namespace Mono.CSharp { if ((args != null) && (args.Length == 0) && TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) { move_next = mi; - return true; + if (mi.IsPublic) + return true; + found = true; } } - return false; + return found; } // @@ -4593,11 +4486,15 @@ namespace Mono.CSharp { return null; } - public void error1579 () + public void Error_Enumerator () { + if (enumerator_found) { + return; + } + Report.Error (1579, loc, - "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible", - TypeManager.CSharpName (expr.Type)); + "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible", + TypeManager.CSharpName (expr.Type)); } bool TryType (EmitContext ec, Type t) @@ -4608,11 +4505,41 @@ namespace Mono.CSharp { if (mg == null) return false; - foreach (MethodBase mb in mg.Methods) { - if (!GetEnumeratorFilter (ec, (MethodInfo) mb)) + MethodBase result = null; + MethodInfo tmp_move_next = null; + PropertyExpr tmp_get_cur = null; + Type tmp_enumerator_type = enumerator_type; + foreach (MethodInfo mi in mg.Methods) { + Type [] args = TypeManager.GetArgumentTypes (mi); + if (args != null && args.Length != 0) + continue; + + // Check whether GetEnumerator is public + if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) continue; - MethodInfo[] mi = new MethodInfo[] { (MethodInfo) mb }; + if (TypeManager.IsOverride (mi)) + continue; + + enumerator_found = true; + + if (!GetEnumeratorFilter (ec, mi)) { + continue; + } + + result = mi; + tmp_move_next = move_next; + tmp_get_cur = get_current; + tmp_enumerator_type = enumerator_type; + if (mi.DeclaringType == t) + break; + } + + if (result != null) { + move_next = tmp_move_next; + get_current = tmp_get_cur; + enumerator_type = tmp_enumerator_type; + MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result }; get_enumerator = new MethodGroupExpr (mi, loc); if (t != expr.Type) { @@ -4671,14 +4598,14 @@ namespace Mono.CSharp { is_disposable = true; if (!ProbeCollectionType (ec, expr.Type)) { - error1579 (); + Error_Enumerator (); return false; } enumerator = new TemporaryVariable (enumerator_type, loc); enumerator.Resolve (ec); - init = new Invocation (get_enumerator, new ArrayList (), loc); + init = new Invocation (get_enumerator, new ArrayList ()); init = init.Resolve (ec); if (init == null) return false; @@ -4689,7 +4616,7 @@ namespace Mono.CSharp { MethodGroupExpr mg = new MethodGroupExpr (mi, loc); mg.InstanceExpression = enumerator; - move_next_expr = new Invocation (mg, new ArrayList (), loc); + move_next_expr = new Invocation (mg, new ArrayList ()); } get_current.InstanceExpression = enumerator;