JLS states (4.10.2):
"Let C be a type declaration (§4.12.6, §8.1, §9.1) with zero or more type parameters (§4.4) F1, ..., Fn which have corresponding bounds B1, ..., Bn. That type declaration defines a set of parameterized types (§4.5) C2 <T1,...,Tn>, where each argument type Ti ranges over all types that are subtypes of all types listed in the corresponding bound. That is, for each bound type Si in Bi, Ti is a subtype of Si[ F1 := T1, ..., Fn := Tn]."
So, given a generic type declaration C<U1 extends B1, U2 extends B2 ... Un extends Bn> a type C<T1, T2 ... Tn> is well formed if and only if T1 <: B1, T2 <: B2 ... Tn <: Bn.
In the example we have that the compiler crashes when checking the well-formedness of the type:
MyClass<? extends MyClass<?, ?>, ? extends MyClass<?, ?>>
wrt the type declaration:
class MyClass<T extends S, S>
This means that the compiler is trying to execute the following subtyping tests:
? extends MyClass<?,?> <: T && ? extends MyClass<?,?> <: S
and also (because of recursion in well-formedness checking)
? <: T && ? <: S
Those type test are not correct and cannot be proved in terms of JLS subtyping rules. This is why the compiler complains about an unexpected wildacrd type in a subtyping test and exits suddenly.
I suggest to replace the sentence in 4.10.2 "That is, for each bound type Si in Bi, Ti is a subtype of Si[ F1 := T1, ..., Fn := Tn]." with the following:
"That is, for each bound type Si in Bi, Ki is a subtype of Si[ F1 := K1, ..., Fn := Kn], where K is the result of capture conversion applied to T."
By applying a capture conversion before checking for conformance wrt declared bounds, we smoothly extends the spec in order to cope with complex wildcard bounds as the one showed in the exammple. The above subtyping test would be rewritten as follows;
#CAP1, #CAP1 <: MyClass<?,?> <: T && #CAP2, #CAP2 <: MyClass<?,?> <: S
#CAP3 <: T && #CAP4 <: S
which are valid subtyping tests.
Posted Date : 2008-02-11 13:10:42.0
JLS 4.5 already requires the instantiation of a generic type to respect the bounds of the generic type, using capture conversion if necessary:
"Let P = G<T1, ..., Tn> be a parameterized type.
It must be the case that,
after P is subjected to capture conversion (§5.1.10) resulting in the type G<X1,..., Xn>,
for each actual type argument Xi, Xi <: Bi[A1 := X1, ..., An := Xn] (§4.10),
or a compile time error occurs."
To check well-formedness of parameterized type P:
MyClass<? extends MyClass<?, ?>, ? extends MyClass<?, ?>>
given the declaration
class MyClass<T extends S, S>
requires the compiler to first capture-convert P by 4.5:
MyClass<cap1-of-? extends MyClass<?,?>, cap2-of-? extends MyClass<?,?>>
and then check
cap1-of-? extends MyClass<?,?> <: S
cap2-of-? extends MyClass<?,?> <: Object
JLS 4.10.2 ought to say "type argument Ti" rather than "argument type Ti". More importantly, 4.10.2 should mirror 4.5 in requiring capture conversion:
"That type declaration defines a set of parameterized types C<T1,...,Tn>,
where each ***type argument Ti, after capture conversion,***
ranges over all types that are subtypes of all types listed in the corresponding bound.
That is, for each bound type Si in Bi,
***Ki is a subtype of Si[F1:=K1, ..., Fn:=Kn], where Ki is the result of capture conversion applied to Ti.***"
Happily, capture conversion is mentioned later in 4.10.2 when checking if a parameterized type with wildcard type arguments is a subtype of some other parameterized type:
"The direct supertypes of the type C<R1,...,Rn>,
where at least one of the Ri is a wildcard type argument,
are the direct supertypes of C<X1,...,Xn>,
where C<X1,...,Xn> is the result of applying capture conversion (§5.1.10) to C<R1,...,Rn>."
(Note this is a different check than the one required by 4.5.)
Posted Date : 2008-03-11 00:21:43.0
so this is definitively a javac bug. Capture conversion should be applied to actual parameter types before checking for conformance wrt their declared bounds.
Posted Date : 2008-02-12 10:07:12.0
Unfortunately fixing this will break the regression tests regarding CRs 4916650, 5097548 (2 tests) and 6213818. This is due to the fact that the behavior described in JLS 4.5 is stricter than the one of javac. I found also that the Eclipse compiler accept the above programs, too. What javac is doing wrt to parameterized type well-formedness can be described as follows (see also the GenericForum post by Neal --- http://forum.java.sun.com/thread.jspa?threadID=431073&forumID=316)
Given the class declaration C<X extends B>
1) C<S> is legal if S <: B, where Y is the declared bound (as JLS 4.5)
2) C<? extends S> is legal, where S is castable to B
3) C<? super S> is legal, unless S cannot be a subtype of B.
Inside the compiler this is reflected by different method being invoked when checking for different cases:
This bug cannot be fixed until JLS investigate which solution is worth pursuing:
1) Scenario A: the relation explained in 4.5 can indeed be loosened so that code in aformentioned CRs can be "officially" accepted by javac.
2) Scenario B: 4.5 is left unchanged and the compiler implementation should react accordingly by rejecting all the above test cases. In this case please describe how this choice affects compatibility.
Posted Date : 2008-02-25 10:26:07.0
In the previous Evaluation, cases (2) and (3) express that the type argument of the parameterized type is within the space of possible type arguments given the bound in the generic type declaration.
It is sadly not possible to speak of the generic type's type variable "containing" the parameterized type's type argument, since containment is strictly a relation between pairs of type arguments, not between a type variable and a type argument.
Given the lack of formal containment, javac is doing its best. It may be appropriate for JLS 4.5 to generalize Xi<:Bi to "Xi can be cast to Bi". (It is very convenient that casting knows how to handle a type variable resulting from capture conversion.)
Posted Date : 2008-02-27 08:23:38.0
After a deep evaluation, it turned out that the behavior of javac is far more subtle than what it seemed at first. Given the class declaration
MyClass<X, Y extends X>
When javac checks the correctness of the type MyClass<?,?> the following steps are involved:
STEP1 - new type-variables are created in which each occurence of former type-variables are replaced with actual types. This means that two type variables are generated here, namely X' and Y'. While ub(X') = Object as one would expect, it is interesting to notice that ub(Y') = ?. This is due to javac substituting the actual type (?) for X within the bound of Y.
NOTE: this only applies when checking MyClass<?,?>. If different type arguments were given (e.g. MyClass<? extends Object,? extends Object>) javac would have fallen back to replacing formal X with ub(? extends Object) = Object. This can be easily shown by running the example in 'description' and replacing each occurrence of MyClass<?,?> with MyClass<? extends Object,? extends Object> --- no crash would occurr.
STEP2 - each newly created type variable is set upon its corresponding actual (this is achieved by calling the method withTypeVar on the type of the actual --- this method will store the type variable into the 'bound' field of the WildcardType upond which the method has been called).
STEP3 - The bounds of these newly created type-variable are then used for checking type well-formedness (see the comment above). In particular, actuals are checked against the bound of their corresponding type-variable (so ? is checked against ub(X') and ? is checked against ub(Y')).
It's very important to notice that this situation does not constitute an error by itself (even if javac is wrongly dealing with a type variable whose upper bound happens to be a wildcard). In fact if we only write:
MyClass<?,?> m = null;
javac will accept the code without problems. However, as a result of STEP2, we have that a type variable with a wildcard bound has been set on the 'bound' field of the second unbounded wildcard type argument. So the bug is already there, but you can't see it!
If we want to reproduce the bug we need to make javac to exploit that buggy wildcard type argument in some way: a possibility is the one shown in 'description':
MyClass<? extends MyClass<?,?>, ? extends MyClass<?,?>>
When javac then checks that the first '? extends MyClass<?,?>' conforms to its corresponding bound, notice that the 'MyClass<?,?>' term has already been type-checked, thus leading to the buggy situation described above. So when javac goes on and apply again STEP1, STEP2 and STEP3, it ends up with a very odd check to carry out:
? <= ? extends ?
where the type argument '? extends ?' has been (uncorrectly) generated by javac by the method Types.rewriteQuantifiers (this method is exploited by Types.isCastable for determining if a cast conversion between two given types exists).
But the thing that make javac crash is the fact that the unbounded wildcard on the left of the type containment test as the aformentioned javac-generated type-variable Y' stored in its 'bound' field. This will cause the types.upperBound to retrieve that hidden '?" bound, so that the above containment test is rewritten as follows:
? <: Object
At this point javac crashes because a subtyping test between a wildcard and a type is detected as an erroneous situation (and it seems indeed quite sensible to do so).
See 'Suggested Fix' for a detailed discussion on how to solve this bug.
Posted Date : 2008-02-28 12:21:30.0