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