Java 8 compiler bug?

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu Nov 17 12:44:00 UTC 2016



On 15/11/16 23:46, Oliver Gierke wrote:
> CriteriaBuilder and all other types come from JPA. The method in question has two overloads:
>
> <E, C extends Collection<E>> Predicate isNotMember(Expression<E> elem, Expression<C> collection);
> <E, C extends Collection<E>> Predicate isNotMember(E elem, Expression<C> collection);
>
> I fail to understand why the second method is even considered as that create a contradiction between the bound values of E (Expression<…>) and C (Collection<Object>).
Hi Oliver,
I believe the JDK 8 behavior is correct here - let's just consider your 
second method - which is the one you have doubts about:

<E, C extends Collection<E>> Predicate isNotMember(E elem, Expression<C> collection);


Now, you want to pass the following actual argument types to this method:

arg1 = Expression<Object>
arg2 = Expression<Collection<Object>>

So, let's look at which constraints these argument cause trigger on our 
inference variables E and C.

So, since Expression<Object> must be compatible with E - we have that

Expression<Object> <: E

Then, since also Expression<Collection<Object>> must be compatible with 
Expression<C> we have that:

Expression<Collection<Object>> <: C

We also have another source of constraint - namely the declared bound:

C <: Collection<E>

Now, from these constraints, (and this is the step that is new in Java 8 
and newer), we can do _incorporation_ meaning we can derive more 
constraints by looking at useful patterns. There's one notable pattern here:

Expression<Collection<Object>> <: C
C <: Collection<E>

By transitivity, this should imply that:

Expression<Collection<Object>> <: Collection<E>

which would then derive an additional constraint:

E = Object.

So, what you end up inferring is E = Object and C = Collection<Object>, 
and this inference is possible thanks to the transitivity treatment that 
is guaranteed by the incorporation step that is new in the Java 8 
specifications (see section 18.3).

Java 7 did not do anything special to enforce transitivity (which often 
result in far less precise inference results and spurious errors) - that 
meant that the only constraints the compiler had in 7 were:

Expression<Object> <: E
C <: Collection<E>

 From which you infer E = Expression<Object> and C = Collection<Object> 
which is an answer incompatible with declared bounds - so Java 7 was 
able to discard your second method as not applicable. But it was doing 
so only because the Java 7 inference machinery was not able to find an 
answer to the inference problem.

Maurizio


More information about the compiler-dev mailing list