JDK-6651719 : Compiler crashes possibly during forward reference of TypeParameter
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6,7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,windows_xp
  • CPU: generic,x86
  • Submitted: 2008-01-17
  • 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 7
7 b33Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Description:
Compiler crashes with an Assertion error for the following code:
<code>
class TestClass {

    public static void main(String... args) {
        MyClass<? extends MyClass<?, ?>, ? extends MyClass<?, ?>> gg = null;
    }
}
class MyClass<T extends S, S> {
}
</code>
Compilation result is :
<output>

Following is the Error message:
An exception has occurred in the compiler (1.7.0-ea). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report.  Thank you.
java.lang.AssertionError: isSubtype 15
        at com.sun.tools.javac.code.Types$5.visitType(Types.java:361)
        at com.sun.tools.javac.code.Types$5.visitType(Types.java:342)
        at com.sun.tools.javac.code.Types$DefaultTypeVisitor.visitWildcardType(Types.java:3185)
        at com.sun.tools.javac.code.Type$WildcardType.accept(Type.java:430)
        at com.sun.tools.javac.code.Types$DefaultTypeVisitor.visit(Types.java:3183)
        at com.sun.tools.javac.code.Types.isSubtype(Types.java:338)
        at com.sun.tools.javac.code.Types.isSubtypeNoCapture(Types.java:325)
        at com.sun.tools.javac.code.Types$8.visitWildcardType(Types.java:812)
        at com.sun.tools.javac.code.Types$8.visitWildcardType(Types.java:761)
        at com.sun.tools.javac.code.Type$WildcardType.accept(Type.java:430)
        at com.sun.tools.javac.code.Types$DefaultTypeVisitor.visit(Types.java:3183)
        at com.sun.tools.javac.code.Types.containsType(Types.java:758)
        at com.sun.tools.javac.code.Types.containsType(Types.java:725)
        at com.sun.tools.javac.code.Types.giveWarning(Types.java:2923)
        at com.sun.tools.javac.code.Types.access$300(Types.java:66)
        at com.sun.tools.javac.code.Types$9.visitClassType(Types.java:983)
        at com.sun.tools.javac.code.Types$9.visitClassType(Types.java:885)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:568)
        at com.sun.tools.javac.code.Types$DefaultTypeVisitor.visit(Types.java:3183)
        at com.sun.tools.javac.code.Types.isCastable(Types.java:876)
        at com.sun.tools.javac.comp.Check.checkExtends(Check.java:433)
        at com.sun.tools.javac.comp.Check.access$100(Check.java:55)
        at com.sun.tools.javac.comp.Check$Validator.visitTypeApply(Check.java:810)
        at com.sun.tools.javac.tree.JCTree$JCTypeApply.accept(JCTree.java:1840)
        at com.sun.tools.javac.comp.Check.validate(Check.java:745)
        at com.sun.tools.javac.comp.Attr.visitVarDef(Attr.java:705)
        at com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:709)
        at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:372)
        at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:409)
        at com.sun.tools.javac.comp.Attr.attribStats(Attr.java:425)
        at com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:761)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:765)
        at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:372)
        at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:409)
        at com.sun.tools.javac.comp.Attr.visitMethodDef(Attr.java:680)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:653)
        at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:372)
        at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:409)
        at com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:2740)
        at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:2666)
        at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:2602)
        at com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1050)
        at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:779)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:744)
        at com.sun.tools.javac.main.Main.compile(Main.java:386)
        at com.sun.tools.javac.main.Main.compile(Main.java:312)
        at com.sun.tools.javac.main.Main.compile(Main.java:303)
        at com.sun.tools.javac.Main.compile(Main.java:82)
        at com.sun.tools.javac.Main.main(Main.java:67)

</output>
<version>
/net/sqindia/export/disk09/jdk/7/latest/binaries/solsparc/bin/java -version
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b24)
Java HotSpot(TM) Client VM (build 12.0-b01, mixed mode)

uname -a
SunOS bonsai 5.11 snv_49 i86pc i386 i86pc
*** (#1 of 1): 2007-10-01 15:42:18 GMT+05:30 ###@###.###

Comments
SUGGESTED FIX A webrev of fix 2 is available at http://sa.sfbay.sun.com/projects/langtools_data/7/6651719/
28-02-2008

SUGGESTED FIX The fix reported here is no longer valid as we decided to go for fix 2)
28-02-2008

SUGGESTED FIX As mentioned in 'Evaluation', despite the fact that the JLS should prevent javac from generating type-variable with wildcard as upper-bound (because of capture conversion, see 4.5), as Alex pointed out, it is simply not viable at this stage to implement the JLS word by word, since applying capture conversion before checking for well-formedness makes the check too strict so that a lot of examples that compiles fine now would be rejected after applying such a fix. As a result of this, we have to fix this bug possibly preserving the existing behavior of javac. I came up with two different fixes: 1) Slightly change the code of the type-checker so that a type variable having an unbounded wildcard as upper bound is translated into a type variable having Object as upper bound. This is a very simple fix, that doesn't break compatibility but, at the same time, prevent the compiler to generate strange types as the ones described above. 2) Disabling the special treatment for unbounded wildcards so that when executing STEP1 (see above) *every* actual type being a wildcard is first replaced by its bound (Object if unbounded) and then that bound is replaced within the formal type-variable's bound. This fix would invalidate the regression test 6213818. For further details on this see the comments in 6213818.
28-02-2008

EVALUATION 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.
28-02-2008

EVALUATION 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.)
27-02-2008

EVALUATION 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: 1) Types.isSubtypeUnchecked(S,B) 2) Types.isCastable(S,B) 3) Types.notSoftSubtype(S,B) 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.
25-02-2008

EVALUATION Thanks Alex so this is definitively a javac bug. Capture conversion should be applied to actual parameter types before checking for conformance wrt their declared bounds.
12-02-2008

EVALUATION 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.)
11-02-2008

EVALUATION 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 and #CAP3 <: T && #CAP4 <: S which are valid subtyping tests.
11-02-2008