-
Bug
-
Resolution: Unresolved
-
P4
-
9
Messages reporting incompatible bounds contain a lot of extraneous details that can be cleaned up. JDK-8067883 helped by avoiding the messages entirely in certain situations, but we can do a better job of presenting useful information when we do end up using the messages.
Test:
interface I<T> {
<S> Class<S> m(T arg, Class<? extends S> c1 ,Class<? extends S> c2, Class<S> c3);
}
static <T> Class<T> anyClass() { return null; }
void m(I<Double> i) {
Class<? extends String> c = i.m(23.0, Number.class, anyClass(), Object.class, anyClass());
}
Output:
error: incompatible types: inference variable S has incompatible bounds
Class<? extends String> c = i.m(23.0, Number.class, anyClass(), Object.class, anyClass());
^
upper bounds: String,Object
lower bounds: T#2,Number
where S,T#1,T#2 are type-variables:
S extends Object declared in method <S>m(T#1,Class<? extends S>,Class<? extends S>,Class<S>,Class<S>)
T#1 extends Object declared in interface I
T#2 extends Object declared in method <T#2>anyClass()
---
Some observations:
- "Object" as an upper bound is trivially redundant: we already have upper bound String. (Similar could be said for redundant lower bounds, too -- and also maybe where an eq bound is a subtype of a certain upper/lower bound, but at that point maybe we're masking useful information...)
- T#1 is not relevant, and shouldn't even appear. The "declared in method" clause should perhaps present the signature *after* substitution of class type variables?
- There is, for some reason, no mention of the equality constraints coming from the 3rd and 4th arguments: S=Object and S=T#3
- If we did present the equality constraints, it would be pointless to mention T#3 -- any bounds on T#3 have been propagated to S. Such ivars only add noise (a lot of it, since each one gets its own "where..." sentence).
- Similarly, it's not useful to talk about T#2 as a lower bound, because it doesn't contribute to the "incompatible bounds" problem. Generally, I don't think bare type variables belong here, either, although there may be some caveats (solving multiple vars at once, legacy inference algorithm, ...)
---
Proposed error message:
error: incompatible types: inference variable S has incompatible bounds
Class<? extends String> c = i.m(23.0, Number.class, anyClass(), Object.class, anyClass());
^
equal bounds: Object
upper bounds: String
lower bounds: Number
where S is a type-variable:
S extends Object declared in method <S>m(Double,Class<? extends S>,Class<? extends S>,Class<S>,Class<S>)
(Or, in a variation that doesn't have the eq bound, report upper bound String and lower bound Number.)
Test:
interface I<T> {
<S> Class<S> m(T arg, Class<? extends S> c1 ,Class<? extends S> c2, Class<S> c3);
}
static <T> Class<T> anyClass() { return null; }
void m(I<Double> i) {
Class<? extends String> c = i.m(23.0, Number.class, anyClass(), Object.class, anyClass());
}
Output:
error: incompatible types: inference variable S has incompatible bounds
Class<? extends String> c = i.m(23.0, Number.class, anyClass(), Object.class, anyClass());
^
upper bounds: String,Object
lower bounds: T#2,Number
where S,T#1,T#2 are type-variables:
S extends Object declared in method <S>m(T#1,Class<? extends S>,Class<? extends S>,Class<S>,Class<S>)
T#1 extends Object declared in interface I
T#2 extends Object declared in method <T#2>anyClass()
---
Some observations:
- "Object" as an upper bound is trivially redundant: we already have upper bound String. (Similar could be said for redundant lower bounds, too -- and also maybe where an eq bound is a subtype of a certain upper/lower bound, but at that point maybe we're masking useful information...)
- T#1 is not relevant, and shouldn't even appear. The "declared in method" clause should perhaps present the signature *after* substitution of class type variables?
- There is, for some reason, no mention of the equality constraints coming from the 3rd and 4th arguments: S=Object and S=T#3
- If we did present the equality constraints, it would be pointless to mention T#3 -- any bounds on T#3 have been propagated to S. Such ivars only add noise (a lot of it, since each one gets its own "where..." sentence).
- Similarly, it's not useful to talk about T#2 as a lower bound, because it doesn't contribute to the "incompatible bounds" problem. Generally, I don't think bare type variables belong here, either, although there may be some caveats (solving multiple vars at once, legacy inference algorithm, ...)
---
Proposed error message:
error: incompatible types: inference variable S has incompatible bounds
Class<? extends String> c = i.m(23.0, Number.class, anyClass(), Object.class, anyClass());
^
equal bounds: Object
upper bounds: String
lower bounds: Number
where S is a type-variable:
S extends Object declared in method <S>m(Double,Class<? extends S>,Class<? extends S>,Class<S>,Class<S>)
(Or, in a variation that doesn't have the eq bound, report upper bound String and lower bound Number.)