LVTI: as per spec upward projection couldn't have resulted in a '? extends' parameterization

Georgiy Rakov georgiy.rakov at oracle.com
Wed Oct 11 15:57:50 UTC 2017


HelloDan,

let's consider following code:

    class C0 {}
         class C1 extends C0 {}
             class C2 extends C1 {}

    public class Shell<T extends C1> {
         void set(T t) {}
         T get() {return null;}

         public static void main(String args[]) {
             Shell<? extends C0> y = new Shell<C1>();

             var z = y; //upward projection is applied here

             z = new Shell<C1>(); //compilation succeeds
             z = new Shell<C2>(); //compilation succeeds
             z = new C0();        //obviously compilation error

             C0 c0 = z.get(); //compilation succeeds
             C1 c1 = z.get(); //compilation succeeds
             C2 c2 = z.get(); //compilation error
         }
    }

When compiling this code by javac from jdk10 build 25 we receive 
following output:

    Shell.java:16: error: incompatible types: C0 cannot be converted to
    Shell<? extends C1>
                     z = new C0();        //obviously compilation error
                         ^
    Shell.java:20: error: incompatible types: CAP#1 cannot be converted
    to C2
                     C2 c2 = z.get(); //compilation error
                                  ^
       where CAP#1 is a fresh type-variable:
         CAP#1 extends C1 from capture of ? extends C1
    2 errors

 From this output and the fact which lines compile and which don't it 
follows that the type of 'z' variable was inferred as Shell<? extends 
C1>. However according to spec [1] this couldn't have happened because 
of the following reasoning.

1. Let's consider assertion below from [1]:

    *If /LocalVariableType/ is |var|, then let /T/ be the type of the
    initializer expression when treated as if it did not appear in an
    assignment context, and were thus a standalone expression (15.2
    <https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.2>).
    The type of the local variable is the upward projection of /T/ with
    respect to all synthetic type variables mentioned by /T/ (4.10.5
    <http://cr.openjdk.java.net/%7Edlsmith/local-var-inference.html#jep286-4.10.5>).*
    **

according to this assertion:*
*type_of(z) = upward_projection(type_of(y)) = 
upward_projection(capture_conversion(Shell<? extends C0>)) = 
upward_projection(Shell<CAP>)
where CAP is a capture variable with following bounds:
NULL_TYPE <: CAP <: glb(C0, C1) =>
NULL_TYPE <: CAP <: C1

2. When applying upward projection to Shell<CAP>, T is Shell<CAP> and 
restricted type variables include just CAP.

3. During upward projection assertions below from [1] are touched:**
**

    *If T is a parameterized class type or a parameterized interface
    type, G<A1, ..., An>, then the result is G<A1', ..., An'>, where,
    for 1 ≤ i ≤ n, Ai' is derived from Ai as follows:
       ...
       If Ai is a type that mentions a restricted type variable, then
    Ai' is a wildcard. Let U be the upward projection of Ai. There are
    three cases:
    *

Here we have just A1 which is CAP so 
U=upward_projection(A1)=upward_projection(CAP)=upward_projection(C1)=C1

4. Afterwards following assertion from [1] is touched:

    *If /U/ is not |Object|, and if either the declared bound of the ith
    parameter of /G/, /Bi/, mentions a type parameter of /G/, or /Bi/ is
    not a subtype of /U/, then /Ai'/ is an upper-bounded wildcard, /|?
    extends| U/.*
    **

Here we have just B1 which is C1, so:
**a. "*/U/ is not |Object|*" is _true_ since U is C1;
b. "*the declared bound of the ith parameter of /G/, /Bi/, mentions a 
type parameter of /G/*" is _false_ since B1 is C1 - no type parameters 
are mentioned;
c. "*/Bi/ is not a subtype of /U/*" is _false_ because both B1 and U are 
C1 so B1 is a subtype of U since a class is always a subtype of itself.

For this reason A1' cannot be defined by this assertion as ? extends C1 
which would make upward projection to result in Shell<? extends C1>, so 
none of other assertions can do either.

This looks like a spec issue because the result of upper projection 
provided by javac looks reasonable. Is it really a spec issue?

It's not quite clear why "*/Bi/ is not a subtype of /U/*" was added to 
the specification. Some examples clarifying this point would be highly 
appreciated.

[1] http://cr.openjdk.java.net/~dlsmith/local-var-inference.html

Thank you,
Georgiy
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20171011/82d17701/attachment.html>


More information about the compiler-dev mailing list