ADDITIONAL SYSTEM INFORMATION :
Windows 10 1903
openjdk 14 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
Javac allows a type conversion which is very reasonable, but which is forbidden by JLS.
This report recommends JLS to be fixed. (But not sure whether it is a bug of JLS or of javac, but surely it is a bug.)
(by the way, why can't I choose Component=specification Subcomponent=language?)
public class MyClass <X extends Runnable> {}
MyClass<? super Runnable> x = null;
MyClass<Runnable> y = x; //allowed by javac, forbidden by jls.
Yes, this conversion is very reasonable. Should be allowed. But unfortunately, jls is forbidding it.
Jls 5_2 says, in short, a ref-to-ref assignation is allowed basically iff it is a widening reference conversion.
A widening reference conversion exists if the src is subtype of the dst, as specified in 5_1_5.
Subtyping among ref types is defined in 4_10_2. To justify the above assignation, <Runnable> must contain
<CAPTURE>, where CAPTURE is a fresh variable whose lower bound and upper bound both are Runnable.
But the definition of 'containing' in 4_5_1 states that a type only contains itself. Unless jls introduces something like equivalence of types or specifies a condition based on which two apparantly different types can be treated as the same thing, <Runnable> does not contain <CAPTURE>. The conversion is wrong.
javac seems to be regarding MyClass<? super Runnable> and MyClass<Runnable> as the same. Moreover, List<MyClass<? super Runnable>> and List<MyClass<Runnable>> as the same. (Each can be converted into the other.) When do these equations occur?
Whoa! The rule is far from trivial! See another example I've just found!
class Second <X extends Runnable, Y extends X> {}
<U extends Thread> void f() {
List<Second<U, ? super Thread>> c0 = null;
List<Second<U, U>> c1 = c0;
List<Second<U, ? super Thread>> c2 = c1;
}
Surprisingly, c1 = c0 is allowed but c2 = c1 is a copile-time error! What is going on?
4_5_1 says two types have containing relation between them only through "T and Second<U, U> can be 'T' at the same time, but when c2 = c1, not? Now I am thinking jls is really buggy around this.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
If you are curious about my second example, compile the following and see how it will fail. Then comment the line c2 = c1 out, compile, and see that it will succeeds. No need to execute it.
---------- BEGIN SOURCE ----------
import java.util.*;
public class JlsSux{
class First <X extends Runnable> {}
class Second <X extends Runnable, Y extends X> {}
public static void main(String... args){
List<First<? super Runnable>> a0 = null;
List<First<Runnable>> a1 = a0;
List<First<? super Runnable>> a2 = a1;
}
static <U extends Thread> void u(){
List<Second<U, ? super Thread>> c0 = null;
List<Second<U, U>> c1 = c0;
List<Second<U, ? super Thread>> c2 = c1;
}
}
---------- END SOURCE ----------
FREQUENCY : always