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

Georgiy Rakov georgiy.rakov at oracle.com
Thu Oct 12 17:53:56 UTC 2017


Thanks for quick response and clarifications. Now two more questions are 
presented below.

1) Let's consider the code from the original letter.

    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
             ...

javac output following error message on line "z = new C0();":

    Shell.java:16: error: incompatible types: C0 cannot be converted to
    Shell<? extends C1>
                     z = new C0();        //obviously compilation error

According to this error message javac assumes type of 'z' to be Shell<? 
extends C1>. It seems that this is at least a cosmetic defect since type 
of 'z' is Shell<?> as it's been clarified.
Should a JBS issue be filed on javac for this reason?

2) The next question relates to further applying rules for calculation 
of upward projection of Shell<CAP> presented in the original letter.

As it's been clarified the assertion (1) from [1] presented below 
doesn't apply so we proceed to the the next assertion (2) from [1]:

    (1) 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.
    (2) Otherwise, if the downward projection of Ai is L, then Ai' is a
    lower-bounded wildcard, ? super L.
    (3) Otherwise, the downward projection of Ai is undefined and Ai' is
    an unbounded wildcard, ?.

Which leads us to downward projection rules from [1]:

    The downward projection of a type T with respect to a set of
    restricted type variables is a partial function, defined as follows:
    (4)    If T does not mention any restricted type variable, then the
    result is T.
    (5)    If T is a restricted type variable, then if T has a lower
    bound, and if the downward projection of that bound is L, the result
    is L; if T has no lower bound, or if the downward projection of that
    bound is undefined, then the result is undefined.

Here T is 'CAP extends C1 super NULL_TYPE' as per capture conversion and 
restricted type variable include just the same CAP.
Rule (4) doesn't apply so according to rule (5): 
downward_projection(CAP) = downward_projection(NULL_TYPE) = NULL_TYPE, 
NULL_TYPE doesn't mention any restricted type variable so downward 
projection of NULL_TYPE is NULL_TYPE according to rule (4).

Thus rule (2) results in ? super NULL_TYPE.
Is this understand correctly that '? super NULL_TYPE' is effectively 
equal to '?' (simple wildcard) because both of these wildcrads define 
the same set of types since NULL_TYPE's super types are all reference 
types and any reference type extends Object directly or not? According 
to this reasoning it's rule (2) which makes 
upward_projection(Shell<CAP>) to result in 'Shell<?>'. Is this correct? 
If it is should spec be clarified on this because the reduction of '? 
super NULL_TYPE' to '?' is not explicitly specified by JLS and it might 
be not obvious that it's applied?

Or should spec just specify that the downward projection of NULL_TYPE is 
undefined? In this case it would be rule (3) which would make 
upward_projection(Shell<CAP>) to result in Shell<?>.

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

Thank you,
Georgiy.

On 12.10.2017 2:09, Dan Smith wrote:
> Hey, thanks for careful testing of these rules. I'm sure they can use 
> some scrutiny!
>
>> On Oct 11, 2017, at 9:57 AM, Georgiy Rakov <georgiy.rakov at oracle.com 
>> <mailto:georgiy.rakov at oracle.com>> wrote:
>>
>> 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.
>
> You are right that this rule does not apply.
>
> What you are missing is that, per the subsequent rules, the type is
> Shell<?>
>
> Subsequently, all reads of 'z' have type
> capture(Shell<?>) = Shell<CAP> where CAP extends C1
>
> In other words, there is no need to mention C1 in the type, because it 
> is implicit from the declaration-site bound of T in Shell.
>
>> 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.
>
> Ideally, we'd like an upper bound and a lower bound:
> ? extends upward(Ai) super downward(Ai)
>
> However, wildcards aren't expressive enough to have both upper and 
> lower bounds, so we have to pick one. The heuristic is: if the upper 
> bound is "meaningful", use that. If the upper bound is not meaningful, 
> use the lower bound instead (or nothing, if there is no lower bound). 
> The definition of "meaningful" is, loosely, "something that may affect 
> the upper bound of the capture of the result".
>
> If Bi is a subtype of of U, then U will not affect the upper bound of 
> the capture of the result, because capture will just do a glb(Bi, U) = Bi.
>
> —Dan

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20171012/32a7ab26/attachment.html>


More information about the compiler-dev mailing list