Community
Participate
Working Groups
Build ID: I20070601-1539 Steps To Reproduce: The following stripped-down code snippet fails to compile in Eclipse 3.3RC3, but compiles fine with javac 1.5 & 1.6. Error: The method compound(Iterable<? extends Comparator<? super T>>) in the type BugSample is not applicable for the arguments (List<Comparator<? extends Object>>) More information: import java.util.Comparator; import java.util.List; public class BugSample { public static <T> Comparator<T> compound( Comparator<? super T> a, Comparator<? super T> b) { return compound(asList(a, b)); } public static <T> Comparator<T> compound( Iterable<? extends Comparator<? super T>> comparators) { return null; } public static <E> List<E> asList(E a, E b) { return null; } }
Reproduced with HEAD + fixes for bug 189933 and bug 190748. ---------- 1. ERROR in D:\tests_sources\X.java (at line 9) return compound(asList(a, b)); ^^^^^^^^ The method compound(Iterable<? extends Comparator<? super T>>) in the type X is not applicable for the arguments (List<Comparator<? extends Object>>) ---------- 1 problem (1 error)
Not a regression. 3.2.2 and 3.2.0 also fail to compile this code.
Since this case does not report a problem : class BugSample { <T> Comparator<T> compound(Comparator<? super T> a, Comparator<? super T> b) { return compound(asList(a)); } <T> Comparator<T> compound(Iterable<? extends Comparator<? super T>> c) { return null; } <E> List<E> asList(E a) { return null; } } It looks like we're having problems detecting type identity/compatibility b/w the parameter types for a & b.
I disagree about this not being a regression--I had reduced my snippet too much. In the complete context in our codebase at Google, the real problematic code does compile under 3.2.0. This slightly expanded snippet compiles under 3.2.0 but not 3.3RC3: import java.util.Comparator; import java.util.List; public class BugSample { public static <T> Comparator<T> compound( Comparator<? super T> a, Comparator<? super T> b, Comparator<? super T>... rest) { return compound(asList(a, b, rest)); } public static <T> Comparator<T> compound( Iterable<? extends Comparator<? super T>> comparators) { return null; } public static <E> List<E> asList(E a, E b, E[] rest) { return null; } }
Olivier found that this new problem is caused by the fix for bug 188103
With HEAD version, leastContainingInvocation(..) returns: public interface Comparator<?> and with v_763 it returns: public interface Comparator<? super T> It seems that the order in which the invocations are processed impact the result. In v_763, the order for the invocations is: public interface Comparator<? super T> public interface Comparator<capture#1-of ? super T> public interface Comparator<capture#2-of ? super T> and in HEAD the order is: public interface Comparator<capture#1-of ? super T> public interface Comparator<capture#2-of ? super T> public interface Comparator<? super T> To prove that the process order is the problem here, I reverted the for loop to be: for (int i = invocations.length - 1; i >= 0; i--) { instead of: for (int i = 0, length = invocations.length; i < length; i++) { and then the test case compiles.
The first test case still doesn't compile even if with RC1.
I have an idea why the ordering is making a difference. Now, in theory we were weak before... the problem could have occurred before, and likely on a different VM (like Harmony) where ordering would have differed (see bug 188103). If true, then this means we had a bug, which only recently made more proeminent. The real fix is elsewhere however (and I don't have it right now).
There are likely several unrelated bugs that cause these cases to fail, but here is one that may be the cause of several failures. Start with this case which we compile fine: class SingleParameter { <T> C<T> x(C<? super T> a, C<? super T> b) { return y(z(a)); } // called with L<C<capture#1-of ? super T>> from result of z(a) <U> C<U> y(I<? extends C<? super U>> c) { return null; } // becomes L<C<capture#1-of ? super T>> z(C<capture#1-of ? super T>) <E> L<E> z(E a) { return null; } } interface C<U1> {} interface I<U2> {} interface L<U3> extends I<U3> {} But once we change it to: class TwoParameters { <T> C<T> x(C<? super T> a, C<? super T> b) { return y(z(a, b)); } // called with L<C<? extends Object>> from result of z(a, b) // L<C<? extends Object>> is NOT a match to I<? extends C<? super T>> // even though it should be <U> C<U> y(I<? extends C<? super U>> c) { return null; } // L<C<? extends Object>> z(C<? extends Object>, C<? extends Object>) <E> L<E> z(E a, E b) { return null; } } So we have a problem with this reduced case : class ReducedCase { void test() { L<C<? extends Object>> l = null; y(l); // should be acceptable but fails to match } <U> void y(I<? extends C<? super U>> c) {} }
We appear to have a problem with inferring the correct type : class ReducedCase { void test() { L<C<? extends Object>> l = null; x(l); // should be acceptable, but we fail y(l); // cannot be applied } <U> void x(I<? extends C<? super U>> c) {} void y(I<? extends C<? super Object>> c) {} }
I also think there are several issues aggregated here. I'd like to go through them carefully, and unfortunately this will likely not make it for 3.3RC4. Now, as soon as I have a patch, I would post it somewhere for you Carl to play with it. How does this sound ?
Re: comment 1. Javac tolerates the code, but I think this is a bug in their land; according to the following analysis. Need to be confirmed (I ping'ed spec lead to find out). I simplified the case to be, as the invocation of #asList(...) is not the issue here. Basically both compilers agree that in original example, the invocation of asList(a,b) returns List<Comparator<? extends Object>> [for the record, lub(Comparator<capture-1-of ? super T>,Comparator<capture-2-of ? super T>) --> Comparator<? extends Object> - as per spec). import java.util.Comparator; import java.util.List; class BugSample { void bar(List<Comparator<? extends Object>> list) { compound(list);// (1) Eclipse rejects, Javac accepts } public static <U> Comparator<U> compound(Iterable<? extends Comparator<? super U>> comparators) { return null; } } 15.12.2.7 - inference from argument types A=List<Comparator<? extends Object>> << F=Iterable<? extends Comparator<? super U>> using top of page 454: if F has the form G<..., Yk-1, ? extends U, Yk+1, ...>, x G<..., Xk-1, V, Xk+1, ...>, where V is a type expression. Then this algorithm is applied recursively to the constraint V << U i.e. V=Comparator<? extends Object> << U=Comparator<? super U> back to A << F (recursively applying A=V << F=U) A=Comparator<? extends Object> << F=Comparator<? super U> using top of page 455 if F has the form G<..., Yk-1, ? super U, Yk+1, ...>, ✣ Otherwise, no constraint is implied on Tj. DISCUSSION Here, we have A = G<? extends V> << G<? super U>. In general, we cannot conclude anything in this case. However, it is not necessarily an error. It may be that U will eventually be inferred to the null type, in which case the call may indeed be valid. Therefore, we simply refrain from placing any constraint on U. --> yields nothing 15.12.2.8 - inference using expected type (none here) --> yields nothing hence: U is inferred by default to be: Object Then need to check applicability to original arguments: Comparator<Object> compound(Iterable<? extends Comparator<? super Object>>) applicable to: (List<Comparator<? extends Object>>) i.e. using 4.10.2 [ARG-COMP] List<Comparator<? extends Object>> <: Iterable<? extends Comparator<? super Object>> i.e. Iterable<Comparator<? extends Object>> <: Iterable<? extends Comparator<? super Object>> only if arguments are contained as per 4.5.1.1 i.e. Comparator<? extends Object> <= ? extends Comparator<? super Object> This doesn't match any of the pattern described by the spec (p.55). So no containment. ===> the generic invocation is not valid. Note that you can also prove that arguments are incompatible (see line [ARG-COMP] above) by writing the following little program; and note that javac rejects it too. import java.util.*; class X { Iterable<Comparator<? extends Object>> itc1; Iterable<? extends Comparator<? super Object>> itc2 = itc1; } X.java:4: incompatible types found : java.lang.Iterable<java.util.Comparator<? extends java.lang.Object>> required: java.lang.Iterable<? extends java.util.Comparator<? super java.lang.Object>> Iterable<? extends Comparator<? super Object>> c2 = c1; Now, is this misinterpreted the spec somewhere ? Or does it smell like this is an inconsistency in Javac ?
Added GenericTypeTest#test1142-1144 to cover these scenarii.
Comment 3 is different in the sense that there is no lub of 2 different captures involved, and thus it surfaces the <capture1-of ? super T> which flows nicely in the inference from thereon. Both Javac and us agree there. Added GenericTypeTest#test1145
Onto comment 4. This one puzzles me. Adding an array type in the process of lub computation, changes completely the end result. e.g. asList(a,b) --> List<Comparator<? extends Object>> asList(a,b,rest) --> List<Comparator<? super T>> Trying to understand why. I think this is an over-simplification done in the compiler (both Javac and us), where the first bound is used where glb should be used. Hence, the ordering makes a difference. Changing the order is not going to make any true difference, as one could reorder code to create another scenario where it breaks now (I think moving the array type front would likely do it).
This, btw, I believe is the same issue as was reported before on: https://bugs.eclipse.org/bugs/show_bug.cgi?id=179902
I don't think it is the same as bug 179902, which is a bound check issue. At least on the surface, it feels a different issue.
(In reply to comment #11) Now, as soon as I have a patch, I would post it somewhere > for you Carl to play with it. How does this sound ? Sounds good, I can test any patches that you want me to.
Carl - the more I investigate it and the more I believe a compiler should reject your code. The fact we accepted it in the past wouldn't change the resolution. Basically, the 2 parameters or 2 parameters+varargs param shouldn't make any difference. As said earlier, this is being confirmed currently. This to say that if a patch becomes available, it will likely still reject your code (unless I am proven to be wrong by the spec). As I explained in comment 12, it is possible to construct a testcase showing the inconsistence in javac. Now, currently, Eclipse may accept the code I think is invalid per mistake (which is what occurred in 3.2.2). The current state is still not perfect, as I think it is still possible to fool the compiler again (by ordering the parameters differently, and this is not good). So there is still work to do on this bug, but the likely outcome is that once addressed the compiler will consistently reject such code patterns. FYI - unsure how much familiar you are with capture conversion, but if not, then read this. When you see code like this: public static <T> Comparator<T> compound( Comparator<? super T> a, Comparator<? super T> b) { return compound(asList(a, b)); } You'd think (intuitively) that the type of asList(a,b) is: List<Comparator<? super T>>. But it is not. In fact, it is List<Comparator<? extends Object>> since both 'a' and 'b' arguments got captured, i.e. the type of 'a' in reference is instead: Comparator<capture-1-of ? super T> which is equivalent to introducing a new type variable on the fly: Comparator<U> The type of 'b' is another capture (NOT the same) which is equivalent to introducing yet a new type variable on the fly: Comparator<V> So when inferring the type of asList(a,b), it is going to intersect the 2 captured types, producing (by the spec) List<Comparator<? extends Object>> I agree this is highly counter-intuitive, but you can only blame the spec here. <g>
FYI - started discussion with JLS master, this is retained in: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6573446 Javac behavior got confirmed to be invalid. Now, the idea is that a better behavior could be implemented, but the spec has first to evolve. So for the current time being, the behavior you are seeing is the right one, as defined by the spec. So technically, there is nothing to fix here. (if you disagree, pls reopen)
Added GenericTypeTest#test1142-1148
Verified for 3.4M1