JDK-6450290 : Capture of nested wildcards causes type error
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2006-07-19
  • 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 b27Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-b03)
Java HotSpot(TM) Client VM (build 1.5.0_07-b03, mixed mode, sharing)


A DESCRIPTION OF THE PROBLEM :
In the attached code the capture of ? extends Box<?> is not treated right, allowing you to put a Box<B> in a Box<Box<A>>.
The code compiles without warnings, has no casts, but throws a ClassCastException.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile (and run) the code

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
An error from the compiler
ACTUAL -
Incorrect code compiles, and throws a ClassCastException

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.ClassCastException: B
        at CaptureBug6.main(CaptureBug6.java:9)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// this code compiles without warnings, has no casts,
// but throws a ClassCastException
public class CaptureBug6 {
	public static void main(String[] args) {
		Box<Box<A,A	>,Box<A,A>> a = new Box<Box<A,A>,Box<A,A>>(new Box<A,A>(new 
A()));
		Box<?, ?> b = a;		
		b.value.same = new Box<B,B>(new B());
		A c = a.value.same.value;
	}
}

class Box<X extends Box<?,?>, T extends X> {
	Box (T v) {
		value = v;
	}

	Box () {}
		
	T value;
	Box<X, T> same;

}

class A extends Box<A,A> {}
class B extends Box<B,B> {}


---------- END SOURCE ----------

Comments
SUGGESTED FIX Note: one might notice that we could have used this line site = types.upperBound(site). since the behaviour of types.upperBound() is exactly to walk recursively into the bounds of a given type finding the first non-type variable type. Unfortunately the implementation of this method needs to distinguish between (i) captured type-variables and (ii) plain type-variables. In (i) the captured type-variable's bound is walked recursively. In (ii) the type-variable itself is returned. This is due to the way in which javac performs subtyping so changing this dicotomy would propagate almost everywhere! It's thus desirable to leave the implementation of types.upperBound() unchanged and to exploit a while loop that performs exactly the same steps we require (looking for the first non type-variable bound of a given type variable).
09-04-2008

EVALUATION This is a javac bug. What is the type of the expression b.value.same where the type of B is Box<?,?> ? Let's do one step at time. We should compute the type of b.value first, but in order to do this, we should first capture the type of b. This lead to the type Box<X,Y>, where the upperbound ub(X) = Box<?,?> and where ub(Y) = X, where both X and Y are captured type-variables. Since the declared type of b.value is T, it turns out that the actual type of b.value is Y. So our problem is now to determine the type of the field 'same' of Box, where the site symbol is Y. It's important to notice that when the site symbol is a type variable, javac assumes the site symbol to be the upper bound of the type variable. In this case, this means that javac looks for the field 'some' within the type ub(Y) = X. Here's the problem, since the site symbol is still a type variable, capture conversion of the site symbol has no effect here and javac ends up in looking for 'same' again into the type ub(X) = B<?,?> and not into the type capture(ub(X)) == capture(B<?,?>) as it should be. This lead to javac uncorrectly assume that the site symbol is indeed of type B<?,?>, so when the field 'same' is accessed, this field is of type B<?,?> (the same type as the site). This makes the assignment b.value.some = new Box<B,B>() to be wrongly marked as correct, since what the compiler is actually checking is: Box<B,B> <: Box<?,?> (which is clearly accepted by the compiler) and not Box<B,B> <: capture(Box<?,?>) as it should be. The solution to this problem is to recursively walk the site symbol's upper bound until we found some type that is not itself a type variable and then apply capture conversion to that non-type variable type. In this case, when accessing 'some' in Y, javac should retrieve ub(Y) = X and then ub(X) = Box<?,?>, since X is itself a (captured) type-variable.
26-02-2008

SUGGESTED FIX see http://sa.sfbay.sun.com/projects/langtools_data/7/6450290/
26-02-2008