SunのコンパイラとEclipse JDTで動きが違う(4)

型推論の正解が分からない - しげるメモからの派生。

型推論で出現する無限型の取り扱いについて、SunのjavacとEclipseのJDTで微妙に動きが違う。
先ほどのエントリで、 method(T, T)にInteger, Longを渡すと、TはR = Number & Comparable<? extends R>となるような型Rに推論されると予測してます。それを踏まえて、現行のコンパイラがどうやって無限型を取り扱ってるのかなーと調査中。

void test() {
    Integer a = 1;
    Long b = 1L;
    bound0(a, b);
    bound1(a, b);
    bound2(a, b);
    bound3(a, b);
    infinite(a, b);
}
<T>
    void bound0(T a, T b) {} // 境界なし
<T extends Number & Comparable<?>>
    void bound1(T a, T b) {} // Comparableが1回
<T extends Number & Comparable<? extends Comparable<?>>>
    void bound2(T a, T b) {} // Comparableが2回
<T extends Number & Comparable<? extends Comparable<? extends Comparable<?>>>>
    void bound3(T a, T b) {} // Comparableが3回
<T extends Number & Comparable<? extends T>>
    void infinite(T a, T b) {} // 無限型のエミュレーション

で、javacとJDTでそれぞれこうなりました。

メソッド javac JDT
bound0 OK OK
bound1 OK OK
bound2 OK ERROR
bound3 ERROR ERROR
infinite ERROR ERROR

bound2 (Number & Comparable<? extends Comparable<?>>)あたりが境目のようです。

JDTは、R = Number & Comparable<? extends R>を

Number & Comparable<?>

として推論してしまっているようです。

javacのほうは

Number & Comparable<? extends Number & Comparable<?>>

として推論しているように見えます*1。ためしに、下のようなメソッドを作ってみたらjavacでコンパイルできました。

<A extends Number & Comparable<?>
,T extends Number & Comparable<? extends A>>
        void boundForJavac(T a, T b) {}

上記はJDTではコンパイルエラーになります。

なお、言語仕様にはこんな記述が。

It is possible that the process above yields an infinite type. This is permissible, and Java compilers must recognize such situations and represent them appropriately using cyclic data structures.

15.12.2.7 Inferring Type Arguments Based on Actual Arguments

この記述に準拠するためにここ2日くらいずっと考えてるわけですが。
本家でもちゃんとやってないので多少気が楽になった。

*1:本当は上記のようには書けない