[External] : Re: Javac type inference issue

Vicente Romero vicente.romero at oracle.com
Mon Aug 28 23:57:14 UTC 2023



On 8/28/23 16:01, Attila Kelemen wrote:
> What I find the most interesting about this issue is what Rémi showed. 
> If you change the argument type of listMethod from `List<List<? super 
> T>>` to `List<? super List<? super T>>`. then the type inferer can 
> find the correct type assignments. And this looks bizarre to me, 
> because that just looks like a straight up more difficult problem, yet 
> it is demonstrably not so for the compiler.

the compiler is basically doing an upfront capture conversion every time 
it does a subtyping test during inference, this produces the expected 
result for most type pairs but not for all as your test case proves,

Vicente

>
> Vicente Romero <vicente.romero at oracle.com> ezt írta (időpont: 2023. 
> aug. 28., H, 20:56):
>
>     very interesting issue, I agree that the compiler could do better
>     here, this needs more research, I have been taking a look at the
>     compiler internals, it could be that a structural type comparison
>     could be needed here. In the mean time I have filed [1]
>
>     Thanks,
>     Vicente
>
>     [1] https://bugs.openjdk.org/browse/JDK-8315134
>
>     On 8/28/23 06:59, Maurizio Cimadamore wrote:
>>
>>     This does seem like an issue.
>>
>>     Note that similar code, w/o a method call works:
>>
>>     |List<List<? super T1>> res = Arrays.asList(args); |
>>
>>     I did some manual calculation of the bounds involved and I got to:
>>
>>     |List<? super T1> <: #A = List<? super #T2> |
>>
>>     Which seems solvable for:
>>
>>     |A = List<? super T2> T2 = T1 |
>>
>>     But the javac error message reveals a capture conversion is being
>>     applied somewhere:
>>
>>     |error: method listMethod in class Test cannot be applied to
>>     given types; List<List<? super T2>> res =
>>     listMethod(Arrays.asList(args)); ^ required: List<List<? super
>>     T2>> found: List<List<? super T1>> reason: inference variable T
>>     has incompatible bounds equality constraints: List<? super CAP#1>
>>     // <-------------------------- lower bounds: List<? super T1> |
>>
>>     Which seems odd - I would have understod if javac tried to
>>     capture “args” before passing to Arrays::asList - but that’s not
>>     what is happening here.
>>
>>     My feeling (confirmed by looking at the internals of the
>>     compiler) is that javac is applying incorporation to the above
>>     set of bounds - and running the following check:
>>
>>     |List<? super T1> <: List<? super #T2> |
>>
>>     The JLS here says that when running the this incorporation step,
>>     since both types are |? super X| wildcards we should just derive:
>>
>>     |#T2 <: T1 |
>>
>>     (e.g. no capture conversion applied).
>>
>>     But that’s not what happens in the compiler implementation (which
>>     does a straight subtyping check with capture conversion), hence
>>     the issue.
>>
>>     Maurizio
>>
>>     On 27/08/2023 15:06, Attila Kelemen wrote:
>>
>>>
>>>         In case of a capture, the inference does not try to replace
>>>         the capture by its bound. It's a limitation of the inference
>>>         which tend to choose the most speciifc type where here, it
>>>         has to use a less specific type.
>>>
>>>
>>>     I don't quite understand the step by step "reasoning"  of the
>>>     type inferer here, because naively this is what I would do if I
>>>     was employed as a full time type inferer :). (I'm copying here
>>>     my code again with some variable renaming for easier reference)
>>>     Suppose I'm a compiler, and I see this:
>>>
>>>     ```
>>>     <T1> void arrayMethod(List<? super T1>[] args) {
>>>         listMethod(Arrays.asList(args));
>>>     }
>>>     <T2> void listMethod(List<List<? super T2>> list) { }
>>>     ```
>>>
>>>     1. I look at `arrayMethod` and see 2 type variables to infer.
>>>     One for `listMethod` (let's call it #L), and one for
>>>     `Arrays.asList` (let's call it #A).
>>>     2. The first thing I see is that `Arrays.asList` returns
>>>     `List<#A>`, and it is assigned to `List<List<? super #L>>`. This
>>>     is easy to satisfy with #A = List<? super #L> (and this is
>>>     actually the only possibility).
>>>     3. Then I go the next part, and see that args is of course
>>>     `List<? super T1>[]` which must be assigned to `#A[]`, which is
>>>     again easy to satisfy with #A = List<? super T1> (which is the
>>>     only possibility again).
>>>     4. So, now I have to satisfy all equalities `#A = List<? super
>>>     #L>` and `#A = List<? super T1>`, which implies that `List<?
>>>     super #L> = List<? super T1>`, which is easy to satisfy by the
>>>     choice #L = T1 (again, this is the only choice).
>>>     5. So, now I have resolved both variables:` #L = T1`, and `#A =
>>>     List<? super T1>`, and indeed I can validate that this choice
>>>     will satisfy every constraint.
>>>
>>>     I'm just puzzled here, because sometimes the type inferer can
>>>     solve much more difficult problems, and this seems trivial
>>>     compared to those.
>>>
>>>         My bad, I should have written List<? super List<? super T>>.
>>>
>>>
>>>     Surprisingly (to me), that would compile without explicitly
>>>     specifying the type arguments (though of course that is not a
>>>     type declaration that is acceptable for me). What surprises me
>>>     here is that I would think that this would make the job of the
>>>     type inferer harder, since now it is more difficult to find the
>>>     correct types, since there are more options. Without the outer
>>>     "? super" the inferer would be forced to make the correct type
>>>     assignment (since there is always only one possibility). That
>>>     is, when you write `List<? super List<? super T>`, now you get
>>>     another fresh type variable to satisfy. Namely, you have to pick
>>>     a type of `? super List<? super T>`. While in the other case
>>>     your only option for this was `List<? super T>` (which is the
>>>     only correct choice even in the `? super` variant.
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230828/f96d8062/attachment-0001.htm>


More information about the compiler-dev mailing list