JDK-8073129 : The result of lub(C, C) is incorrect
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u65,7,8,9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2015-02-13
  • Updated: 2024-01-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_minorUnresolved
Related Reports
Relates :  
Description
Let's consider following example:

    class MyList<T> {
        T add(T v) {return null;}
        T get(int i) {return null;};
    }

    class ClassA { }
    class ClassA1 extends ClassA { }

    public class Test62 {
        public static <T> MyList<T> foo(MyList<? extends T> list1, MyList<? extends T> list2) {
            return null;
        }

        public static void main(String argv[]) {
            foo(new MyList<MyList<ClassA1>>(), new MyList<MyList<? super ClassA>>()).get(0).add(new ClassA1());
        }
    }

it should compile, but actually it fails on JDK9b47 with following error:

    D:\langworks\smalltests2\src\Test62.java:15: error: incompatible types: ClassA1 cannot be converted to CAP#1
            foo(new MyList<MyList<ClassA1>>(), new MyList<MyList<? super ClassA>>()).get(0).add(new ClassA1());
                                                                                                ^
      where CAP#1 is a fresh type-variable:
        CAP#1 extends Object from capture of ? extends Object
    Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output

According to my understanding it should compile because:
1. As a result of reduction during type inference following bounds are created:

    MyList<ClassA1> :< T, MyList<? super ClassA> :< T

2. As a result of resolution T is instantiated as:

    lub(MyList<ClassA1>, MyList<? super ClassA>)

3. The result of this lub should be:

    MyList<lcta(ClassA1, ? super ClassA)>

4. jls-4.10.4-210-D.6-C states:

    lcta(U, ? super V) = ? super glb(U, V)  (jls-4.10.4-210-D.6-C)

thus:

    T =
    MyList<lcta(ClassA1, ? super ClassA)> =
    MyList<? super glb(ClassA1, ClassA)> =
    MyList<? super ClassA1>

5. For this reason the type of foo(...) invocation is:

    capture of MyList<MyList<? super ClassA1>>

6. The type of invocation of ...get(0) is:

    capture of MyList<? super ClassA1> is MyList<CAP#1> where
    ClassA1 :< CAP#1 
   

7. Thus the formal parameter type when invoking ...add(...) is:

    CAP#1, where ClassA1 :< CAP#1 

8. ClassA1 is a subtype of #CAP1 for this reason passing new ClassA1() as actual argument should be Ok.
Comments
What is the fate of this bug?
11-01-2024

Expected behavior in this specific case is well-defined, but JDK-5052943 is related, identifying a broader area of confusion about specified behavior of lub.
02-06-2016

Specifically, it appears that, for javac's implementation: lub(MyList<ClassA1>, MyList<? super ClassA>) = MyList<? extends Object> Per JLS, lub(MyList<ClassA1>, MyList<? super ClassA>) = MyList<? super ClassA1> I can't find any javac versions with different behavior, so this is a longstanding issue.
10-12-2015

ILW prioritization: Impact: high -- breaks specification Liklihood: low -- typical use cases make unusually heavy use of nested parameterized types Workaround: low -- user can add an explicit type parameter or cast to help inference HLL = P4
01-09-2015

Evaluation: agreed, a javac bug. Something's going wrong in the way the lub or glb is computed, because it compiles if the return type of 'foo' is declared as 'MyList<MyList<? super ClassA1>>'.
01-09-2015