Community
Participate
Working Groups
/* Attempting to run the following program compiled by the eclipse compiler results in this error: Exception in thread "main" java.lang.VerifyError: (class: CompilerBugTest, method: i signature: (Ljava/lang/Enum;)V) Incompatible object argument for function call Could not find the main class: CompilerBugTest. Program will exit. */ import java.util.HashMap; import java.util.Map; interface S { } enum A implements S { L; } public class CompilerBugTest { public static void main(String[] args) throws Exception { i(A.L); } static void i(Enum<? extends S> x) { Map m = new HashMap(); for (Enum s : x.getDeclaringClass().getEnumConstants()) m.put(s.name(), s); } }
The compiler is confused about the type of x.getDeclaringClass().getEnumConstants(); Manual analysis yields x : Enum<? extends S> x.getDeclaringClass() : Class<Enum<? extends S>> x.getDeclaringClass().getEnumConstants() : Enum<? extends S>[] However, the compiler seems to see a value of type S[]. Thus after invoking getEnumConstants() it adds a checkcast to S[] which would mean that s is of type S - where S does not have a method name(). javac correctly puts a checkcast to Enum[] instead. The bug can be worked around by changing the loop to for (Object s : x.getDeclaringClass().getEnumConstants()) { m.put(((Enum)s).name(), s); } By typing s to Object we forget the wrong typing info and the cast (Enum)s will produce the expected result.
Created attachment 137801 [details] proposed patch > The compiler is confused about the type of > x.getDeclaringClass().getEnumConstants(); Well, I was confused, too :-/. Should have looked at the declaration of class Enum<E extends Enum<E>>! Types are better described as x : Enum<? extends S,Enum<?>> x.getDeclaringClass() : Class<? extends S,Enum<?>> x.getDeclaringClass().getEnumConstants() : <? extends S,Enum<?>>[] The issue is, that the compiler finds two bounds for the leaf type of the overall expression: S and Enum<?>. From these two it incorrectly decides to use S for the checkcast, whereas Enum<?> is used during type checking. Me thinks that when computing the conversion (including the cast) the expression cannot be analyzed in isolation but the expected type is needed to decide which of two possible casts to insert. The attached little patch does exactly this: feed an expected type into computeConversion. I did run regression.GenericTypeTest and ForeachStatementTest, but exhaustive testing still needs to be done. Should also check if collections rather than arrays in the foreach cause the same confusion.
Stephan - the patch looks good to me. We need the expected return type to be passed to the conversion. This code shows that either expected return type is valid : import java.util.*; interface S {} enum A implements S { L; } class CompilerBugTest { public static void main(String[] args) throws Exception { i(A.L); } static void i(Enum<? extends S> x) { Class<? extends S> c = x.getDeclaringClass(); S[] cs = c.getEnumConstants(); // Enum[] ce = c.getEnumConstants(); // javac & eclipse expect S[] S[] cs2 = x.getDeclaringClass().getEnumConstants(); Enum[] ce2 = x.getDeclaringClass().getEnumConstants(); // allowed by both Map m = new HashMap(); // for (Enum s : cs2) // cannot convert from S to Enum // m.put(s.name(), s); for (Enum s : ce2) m.put(s.name(), s); for (Enum s : x.getDeclaringClass().getEnumConstants()) m.put(s.name(), s); } }
+1 for 3.5.1
Created attachment 139899 [details] Proposed patch and testcase This patch runs fine on the 1.5 & 1.7 VM I have, but fails on 1.6 with : [ERR]:java.lang.VerifyError: Bad type on operand stack in method X.i(Ljava/lang/Enum;)V at offset 36 at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2427) at java.lang.Class.getMethod0(Class.java:2670) at java.lang.Class.getMethod(Class.java:1603) at org.eclipse.jdt.core.tests.util.VerifyTests Olivier, Stephan - please try the new EnumTest on a 1.6 VM to see if its reproduceable for you - thx
I am working on it.
Reproduced the failure with the 1.6 VM. I also get a failure with a JDK7 VM if I set the vm arguments to be -XX:-FailOverToOldVerifier -Xverify:all. The type of the secret local for the array also needs to be updated. I'll update the patch and make sure that the test also fails with a JDK7 VM.
Created attachment 139927 [details] Proposed fix + regression tests Fix the type of the local for the secret array.
Kent, please review.
Comment on attachment 139927 [details] Proposed fix + regression tests This was not good enough. Some problem with boxing/unboxing.
Created attachment 139933 [details] Proposed fix + regression tests This patch is passing all existing tests + the new regression tests.
Released for 3.5.1 and 3.6M1. Added regression tests: HEAD org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test174 org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test175 3.5 maintenance org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test173 org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test174
Verified for 3.6M1 using I20090803-1800
Verified for 3.5.1RC2 using M20090826-1100