Does JLS tell if this program is legal?

Stephan Herrmann stephan.herrmann at berlin.de
Tue May 21 19:49:29 UTC 2024


Hello,

I am investigating a report about a program that is accepted by javac, but 
rejected by ecj, where per my current thinking I suspect the case to be 
underspecified in JLS.

The program could be reduced to this:

	interface Inner<S> { }
	interface Super<T> { }
	interface One<U> extends Super<Inner<U>> { }
	interface Two<V> extends Super<Inner<V>>, One<V> { }

	public class Bug {
		<W extends One<?>> W getOne(W w) {
			return null;
		}
		<X, Y extends Two<X>> void foo(Y y) {
			Y one = getOne(y);
		}
	}

In ecj type inference fails during the incorporation phase of invocation type 
inference.

At that point we have these type bounds:
	W#0 :> Y
	W#0 <: Y
	W#0 <: One<?>

 From these, type bounds 2 and 3 trigger the following sentence from §18.3.1:
"When a bound set contains a pair of bounds α <: S and α <: T, and there exists 
a supertype of S of the form G<S1, ..., Sn> and a supertype of T of the form 
G<T1, ..., Tn> (for some generic class or interface, G), then for all i (1 ≤ i ≤ 
n), if Si and Ti are types (not wildcards), the constraint formula ‹Si = Ti› is 
implied."

We have:
* α  = W#0
* S  = Y
* T  = One<?>
* Super<Inner<X>> is a supertype of S / Y (indirectly via Two<X>)
* Super<Inner<?>> is a supertype of T / One<?> (directly)
* S1  = Inner<X>
* T1  = Inner<?>
* new constraint formula created: ⟨Inner<X> = Inner<?>⟩

As both types are proper types, but not the same type, this constraint reduces 
to false, causing inference to fail.

For a moment I was tempted to challenge the part "if Si and Ti are types (not 
wildcards)", perhaps also Inner<?> would be a reason not to create that 
constraint, but I gave up that idea when I discovered the following solution:

* One<X> is a supertype of S / Y (as the 2nd superinterface of Two<X>)
* One<?> is a supertype of T / One<?> (identity)
* S1 = X
* T1 = ?
* NO new constraint is created
* inference succeeds to instantiate W#0 = Y

In my understanding the weakness here is in "there *exists* a supertype of S ... 
(for *some* generic class or interface, G)". Which seems to imply once a 
compiler finds one witness for the conditions of that supertype, that's the only 
thing that needs to be looked at for this sentence. In fact, the choice which 
witness is found by ecj can be toggled by swapping the order of Two's 
superinterfaces.

Now declaration order is probably a bad thing to decide about accept/reject. So 
which criterion should be used to select G?

My current implementation draft prefers a "more specific" supertype, which 
suffices to select One<X> instead of Super<Inner<X>> in this particular case.

Does javac perform an explicit selection in this situation, or does it "just 
happen" to find the more helpful supertype?

 From a theory point of view which supertype is the "correct" one to work with? 
How to select among several candidates?

I suspect that a direct subtype check among candidates may not be the relevant 
criterion, since the different effects are caused by the position / nesting 
level into which the wildcard is propagated.

thanks,
Stephan


More information about the compiler-dev mailing list