Community
Participate
Working Groups
Hi. Found another bug in generics, this time combined with varargs: public class Test { public static void main (String[] args) { audit("osvaldo", "localhost", "logged", "X", new Integer(0)); } public static <A extends Object> void audit ( String login, String address, String event, A... args) {} } If yo build this code with 3.1RC1, it compiles the code, but the resulting .class does not run in JVMs because it's invalid. Decompiling with javap shows: --------------------------------------------- javap -c -s -verbose Test Compiled from "Test.java" public class Test extends java.lang.Object SourceFile: "Test.java" minor version: 0 major version: 49 Constant pool: const #1 = Asciz Test; const #2 = class #1; // Test const #3 = Asciz java/lang/Object; const #4 = class #3; // java/lang/Object const #5 = Asciz <init>; const #6 = Asciz ()V; const #7 = Asciz Code; const #8 = NameAndType #5:#6;// "<init>":()V const #9 = Method #4.#8; // java/lang/Object."<init>":()V const #10 = Asciz LineNumberTable; const #11 = Asciz LocalVariableTable; const #12 = Asciz this; const #13 = Asciz LTest;; const #14 = Asciz main; const #15 = Asciz ([Ljava/lang/String;)V; const #16 = Asciz osvaldo; const #17 = String #16; // osvaldo const #18 = Asciz localhost; const #19 = String #18; // localhost const #20 = Asciz logged; const #21 = String #20; // logged const #22 = Asciz ; const #23 = class #22; // "" const #24 = Asciz X; const #25 = String #24; // X const #26 = Asciz java/lang/Integer; const #27 = class #26; // java/lang/Integer const #28 = Asciz (I)V; const #29 = NameAndType #5:#28;// "<init>":(I)V const #30 = Method #27.#29; // java/lang/Integer."<init>":(I)V const #31 = Asciz audit; const #32 = Asciz (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V; const #33 = NameAndType #31:#32;// audit:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V const #34 = Method #2.#33; // Test.audit:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V const #35 = Asciz args; const #36 = Asciz [Ljava/lang/String;; const #37 = Asciz Signature; const #38 = Asciz <A:Ljava/lang/Object;>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[TA;)V; const #39 = Asciz login; const #40 = Asciz Ljava/lang/String;; const #41 = Asciz address; const #42 = Asciz event; const #43 = Asciz [Ljava/lang/Object;; const #44 = Asciz SourceFile; const #45 = Asciz Test.java; { public Test(); Signature: ()V Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #9; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; public static void main(java.lang.String[]); Signature: ([Ljava/lang/String;)V Code: Stack=9, Locals=1, Args_size=1 0: ldc #17; //String osvaldo 2: ldc #19; //String localhost 4: ldc #21; //String logged 6: iconst_2 7: anewarray #23; //class "" 10: dup 11: iconst_0 12: ldc #25; //String X 14: aastore 15: dup 16: iconst_1 17: new #27; //class java/lang/Integer 20: dup 21: iconst_0 22: invokespecial #30; //Method java/lang/Integer."<init>":(I)V 25: aastore 26: invokestatic #34; //Method audit:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V 29: return LineNumberTable: line 5: 0 line 6: 29 LocalVariableTable: Start Length Slot Name Signature 0 30 0 args [Ljava/lang/String; public static void audit(java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]); Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V Signature: length = 0x2 00 26 Code: Stack=0, Locals=4, Args_size=4 0: return LineNumberTable: line 11: 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 login Ljava/lang/String; 0 1 1 address Ljava/lang/String; 0 1 2 event Ljava/lang/String; 0 1 3 args [Ljava/lang/Object; } --------------------------------------------- Notice the problem here: 7: anewarray #23; //class "" The array that the compiler generates to wrap the variable arguments has an invalid class "". It seems that the compiler got confused by my declaration of varargs of type <T extends Object>. In this case, Sun JDK 5.0_03's javac does the right thing, producing a Serializable[] since this is the best possible T that is compatible with both String and Integer.
FYI, I just re-tested with I20050609-1228, bug is still there. Thre is another important detail: the bug depends on the types of all variable arguments in some odd way. For example, audit("osvaldo", "localhost", "logged", "X", "Y"); audit("osvaldo", "localhost", "logged", new Float(0), new java.awt.Point(0)); will both compile correctly. The first call is OK, maybe just because all variable arguments are the same type. But in the second call, there are args of different types, and the compiler works too. The suspect cause is that the compilers is confused only when the vararg list begins with an argument that has the same type as the last non-variable argument. This is the case of all places in my app code where this bug happened, and it surely explains why the bug is very rare, and may hint at the root cause: perhaps some problem in disambiguation of fixed vs. variable arguments, plus the generics factor...
Created attachment 22762 [details] Apply on HEAD Proposed fix. Philippe, please verify the part with Scope.greaterLowerBound(bounds)[0]. I get a code gen similar to what javac is doing. And it runs fine.
Created attachment 22763 [details] Apply on HEAD Regression test
Created attachment 22764 [details] Apply on HEAD New patch that is using locals to acces bounds.
I suspect this is not the proper fix. The wildcard represents here an intersection type, and the glb there is ignoring the first bound which could carry useful information. I believe, it should rather simply use the erasure when generating arguments. We would result in constructing an Object[] here, which is fine as it is all the method is expecting anyway.
My fix would rather be to use the expected type from codegen binding varargs type, i.e. in Statement#generateArguments, introduce 'codeGenVarArgsType' and used it for #newArray(...) invocations. ArrayBinding varArgsType = (ArrayBinding) params[varArgIndex]; // parameterType has to be an array type ArrayBinding codeGenVarArgsType = (ArrayBinding) binding.original().parameters[varArgIndex].erasure();
Released VarargsTest#test30 (from patch). Fixed in latest.
Just wondering... there is one subtle aspect of ths bug that you may want to consider as a separate bug: the fact that the compiler's classfile assembling phase agreed to write a .class with an invalid entry in the constant pool - const #22 = Asciz ; const #23 = class #22; // "" AFAIK a class named "" is not possible in any circumstances, not even for synthetic or obfuscated code. If the code that writes the classfile would validate this, it would flag the problem instead of covering bugs from the front-end compiler and silently producing a invalid, unverifiable classfile.
Our infrastructure relies on the compiler to behave. Checking for this rare situation to occur would penalize normal compilation. Though I agree it would help finding bugs in general.
I see. Perhaps a good case for asserts, since Eclipse 3.0+ requires J2SE 1.4+? When reporting bugs like this one and also bug 97440, it takes a reasonable effort to produce a good bug report including test code that's simple, easy to reproduce, and doesn't carry much code that cannot be disclosed publicly... so it'd be nice if I could run Eclipse with -vmargs:ea, even if 10X slower, and get some clues.
Verify in I20050610-1200.
Closing.
*** Bug 108497 has been marked as a duplicate of this bug. ***