resolving several ivars to the same capture?

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed Oct 22 10:05:59 UTC 2025


Hi Stephan,
first, I'll note that this issue seems dangerously close to this open 
spec issue:

https://bugs.openjdk.org/browse/JDK-8016196

Then, to the specifics of your query, the crux of the problem here is to 
determine how this subexpression is type checked:

new A<>(c)

Because that determines T, and the type of T then determines whether 
calling intValue in the other lambda makes sense.

Now, to check the above expression we have to prove that the type of `c` 
is compatible with the formal parameter of the A's constructor, so, 
something like:

C<B<?>> <: C<? extends C<U>> (where U is an inference variable here)

This seems to lead to:

B<?> <: C<U>
B<#CAP> <: C<U>, where #CAP <: Number // CAPTURE HERE!
U = #CAP

So, the type we infer for the instance creation expression would be 
A<#CAP>. Note that this capture variable does have an upper bound 
(Number), which you didn't report in your ecj dump, but I assume it to 
be there (otherwise the second lambda would fail to type check).

Now, the second step of the subtyping proof above requires capture, 
which is the spec issue I mentioned above. I think javac went back and 
forth a bit on whether to apply capture or not (given the open spec 
issue), and perhaps landed on a slightly different place than ecj.

We have other examples where applying capture conversion during 
incorporation this way leads to issues:

https://bugs.openjdk.org/browse/JDK-8206142

(That said, I would have expected javac to perform a capture there -- 
@Vicente, when you have time, can you please take a look and see why 
javac is not capturing?)

Thanks
Maurizio

On 21/10/2025 20:26, Stephan Herrmann wrote:
> Once more, I have a bunch of tests on my desk where javac and ecj 
> disagree.
>
> The pending fix for one case, makes ecj accept the following program, 
> which is rejected by javac:
>
> //---
> import java.util.function.Function;
> public class Test {
>     public static void main(String[] args) {
>         C<B<?>> c = null;
>         m(
>                 _ -> new A<>(c),
>                 b -> b.intValue());
>     }
>     static <T, R> void m(Function<B<Number>, A<T>> f1, Function<T, R> 
> f2) {}
>     static class A<U> {
>         public A(C<? extends C<U>> t) {}
>     }
>     private record B<V extends Number>(V t) implements C<V> {
>
>     }
>     private interface C<W> {
>         W t();
>     }
> }
> //---
>
> javac reports:
> Test.java:5: error: cannot infer type arguments for A<>
>                 _ -> new A<>((C<B<?>>) null),
>                      ^
>   reason: cannot infer type-variable(s) U
>     (argument mismatch; C<B<?>> cannot be converted to C<? extends C<U>>)
>   where U is a type-variable:
>     U extends Object declared in class A
> Test.java:6: error: cannot find symbol
>                 b -> b.intValue());
>                       ^
>   symbol:   method intValue()
>   location: variable b of type Object
> 2 errors
>
>
> With my pending fix ecj would accept, with these details:
>
> Outer inference of m() yields:
>     T#0    :    capture#1-of ?
>     R#1    :    java.lang.Integer
>     U#3    :    capture#1-of ?
> so m() resolves as:
> void m(Function<B<Number>,A<capture#1-of ?>>, Function<capture#1-of 
> ?,Integer>)
>
> With target types Function<B<Number>,A<capture#1-of ?>> and 
> Function<capture#1-of ?,Integer> the two lambdas complete resolution 
> just fine.
>
>
> Am I missing any detail, why this inference solution is not valid?
>
>
> FYI, the pending fix relates to capturing while computing super types 
> of A<?>. While normally we ensure uniqueness of captures per source 
> location, when type inference requests a capture for supertype 
> computation we use the current invocation as the point of reference, 
> i.e., repeatedly capturing the same wildcard during one invocation of 
> type inference will share one unified capture. Is this OK? Or what are 
> the rules for uniqueness vs. unification in this case?
>
>
>
> thanks,
> Stephan


More information about the compiler-dev mailing list