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() {
m();
}
}
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 15.12.2.7 (inference from
method arguments) aren't propagated into 15.12.2.8 (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!
[1] http://bugs.sun.com/view_bug.do?bug_id=6369605
[2] http://bugs.sun.com/view_bug.do?bug_id=6650759
[3] http://www.cs.rice.edu/%7Edlsmith/java-type-system-oopsla08.pdf
Maurizio
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 15.12.2.8) 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 Bug.java; 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
>>
>> Bug.java; 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 ----
>> Bug.java:15: 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