Bug in type inference algorithm in the specification
Timo Kinnunen
timo.kinnunen at gmail.com
Sat Apr 19 09:22:08 UTC 2014
Hi,
I’ve found a bug in the type inference algorithm as it is specified in JLS. I’m going to make the dangerous assumption that the way Javac implements the specification (in this particular case) is correct, because unfortunately I have to, I can’t read the algorithm in the specification itself.
The bug is reproduced by this code:
@FunctionalInterface interface Subsumer<T> { void accept(T t);
default Subsumer<T>
andThe2(Subsumer<T> this, Subsumer<? super T> afterT)
{ return (T t) -> { this.accept(t); afterT.accept(t); }; }
static <U> Subsumer<U>
andThe3(Subsumer<U> tihs, Subsumer<? super U> afterU)
{ return (U u) -> { tihs.accept(u); afterU.accept(u); }; }
} public class JavacECJDiffer { static <T extends ISSUPER_T, ISSUPER_T> void method() {
BinaryOperator<Subsumer<? super T>> attempt_X_2 = Subsumer::andThe2;
BinaryOperator<Subsumer<? super T>> attempt_X_3 = Subsumer::andThe3;
// Summary:
// ECJ error #3, javac no error
// ECJ error #4, javac error #1
} }
As you can see, the types are equivalent, apart from the names T and U.
(Aside: is this list capable of processing colors? I always hate to scrub all the pretty syntax coloring before sending…)
(Aside 2: the full version of the source code is available here in this ECJ bug report https://bugs.eclipse.org/bugs/show_bug.cgi?id=432759 about the difference between the compilers.)
Javac’s error messages are really good and in this case reveal what the cause of the bug is:
error: incompatible types: invalid method reference
BinaryOperator<Subsumer<? super T>> attempt_X_3 = Subsumer::andThe3;
<50 spaces>---------------------------------------^
The3 in interface Subsumer<T#2> cannot be applied to given types
required: Subsumer<U>,Subsumer<? super U>
found: Subsumer<? super T#1>,Subsumer<? super T#1>
reason: inference variable U has incompatible bounds
equality constraints: CAP#1
upper bounds: T#1,Object
where U,T#1,ISSUPER_T,T#2 are type-variables:
U extends Object declared in method <U>andThe3(Subsumer<U>,Subsumer<? super U>)
T#1 extends ISSUPER_T declared in method <T#1,ISSUPER_T>method()
ISSUPER_T extends Object declared in method <T#1,ISSUPER_T>method()
T#2 extends Object declared in interface Subsumer
where CAP#1 is a fresh type-variable:
CAP#1 extends Object super: T#1 from capture of ? super T#1
Did you see it? Where it says “required: Subsumer<U>,Subsumer<? super U>” it should be saying
“required: Subsumer<U>,Subsumer<U> or required: Subsumer<U>,Subsumer<? super U>” because that is what is actually allowed.
This bug makes generics in Java much harder to use than they need be. In addition, my gut feeling is that if the simpler case of “required: Subsumer<U>,Subsumer<U>” was checked before the more complicated case of “required: Subsumer<U>,Subsumer<? super U>” this could result in faster compile times as well.
--
Have a nice day,
Timo.
Sent from Windows Mail
More information about the lambda-dev
mailing list