ivar dependencies during resolution
Stephan Herrmann
stephan.herrmann at berlin.de
Tue Jan 6 21:53:47 UTC 2026
Hello,
I am currently revisiting an old test case, where I'm no longer sure, how
compilers can find the desired inference solution:
//---
import java.util.*;
import java.util.stream.*;
import static java.util.Arrays.asList;
public class C {
static final List<Integer> DIGITS =
Collections.unmodifiableList(asList(0,1,2,3,4,5,6,7,8,9));
Collection<String> flatMapSolutions(final boolean b) {
Collection<String> solutions =
DIGITS.stream().flatMap( s -> {
return b ? Stream.empty() : Stream.of("");
}) .collect(Collectors.toList());
return solutions;
}
}
//---
javac and released versions of ecj accept this.
With some pending fixes, however, this is how ecj would handle this:
ivars are:
R#2 for <R> from flatMap()
T#3 for <T> from empty()
T#4 for <T> from of()
flatMap() is inferred as a standalone expression, where resolution starts from
these type bounds:
T#3 <: R#2
R#2 :> java.lang.String
T#4 :> java.lang.String
T#4 <: R#2
From this we compute the following dependencies:
R#2 depends on [R#2, T#3, T#4]
T#3 depends on [T#3, R#2, T#4]
T#4 depends on [T#4, R#2, T#3]
i.e., every ivar depends on every ivar, and whichever ivar we want to resolve,
we need to resolve them all in one batch.
The first attempt of resolution adds these instantiations:
T#3 = java.lang.Object
R#2 = java.lang.String
T#4 = java.lang.String
which lets incorporation fail at
java.lang.String :> java.lang.Object
derived from
R#2 = java.lang.String
R#2 :> java.lang.Object (from T#3 = Object & T#3 <: R#2)
Then during the second attempt we resolve R#2 to a fresh type variable Z#0, with
lower bound String and upper bound Object.
With this, inference for flatMap() succeeds with a return type of Stream<Z#0>.
That return type is propagated through the collect() call to yield List<Z#0>
which cannot be assigned to Collection<String>.
Can anyone help me, at which point the above diverges from the desired path of
inference?
My best guess was, that perhaps R#2 should not be seen as depending on T#3 nor
T#4: then resolving only R#2 would succeed in the first attempt, leading to an
inference result of "Stream<String> flatMap(..)" as desired. But I doubt that
this is how things are meant to work.
thanks,
Stephan
More information about the compiler-dev
mailing list