resolving several ivars to the same capture?
Stephan Herrmann
stephan.herrmann at berlin.de
Thu Oct 23 10:25:49 UTC 2025
Hi Maurizio
thanks for taking the time.
Am 22.10.25 um 12:05 schrieb Maurizio Cimadamore:
> first, I'll note that this issue seems dangerously close to this open spec issue:
>
> https://bugs.openjdk.org/browse/JDK-8016196
In this particular issue I'm puzzled by this statement:
"The correct behavior is unspecified: what is the parameterization of List that
is a supertype of ArrayList<?>? How do we derive it?"
Doesn't 4.10.2 clearly state that type arguments should be captured here?
(in fact until recently ecj failed to apply this rule, and many of our current
issues are "regressions" resulting from fixing this).
Is that rule in 4.10.2 at stake, potentially to be removed or altered?
> 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).
So far all looks good, and yes, the capture has Number as its upper bound.
> 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
The issue states that rejecting the example is a regression (in 9). FWIW, ecj
accepts the example ever since Java 1.8. Seeing that a fix for javac exists for
many years, why is that fix not applied? Is this an indication that perhaps JLS
is to be changed rather than javac?
What do you recommend for the current issue? Is it OK for ecj to accept the
program, and if people complain about the difference to javac, explain that this
is a bug in javac? Or are there any fine points in the spec which we are
missing, that justify rejecting?
> (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?)
OK, I might just wait for Vicente's answer here :)
thanks,
Stephan
> 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