JDK-8219318 (???): inferred type does not conform to upper bound(s) when collecting to a HashMap

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Dec 16 16:59:32 UTC 2019


On 16/12/2019 16:40, Maurizio Cimadamore wrote:
> Which again leaves us in a position where no useful constraint can be 
> derived from HashMap::new 

I think that the key in unlocking this lies deeper in the spec. If we 
look at 18.5.1 we will see sentences such as:

" Otherwise, C includes, for all i (1 ≤ i ≤ k) where ei is pertinent to 
applicability, ‹ei → Fi θ›. "

In the case of your example, the outermost call is putAll and the 
argument in question is call to Stream::collect. So that one is 
pertinent to applicability. But what does the constraint above reduces to?

If we follow 18.2.1 we find the answer:

"If the expression is a class instance creation expression or a method 
invocation expression, the constraint reduces to the bound set B3 which 
would be used to determine the expression's compatibility with target 
type T, as defined in §18.5.2.1. (For a class instance creation 
expression, the corresponding "method" used for inference is defined in 
§15.9.3.) "

So, it seems like we should compute a new bound set using the rules in 
18.5.2.1 - using the target type which is the argument type of putAll - 
hence Map<? extends Integer, ? extends Integer>.

Now, this *should* work (despite we get no constraints from 
HashMap::new), but it seems like it doesn't. Of these there could be two 
possible scenario: (1) there's a subtle javac bug or (2) under some 
situations the spec forces eager instantiation for some of its 
arguments. Now, let's examine (2) - 18.5.2.1 says the following:

"Otherwise, if R θ is an inference variable α, and one of the following 
is true:

     T is a reference type, but is not a wildcard-parameterized type, 
and either (i) B2 contains a bound of one of the forms α = S or S <: α, 
where S is a wildcard-parameterized type, or (ii) B2 contains two bounds 
of the forms S1 <: α and S2 <: α, where S1 and S2 have supertypes that 
are two different parameterizations of the same generic class or interface.

     T is a parameterization of a generic class or interface, G, and B2 
contains a bound of one of the forms α = S or S <: α, where there exists 
no type of the form G<...> that is a supertype of S, but the raw type 
|G<...>| is a supertype of S.

     T is a primitive type, and one of the primitive wrapper classes 
mentioned in §5.1.7 is an instantiation, upper bound, or lower bound for 
α in B2.

  then α is resolved in B2, and where the capture of the resulting 
instantiation of α is U, the constraint formula ‹U → T› is reduced and 
incorporated with B2. "

Now, this does ring some bells - if you look, Stream::collect does 
return a type variable:

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.stream.Collector-

Now, the target type here is a wildcard-parameterized type (first 
bullet), so eager instantiation should not happen - but I wonder what 
happens inside javac.

Maurizio



More information about the compiler-dev mailing list