EVALUATION
This fix requires a minor change to the subtyping between intersection types. I filed the spec rfe 6718388 for this.
|
|
|
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 4.5.1.1)
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
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
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
Looks like a bug in the cast implementation.
|
|
|