JDK-6315770 : javac inference allows creation of strange types: Integer & Runnable
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 5.0,6u10
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,linux
  • CPU: generic,x86
  • Submitted: 2005-08-25
  • Updated: 2012-01-13
  • Resolved: 2012-01-13
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.
JDK 7
7 b48Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
This program should not compile:

class Test<X extends Integer & Runnable> {
    private X x = null;
    public X getX() { return x; }
    public static <T extends Integer & Runnable> Test<T> factory() { return new Test<T>(); }
    public String toString() { return "Test("+x+")"; }
    public static void main(String[] args) {
        Test<?> t = Test.factory();
        Integer i = t.getX();
        Runnable r = t.getX();
        System.out.println("Peter won't be happy: "+t);
    }
}

The call to Test.factory should infer Object for T
and thus fail.  Object is not a subtype of Integer nor
Runnable.
However I discovered that javac is really sensitive to little differences in the source code; consider the following source:

class Test<V> {
	<T extends Integer & Runnable> Test<T> m() {
        return null;
    }
    void test() {
        Test<?> c1 = m(); //1 - should compile
        Test<? extends String> c2 = m(); //2 - shouldn't compile
        Test<? super String> c3 = m(); //3 - shoudln't compile
    }
}

Surprisingly, javac rejects (1), which is wrong and, worse, diagnostics for both (2) and (3) reveal that something weird is going on:

Test3.java:6: incompatible types; inferred type argument(s) java.lang.Object do not conform to bounds of type variable(s) T
found   : <T>Test<T>
required: Test<?>
        Test<?> c1 = m();
                      ^
Test3.java:7: incompatible types; inferred type argument(s) java.lang.String do not conform to bounds of type variable(s) T
found   : <T>Test<T>
required: Test<? extends java.lang.String>
        Test<? extends String> c2 = m(); //2 - shoudln't compile
                                     ^
Test3.java:8: incompatible types; inferred type argument(s) java.lang.Object do not conform to bounds of type variable(s) T
found   : <T>Test<T>
required: Test<? super java.lang.String>
        Test<? super String> c3 = m(); //3 - shoudln't compile
                                   ^
3 errors

The inferred types are all wrong:

(1) Object instead of Integer&Runnable
(2) String instead of error (glb(String,Integer,Runnable)
(3) Object instead of Integer&Runnable

Comments
SUGGESTED FIX A webrev of this fix is avaiable at the following URL http://hg.openjdk.java.net/jdk7/tl/langtools/rev/1aa81917016a
29-01-2009

EVALUATION While the original code didn't contain bugs (see above evaluations) - desription #2 is definitively a bug. The problem involves subtyping and containment between UndetVar types (type-variable types used during type-inference) and wildcard types - in particular when javac has to perform the following containment test: U <: ?, where U is an UndetVar type the undetvar's inferred type is set to the wildcard's upper bound; this is wrong for at least two reasons: *) the wildcard's upper bound should be added to the list of the undetvar's upper bounds *) if the wildcard is of the kind ? super W, no additional lower bound is set on the undetvar Because of these problems, javac sometimes generates constraints that are not mandated by JLS3 15.12.2.8 (e.g. T == Object in (1) and (3) and T == String in (2)). The solution is to add the wildcard upper/lower bound to the undetvar upper/lower bound; when doing so javac should also check that any additional upper/lower bound is consistent with already existing lower/upper bounds.
22-01-2009

EVALUATION There is no reason to fail compilation of this program. Whatever T is inferred as, the LHS of Test<?> t = Test.factory(); can handle it. The LHS simply requires that T is constrained to be a subtype of Object, in addition to a subtype of T's own bounds. If the LHS had Test<? extends String> then of course nothing could be inferred for T that is a subtype of both String and Integer. To be specific about the submitted code, the assignment induces three constraints of the form A >> F: Test<?> >> Test<T> Integer >> T Runnable >> T For the first, JLS 15.12.2.8 doesn't say what to do if the form of A has an unbounded wildcard. I bet javac is treating Test<?> as Test<? extends Object> and hence inferring Object >> T. This becomes T <: Object, so overall we have: T <: Object T <: Integer T <: Runnable and T should be inferred as glb(Integer, Runnable) as per the first Evaluation.
22-01-2009

EVALUATION I think the program should compile - 15.12.2.8 says that the following set of contraints should be derived from T's declared bounds: T <: Integer T <: Runnable for this reason T would be inferred to glb(Integer, Runnable) which is Integer & Runnable which is also compliant w.r.t. T's declared bound.
21-01-2009