Community
Participate
Working Groups
Consider this toy example: import java.util.Map; class GenericType <E extends Object & Comparable<E> & Map.Entry<String, E>> { public void doSomething(E e) { System.out.println(e.compareTo(e.getValue())); } } class ConcreteType { public void doSomething(Object obj) { System.out.println(((Comparable)obj).compareTo(((Map.Entry)obj).getValue())); } } public class Foo { public static void main(String[] args) { new GenericType().doSomething("a1"); new ConcreteType().doSomething("aa"); } } The purpose of this test was to understand the byte code generated for the first doSomething method, which we imagine must look just like the byte code for the second doSomething method. But looking at the byte code, that wasn't the case. So then we tried to run the example, expecting to get a class cast exception for both. But instead we get this: Exception in thread "main" java.lang.IncompatibleClassChangeError at GenericType.doSomething(Foo.java:7) at Foo.main(Foo.java:23) When we compile with Sun javac, we get class cast exceptions as expected. This leads me to conclude that the CHECKCAST code is not generated properly by the Eclipse compiler.
Indeed, our code produces: [IncompatibleClassChangeError:1][ClassCastException:2] where javac's produces: [ClassCastException:1][ClassCastException:2] Suspect a generic cast optimization which is proving wrong here. -------- import java.util.Map; class GenericType<E extends Object & Comparable<E> & Map.Entry<String, E>> { public void doSomething(E e) { System.out.println(e.compareTo(e.getValue())); } } class ConcreteType { public void doSomething(Object obj) { System.out.println(((Comparable) obj).compareTo(((Map.Entry) obj).getValue())); } } public class X { public static void main(String[] args) { try { new GenericType().doSomething("a1"); } catch(Throwable e) { System.out.print("[" + e.getClass().getSimpleName() + ":1]"); } try { new ConcreteType().doSomething("a2"); } catch(Throwable e) { System.out.print("[" + e.getClass().getSimpleName() + ":2]"); } } }
GenericType#doSomething() eclipse===================== // Method descriptor #17 (Ljava/lang/Object;)V // Signature: (TE;)V // Stack: 3, Locals: 2 public void doSomething(Object e); 0 getstatic System.out : PrintStream [20] 3 aload_1 [e] 4 aload_1 [e] 5 invokeinterface Map$Entry.getValue() : Object [26] [nargs: 1] 10 invokeinterface Comparable.compareTo(Object) : int [32] [nargs: 2] 15 invokevirtual PrintStream.println(int) : void [38] 18 return javac======================= // Method descriptor #15 (Ljava/lang/Object;)V // Signature: (TE;)V // Stack: 3, Locals: 2 public void doSomething(Object arg0); 0 getstatic System.out : PrintStream [2] 3 aload_1 4 checkcast Comparable [3] 7 aload_1 8 checkcast Map$Entry [4] 11 invokeinterface Map$Entry.getValue() : Object [5] [nargs: 1] 16 invokeinterface Comparable.compareTo(Object) : int [6] [nargs: 2] 21 invokevirtual PrintStream.println(int) : void [7] 24 return
also see bug 141289
Our support for inserted generic cast on multi-bound scenario is only dealing with fieldref/methodref receivers. Extra cast should be on codegen of msgSend, when rcv type doesn't match expectation (secondary bound issue), whatever the receiver expression may be (not just field/method). Should construct testcase with ternary or local or cast expression etc...
Created attachment 52900 [details] Proposed patch
Added GenericTypeTest#test1057 Released for 3.3M3. Fixed
Verified for 3.3 M3 using build I20061030-0010