inference trouble with recursive generics and raw types
Dan Smith
daniel.smith at oracle.com
Mon Nov 4 08:38:55 PST 2013
On Nov 3, 2013, at 10:27 AM, Stephan Herrmann <stephan.herrmann at berlin.de> wrote:
> Working on the latest spec I'm seeing a discrepancy between
> the spec and actual javac behavior.
>
> interface Foo<T extends Foo<T>> {
> }
>
> class Bar<Q> {
> }
>
> public class X {
> void readDatabase() {
> Bar<Foo> bar = new Bar<Foo>();
> read(bar, "sadasd"); // infer this call!
> }
>
> <P extends Foo<P>, D extends Bar<P>>
> D read(D d, String s) {
> return d;
> }
> }
This is essentially the same issue that you reported on October 10, right?
public class X {
public static void main(String[] args) {
EntityKey entityKey = null;
new EntityCondenser().condense(entityKey);
}
public static class EntityCondenser {
<I, E extends EntityType<I, E, K>, K extends EntityKey<I>> void condense(K entityKey) {
}
}
public class EntityKey<I> {}
public interface EntityType<
I,
E extends EntityType<I, E, K>,
K extends EntityKey<I>> {
}
}
> I see type inferencing regarding the invocation of read(..)
> producing the following:
> (a) declaration of type variable P produces this dependency:
> P#0 <: Foo<P#0>
> (where P#0 is an inference variable representing P)
> (b) declaration of type variable D produces this type bound:
> D#1 <: Bar<P#0>
> (c) the constraint formula for the argument expression 'bar' yields:
> D#1 :> Bar<Foo>
> (d) incorporation of (b) and (c) yields this constraint:
> ⟨Bar<Foo> <: Bar<P#0>⟩
> (e) reduction on (d) yields this type bound:
> P#0 :> Foo
> (f) incorporating (a) with (e) yields this type constraint:
> ⟨Foo <: Foo<P#0>⟩
> At this point inference fails because the raw type Foo is not
> recognized as a legal subtype of Foo<P#0>: In 18.2.3 the
> 5.2nd bullet applies: We need to find the parameterization
> of Foo that is a supertype of Foo, which, however, cannot be
> determined since Foo (substitute for S) is a raw type.
>
> Is there a mistake in my derivation?
Nope, I agree with your explanation (except step (e) should be an eq bound, not a lower bound). Crucially, raw Foo is not within the upper bound of P#0.
> OTOH, all compilers that I tested accept the above program
> (from javac 1.5 to 8b112 as well as ecj).
Yes, I identified this as a bug in your last report.
Significantly, an invocation of the form 'this.<Foo,Bar<Foo>>read(bar, "sadasd")' will compile. So this isn't really specifically an inference issue: the compilers simply believe that a raw type is "within" a bound that is a parameterization of that type. And that interpretation is flat-out wrong. Bounds are subtyping assertions. JLS is consistent about this -- see 4.5 and 15.12.2.2-4.
Here's my javac bug report: https://bugs.openjdk.java.net/browse/JDK-8026527
> In spite of all talk about reducing the support for raw types, the
> above example seems to be a relevant pattern, were avoidance of
> raw types is not as easy as just filling in some type parameters
> (or is the above program actually unsafe and thus can only be
> written using raw types?)
>
> Unless I'm missing s.t.:
>
> Either the spec needs to be extended to gracefully handle raw
> types in at least this particular rule.
>
> Or all compilers have a bug and must be fixed :)
The compilers have a bug.
It might make sense to reexamine the spec here if there were some sort of lack of clarity, but there's really not. And making a change in the interpretation of bounds would be a big deal, because the use of subtyping here is fundamental to a lot of things (like inference, type variable subtyping, etc.) that are built on top.
—Dan
More information about the lambda-spec-observers
mailing list