Change in javac generics type inference

Maurizio Cimadamore Maurizio.Cimadamore at Sun.COM
Fri Feb 6 02:02:54 PST 2009

Hi Martin
I confirm you that you ran into a fatal combo of both [1] and [2]. The 
problem is that it's not clear from the JLS what type should be inferred 
for K in the following program:

class Test<X> {

 <K extends Test<K>> void m() {}

 void test() {

On the one hand, JLS rules imply that K should be inferred as Test<K> - 
but this is wrong as inferred types should not contain type variable 
that should have been inferred! A paper has been writen on this topic 
[3]. The solution that (for now) is implemented in both javac and 
Eclipse is to simply leave K as uninferred - thus inferring Object for 
K; but this causes an attribution failure, as Object is not within K's 
bound ([K:=Object]Test<K> = Test<Object>). I came up with an idea for 
making things work in such corner cases - but, as I said, that will 
require a JLS change.

Back to your original example, it's not entirely true that javac doesn't 
have enough info to infer a type for Key<K>, since E (whose bound is 
Entrity<E, K>) has been already inferred to EntityImpl which implements 
Entity<EntityImpl, KeyImpl>, which implies that K = KeyImpl. But, 
unfortunately, constraints derived from JLS3 (inference from 
method arguments) aren't propagated into (inference from 
method return type). This means that the useful constraint K = KeyImpl 
is never exploited by javac, which in this case is silly - see [2]... 
which requires another JLS change!



Maurizio Cimadamore wrote:
> Hi Martin
> thanks for the report - this looks like a javac bug - I've been able 
> to reproduce it with the following test case:
> class Test<X> {
>  <K extends Test<K>> void m() {}
>  void test() {
>    m();
>  }
> }
> It seems like this bug has been there for a while - I see you are 
> using jdk7 b05 and that openjdk6 is also affected, which means that 
> the bug got in *before* the openjdk6/jdk7 split. At a first sight it 
> looks vaguely as related to/duplicate of 6369605 , as the problem 
> involves an uninferred type-variable with a recursive bound - even 
> when this program used to compie neither javac nor Eclipse used to get 
> this right (kinda failed silently); perhaps, after some fix, the 
> problem has become more evident. My current fix for 6369605 seems to 
> solve the problem, which is a good thing. Bad news is that my fix 
> requires a non trivial spec change (JLS3 and we'll need to 
> go through CCC for fixing that, I'll definitively look into that.
> Thanks again
> Maurizio
> Martin Buchholz wrote:
>> Hi javac maintainers,
>> We have a program that started to fail to compile as of jdk7-b05
>> (openjdk6 is naturally also affected)
>> I'm not an expert on generics type inference,
>> but the code seems reasonable, and used to compile,
>> so....bug?
>>  $ cat; for java in jdk1.7.0-b04 jdk1.7.0-b05; do echo ---
>> $java ---- ; 
>> /home/build/static/projects_norep/java/sun-jdk/linux-i586/$java/bin/javac 
>>; done
>> public class Bug {
>>   public static <K extends Key<K>, E extends Entity<E, K>> void get(E 
>> entity) {}
>>   public interface Entity<E extends Entity<E, K>, K extends Key<K>> {}
>>   public interface Key<K extends Key<K>> {}
>>   public static class EntityImpl implements Entity<EntityImpl, 
>> KeyImpl> {}
>>   public static class KeyImpl implements Key<KeyImpl> {}
>>   public static void main(String[] args) {
>>     EntityImpl e = new EntityImpl();
>>     get(e);
>>   }
>> }
>> --- jdk1.7.0-b04 ----
>> --- jdk1.7.0-b05 ----
>> incompatible types; inferred type argument(s)
>> java.lang.Object do not conform to bounds of type variable(s) K
>> found   : <K>void
>> required: void
>>     get(e);
>>        ^
>> 1 error

More information about the compiler-dev mailing list