unchecked conversion with self-referencing type parameter

Dan Smith daniel.smith at oracle.com
Fri Jan 31 13:39:18 PST 2014


See this javac bug:

https://bugs.openjdk.java.net/browse/JDK-8026527

Per JLS, the program should not compile.

On Jan 30, 2014, at 4:29 PM, Rafkind, Jon <jon.rafkind at hp.com> wrote:

> On 01/30/2014 12:20 PM, Rafkind, Jon wrote:
>> In the following code javac warns that unchecked conversion was needed,
>> but my own type inference engine doesn't find a case where unchecked
>> conversion is possible.
>> 
>> public class C<X>{
>>    public <T extends C<T>> void foo(Class<T> c){
>>    }  
>> 
>>    public void test(){
>>        Class<C> c = null;
>>        foo(c);
>>        // foo(C.class); // originally my test case was this, but its
>> the same as using 'c'
>>    }  
>> }
>> 
>> Substitute 'a1' for T.
>> 
>> Bounds: a1 <: C<a1>
>> Constraints: c -> Class<a1>
>> 
>> reduce 'c -> Class<a1>'
>> 1. c -> Class<a1>
>> 2. Class<C> -> Class<a1>
>> // at this point I think unchecked conversion is needed as loose
>> constraints are the only place in the reduction process that mention
>> unchecked conversion
>> // but case 1 doesn't apply (Class<a1> is not proper), and case 4
>> doesn't apply because G = Class<a1>, S is already a form of G = Class<C>.
>> 3. Class<C> <: Class<a1>
>> 4. C <= a1
>> 5. C = a1
>> 
>> add bound C = a1, infer the constraint 'C <: C<a1>' during incorporation.
>> 
>> reduce 'C <: C<a1>'
>> 1. C <: C<a1>
>> fail, because C<a1> is a parameterized type but there is no super type
>> of C that is a parameterized type of C
>> 
> 
> I think the mistake I made is during incorporation, the new bound should
> have been 'C <: C<C>' which succeeds with unchecked conversion for the
> subtype constraint. If the rule 'a = U and S <: T' matches before 'a = S
> and a <: T' this is possible. The rules for incorporation (18.3) need to
> be changed slightly to allow for this outcome, however.

Yes, I imagine javac is doing something like this in order to succeed in compilation.  But bounds of the form "T extends Foo<Bar>" are not meant to allow unchecked conversion, and it is incorrect to treat them that way.

> 1. The process doesn't explicitly say only one rule should match, just
> that 'for any pair of bounds matching one of the rules'. I can see the
> meaning go either way, but I originally took this to mean match as many
> rules as possible.

Yes, it's a loop -- while some pair of bounds matches a rule, apply the rule; repeat until no pair of bounds can produce a new bound.

> 2. Supposing only one rule should match it is not clear that the 'a = U
> ...' rules should match before the 'a = S' rules. So the ordering of the
> rules should put 'a = U' at the top. Probably the process should be
> explicit about matching in the order they are given as well.
> 
> Alternatively, S and T could be types that must contain inference
> variables while U must be a proper type. I understood 'S and T are
> inference variables or types' to mean they can be any kind of type.

Yes, they are meant to include any kind of type.

—Dan


More information about the lambda-spec-observers mailing list