JDK-6548436 : Incorrect inconvertible types error
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6,6u14
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,linux,windows_xp
  • CPU: generic,x86
  • Submitted: 2007-04-20
  • Updated: 2011-05-18
  • Resolved: 2011-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 Other
6u16-revFixed 7 b40Fixed OpenJDK6Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)

Linux lotp-laptop 2.6.20-15-generic #2 SMP Sun Apr 15 07:36:31 UTC 2007 i686 GNU

In some cases javac outputs errors about inconvertible types. The circumstances in which this happens seem to be rather complicated. Trying to reduce the problem to a canonical form eradicates it.

Full discussion can be found at http://forum.java.sun.com/thread.jspa?threadID=5162196&tstart=0

Try to compile the supplied source code example.

Should compile without errors
javac outputs the following errors:

javac Bug.java
  Bug.java:27: inconvertible types
found   : JETEvent<capture#608 of ?>
required: FSDEvent
                if(event == null || ! (event instanceof FSDEvent)) { // <-- doesn't compile
  Bug.java:30: inconvertible types
found   : JETEvent<capture#870 of ?>
required: FSDEvent
                FSDEvent fsdEvent = (FSDEvent)event;
2 errors

This bug can be reproduced always.

---------- BEGIN SOURCE ----------

abstract class JETEvent<E extends Enum<E>> {}
class FSDEvent extends JETEvent<FSDEvent.SUBTYPE> {
    public enum SUBTYPE {
interface State<T extends Task> {
    public Transition<T> getTransition(JETEvent<?> event);
class DefaultState<T extends Task> implements State<T> {
    public Transition<T> getTransition(JETEvent<?> event) {
    	return null;
class FSDState extends DefaultState<FSDTask> {
	public FSDTransition getTransition(JETEvent<?> event) {
		if(event == null || ! (event instanceof FSDEvent)) { // <-- doesn't compile
			return null;
		FSDEvent fsdEvent = (FSDEvent)event; // <-- doesn't compile either
		return null;
interface Task<T extends Task> {}
class FSDTask extends DefaultTask<FSDTask> {}
class DefaultTask<T extends DefaultTask> implements Task<T> {}
interface Transition<T extends Task> {}
class DefaultTransition<T extends Task> implements Transition<T> {}
class FSDTransition extends DefaultTransition<FSDTask> {}
---------- END SOURCE ----------

Casting to Object first solves the problem. The two problematic lines must be

if(event == null || ! ((Object)event instanceof FSDEvent)) { // <-- This compiles
FSDEvent fsdEvent = (FSDEvent)(Object)event; // <-- This now also compiles

for the compilation to work

Release Regression From : 5.0u11
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Release Regression From : 5.0u11
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

EVALUATION This fix requires a minor change to the subtyping between intersection types. I filed the spec rfe 6718388 for this.

EVALUATION Note that this problems might seem to be caused by an unnecessary capture conversion being applied to the expression of a cast (if instead of Base<#Cap> we'd have simply Base<?> things would have been much more easier!). We could in principle remove the redundant capture conversion when attributing the target expression of a cast, but we'd still have problems in a more general example as the following: class Base<E extends Comparable<E>> { Base<E> base; void m(Base<?> je) { Object o = (Base<Integer>)je.base; } } Here we have that the 'je' MUST be capture converted before the field access (accordingly to the JLS). This results in accessing the 'base' field on an instance of type Base<#Cap> and, since 'base' is again of type Base<E>, we end up in the exactly same erroeous behavior we described above.

EVALUATION Fixing this requires also adding some additional subtyping rules between intersection types. Given a classtype D and an intersection type S = C & I1 & I2 ... & In we say that D <: S iff D <: C, D <: I1, D <: I2 ... , D <: In This rule is needed e.g. to prove that: Integer <: Number & Comparable<Integer> in fact 1) Integer <: Number 2) Integer <: Comparable<Integer> Note however that the above rules are not part of the JLS (neither section 4.9 - intersection types - nor 4.10 - subtyping). Alex seems to agree with the above rules.

EVALUATION This problem is due to the function Types.notSoftSubtype exploiting a containment relation that is too strong for the purposes of a cast conversion. In fact, when the type Base<?> is cast converted to the type Base<Integer>, the method notSoftSubtypes is invoked (as part of the algorithm for proving distinctess of type arguments) on the two type arguments namely ? and Integer. This method is in charge for answering this question: is it impossible that a given type X can ever be a subtype of another type Y? In this case: it is impossible that Integer is a subtype of #Cap, where ub(#Cap) === Comparable<#Cap> ? Following standard subtyping rules the answer is: yes, it's impossible, as: Integer <: ub(#Cap) = Comparable<#Cap> (the algorithm starts by computing ub of rhs) Comparable<Integer> <: Comparable<#Cap> Integer <= #Cap No, no type-containment defined for captured type variables, see JLS However this is not the right answer, as there is a possibility that types Integer and #Cap are indeed related (e.g. if #Cap happens to *be* Integer - which means that the type behind the wildcard that has been captured converted was an Integer). To solve this problem the containment relation to be used ONLY within notSoftSubtype should be weakened so that: Integer <= #Cap iff lb(#Cap) <: Integer <: ub(#Cap) in other words if Integer fit the bounds of the captured type-variable. Note that since #Cap has an fbound (Comparable<#Cap>) special handling is required, otherwise we end up in repeating the same test again and again as: Integer <= #Cap Integer <: ub(#Cap) Integer <: Comparable<#Cap> Comparabe<Integer> <: Comparable<#Cap> Integer <= #Cap //oops, back where we started In fact the correct formula is Integer <: #Cap iff [Integer/#Cap]lb(#Cap) <: Integer <: [Integer/#Cap]ub(#Cap) where the notation [Integer/#Cap]T means that we have to replace each occurrence of #Cap with Integer in the type T.

SUGGESTED FIX A webrev of this fix is available at the following URL http://hg.openjdk.java.net/jdk7/tl/langtools/rev/433ee48257c0

SUGGESTED FIX An interesting idea would be to make the subtyping relation exploited within Types.java parameterized in another TypeRelation representing the type-containment relation to be used within that given subtyping test. Thi solution would allow the code to be more readable and to maximize the reuse of existing subtyping and type-containment algorithms. Some care is required since when no explicit type-containment relation is given to the subtyping algorithm javac should default to the JLS relation (this way we *override* the behaviour of subtyping only when we want to, e.g. during cast).

EVALUATION Looks like a bug in the cast implementation.