Bugzilla – Bug 322941
[GMCS] incorrect compilation of foreach
Last modified: 2007-09-15 21:24:23 UTC
---- Reported by lupus@ximian.com 2006-12-13 08:27:29 MST ---- Given the following test: using System; using System.Collections; using System.Collections.Generic; class MyEnum : IEnumerator { int count = 0; public object Current { get {return "";} } public bool MoveNext () { if (count > 0) return false; count++; return true; } public void Reset () { } } class MyDict : IEnumerable<KeyValuePair<object,object>> { IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object, object>>.GetEnumerator() { KeyValuePair<object, object> o = new KeyValuePair<object, object> ("hello", "world"); yield return o; } public IEnumerator GetEnumerator() { return new MyEnum (); } } class T { static void Main () { MyDict dict = new MyDict (); foreach (KeyValuePair<object, object> o in dict) { Console.WriteLine ("{0} {1}", o.Key, o.Value); } } } Compiling with gmcs yields a cast exception. This is because gmcs calls the non-generic GetEnumerator() instead of the proper generic implementation that returns KeyValuePair<object, object values. This is a reduced test case from ironpython code. This bug makes a gmcs-compiled ironpython (1.1) unusable. ---- Additional Comments From miguel@ximian.com 2006-12-13 12:32:52 MST ---- Am going to CC Marek and Martin for good measure, as he has been looking at some of these bits recently. Marek, do you know what is going on? ---- Additional Comments From marek.safar@seznam.cz 2006-12-13 15:34:39 MST ---- Here is little bit simplified version using System; using System.Collections; using System.Collections.Generic; class MyDict : IEnumerable<KeyValuePair<object,object>> { IEnumerator<KeyValuePair<object, object>> IEnumerable<KeyValuePair<object,object>>.GetEnumerator() { KeyValuePair<object, object> o = new KeyValuePair<object, object>("hello", "world"); yield return o; } public IEnumerator GetEnumerator() { throw new NotImplementedException(); } } class T { static void Main () { MyDict dict = new MyDict (); foreach (KeyValuePair<object, object> o in dict) { Console.WriteLine ("{0} {1}", o.Key, o.Value); } } } ---- Additional Comments From marek.safar@seznam.cz 2006-12-13 15:37:11 MST ---- Unfortunately it looks like not a bug because when you try to compile the code with csc you will get exactly same result in the both cases. InvalidCast or NotImplemented So, we can fix it but that we will be incompatible with csc. ---- Additional Comments From lupus@ximian.com 2006-12-13 15:51:39 MST ---- Marek, thanks for checking. I reduced the test from similar code in ironpython. Please download version 1.1 alpha 1 from http://www.codeplex.com/IronPython. Compile with gmcs (in Src make CSC=gmcs, change mkdir to mkdir -p in the makefile). Run mono ipy.exe and get the crash. Take the Bin package and it will work. The issue is in the IronPython.Runtime.Types.DynamicType.GetAttrNames method. The sample code matches that method and the generated code in the csc-compiled bin calls the generic method. ---- Additional Comments From rhowell@novell.com 2006-12-13 20:59:00 MST ---- Does this bug have anything to do with https://bugzilla.novell.com/show_bug.cgi?id=MONO79561 ? They both deal with IEnumerable, Generics, and foreach loops. ---- Additional Comments From rharinath@novell.com 2006-12-13 23:27:19 MST ---- IIRC, 'foreach' prefers direct calls to GetEnumerator rather than going through the IEnumerable interface. ---- Additional Comments From rharinath@novell.com 2006-12-13 23:47:51 MST ---- ... and after a quick scan of the IronPython source code, I think the testcase above doesn't reflect the code (and we seem to be doing the right thing wrt the testcase). I think the operative declaration is public IAttributesDictionary dict; so, there may be some other issue. Perhaps we are checking 'is IEnumerable' too early somewhere. ---- Additional Comments From lupus@ximian.com 2006-12-14 07:16:46 MST ---- *** https://bugzilla.novell.com/show_bug.cgi?id=MONO80259 has been marked as a duplicate of this bug. *** ---- Additional Comments From miguel@ximian.com 2007-01-10 01:05:51 MST ---- Hello, This seems to have regressed. Now instead of an exception, this is what I get from running ipy.exe: ** ERROR **: file class.c: line 574 (mono_generic_class_get_context): assertion failed: (context->class_inst == gclass->inst) aborting... Stacktrace: at IronPython.Runtime.SystemState.Initialize () <0xffffffff> at IronPython.Runtime.SystemState.Initialize () <0x00043> at IronPython.Runtime.SystemState..ctor (IronPython.Hosting.EngineOptions) <0x00013> at IronPython.Hosting.PythonEngine.Initialize (IronPython.Hosting.EngineOptions) <0x00040> at IronPython.Hosting.PythonEngine..ctor (IronPython.Hosting.EngineOptions) <0x00025> at IronPythonConsole.PythonCommandLine.Main (string[]) <0x00088> at (wrapper runtime-invoke) System.Object.runtime_invoke_int_string[] (object,intptr,intptr,intptr) <0xffffffff> Paolo's original test case produces an InvalidCastException on both gmcs and .NET 2.0. Marek's reduced test case produces a NotImplementedException on both gmcs and .NET 2.0. But the new IronPython alpha still fails to start up. The issue is in the initialization of the EmptyTypeOps`1 class: #5 0xb7f37286 in g_assert_warning () from /opt/gnome/lib/libglib-2.0.so.0 (gdb) up #6 0x080f41f0 in mono_generic_class_get_context (gclass=0x81eac40) at class.c:574 574 g_assert (context->class_inst == gclass->inst); (gdb) #7 0x080fa051 in mono_class_init (class=0x8333850) at class.c:2414 2414 gklass->methods [i], class, mono_generic_class_get_context ((MonoGenericClass *) gclass)); (gdb) p *class $4 = {image = 0x826a480, enum_basetype = 0x0, element_class = 0x8333850, cast_class = 0x8333850, rank = 0 '\0', inited = 0, init_pending = 1, size_inited = 0, valuetype = 0, enumtype = 0, blittable = 0, unicode = 0, wastypebuilder = 0, min_align = 0, packing_size = 0, ghcimpl = 0, has_finalize = 0, marshalbyref = 0, contextbound = 0, delegate = 0, gc_descr_inited = 0, has_cctor = 0, has_references = 0, has_static_refs = 0, no_special_static_fields = 0, is_com_object = 0, exception_type = 0 '\0', exception_data = 0x0, declsec_flags = 0, parent = 0x8217d14, nested_in = 0x0, nested_classes = 0x0, type_token = 33555017, name = 0xb734b8ac "EmptyTypeOps`1", name_space = 0xb7349322 "IronPython.Runtime.Types", supertypes = 0x835a6ec, idepth = 2, interface_count = 0, interface_id = 0, max_interface_id = 0, interface_offsets = 0x0, interfaces = 0x0, instance_size = 0, vtable_size = 0, sizes = {class_size = 0, element_size = 0}, flags = 1048704, field = {first = 2364, count = 4}, method = { first = 7599, count = 14}, property = {first = 0, count = 0}, event = {first = 0, count = 0}, marshal_info = 0x0, fields = 0x0, properties = 0x0, events = 0x0, methods = 0x8333940, this_arg = {data = {klass = 0x83336c8, type = 0x83336c8, array = 0x83336c8, method = 0x83336c8, generic_param = 0x83336c8, generic_class = 0x83336c8}, attrs = 0, type = 21, num_mods = 0, byref = 1, pinned = 0, modifiers = 0x83338e8}, byval_arg = {data = {klass = 0x83336c8, type = 0x83336c8, array = 0x83336c8, method = 0x83336c8, generic_param = 0x83336c8, generic_class = 0x83336c8}, attrs = 0, type = 21, num_mods = 0, byref = 0, pinned = 0, modifiers = 0x83338f0}, generic_class = 0x83336c8, generic_container = 0x0, reflection_info = 0x0, gc_descr = 0x0, runtime_info = 0x0, vtable = 0x0} It seems like an area that Hari has been recently looking at. ---- Additional Comments From marek.safar@seznam.cz 2007-01-10 06:27:06 MST ---- Yes, none of the test cases are the right one. That's why Paulo recommended to download full IP and try to compile it to find what is the real problem. ---- Additional Comments From rharinath@novell.com 2007-01-11 08:20:16 MST ---- Should be fixed in SVN r70850. Unknown operating system unknown. Setting to default OS "Other".