Type Inference Question

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed May 30 07:38:55 PDT 2012


On 30/05/12 14:12, Jan Finis wrote:
> Hi compiler-dev Team,
>
> I am currently implementing parts of a Java compiler and I am stuck 
> with the type inference of method type parameters, as specified in 
> paragraph 15.12.2.7 of the Java Spec.
>
> I have already tried to ask my questions on stackoverflow, but they 
> are so in-depth that only people who have already gone the trouble 
> implementing this weird piece of text can help me.
>
>
> Actually, I have two questions, the first one was already posed on 
> stackoverflow. I copy its text here:
>
> My problem is this line in the spec:
>
> /lcta(U) = ? if U's upper bound is Object, otherwise ? extends 
> lub(U,Object)/
>
> U is an arbitrary type expression. What is the upper bound of a type 
> expression? In addition, why is the lcta always a wildcard?
>
> The spec defines
>
> /CandidateInvocation(G) = lci(Inv(G))/.
>
> Now, for example, consider the case that Inv(G) = { List<String> }, 
> i.e., the only possible candidate invocation is a single parameterized 
> type. Now, due to the rule
>
> /lci(G<X1, ..., Xn>) = G<lcta(X1), ..., lcta(Xn)>/,
>
> the result of CandidateInvocation( G ) = lci( { List<String> } ) would 
> be defined as:
>
> List<lcta(String)>
>
> in my opinion, lcta should simply return String here, because if 
> List<String> is the only possible invocation, it is a good idea to 
> infer List<String> as the argument. However, the definition of lcta(U) 
> dictates that the result is either ? or ? extends lub(...), so the 
> result IS ALWAYS a wildcard. This seems strange. What am I 
> misinterpreting here?
>
I'll leave the fine print to our spec guru - but javac infers 
List<String> as demonstrated by the following example:

class Test {
<Z> Z m(Z z) { return null; }
     void test(java.util.List<String> ls) {
         ls = m(ls); //ok!
     }
}

This is because javac has a fast-path where lub is not even executed 
when there's only one lower bound. Interestingly, this is what keeps 
this example consistent with this other one:

class Test {
<Z> Z m(Z z1, Z z2) { return null; }
     void test(java.util.List<String> ls) {
         ls = m(ls, ls); //ok!
     }
}

While reading from the spec it looks like the first one should not 
compile while the latter should...
>
>
> The second problem is about type inference of capture converted 
> arguments. This code is taken from the openJDK collections library:
>
> class UnmodifiableMap<K,V> {
>     public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
>         return null;
>     }
>
>     private final Map<? extends K, ? extends V> m;
>     private transient Set<K> keySet;
>     public Set<K> keySet() {
>         if (keySet==null)
>             keySet = UnmodifiableMap.unmodifiableSet(m.keySet()); 
> //This line is the problem
>         return keySet;
>     }
>
>
> }
>
> the problem is the type inference for the unmodifiableSet method call. 
> The actual type of the argument m.keySet() is Set<Capture of ? extends 
> K> and the formal type argument is Set<? extends T>. This imposes the 
> subtype constraint Set<Capture of ? extends K> <: Set<? extends T> 
> which is simplified to Capture of ? extends K <: ? extends T and 
> finally T :> Capture of ? extends K. According to the spec, this final 
> constraint yields lub(Capture of ? extends K) as inferred type for T. 
> If I read the spec correctly, lub(Capture of ? extends K) is Capture 
> of ? extends K, so this very capture is inferred for T. My compiler 
> does this and it produces a compile error, since the result of the 
> method is then Set<Capture of ? extends K>. This result cannot be 
> applied to the variable keySet which is of type Set<K>. Thus, the 
> "correct" type inference should yield K instead of Capture of ? 
> extends K. Since this is code from the standard library which should 
> compile fine, it seems that the usual javac infers that correct bound. 
> But  how? According to the spec, Capture of ? extends K should be 
> inferred. What am I misinterpreting here?

Javac workarounds the problem by using the capture of the upper bound of 
an argument type during method checking. This allows inference to infer 
less captured types, which, at the time the javac implementation with 
generics was rolled out, it was believed to be better in terms of usability.

Maurizio
>
>
> Thank you in advance for any help.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20120530/6de257ec/attachment.html 


More information about the compiler-dev mailing list