United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 6559182 Cast from a raw type with non-generic supertype to a raw type fails unexpectedly
6559182 : Cast from a raw type with non-generic supertype to a raw type fails unexpectedly

Details
Type:
Bug
Submit Date:
2007-05-18
Status:
Closed
Updated Date:
2012-03-22
Project Name:
JDK
Resolved Date:
2011-05-17
Component:
tools
OS:
generic
Sub-Component:
javac
CPU:
generic
Priority:
P5
Resolution:
Fixed
Affected Versions:
6
Fixed Versions:
7

Related Reports
Duplicate:

Sub Tasks

Description
interface Super<P> {}
class Y<A> implements Super<Integer>{}
interface X<A> extends Super<Double>{}
class S<L> extends Y<Object> {}
interface T<L> extends X<Object>{}

public class Test{
    public static void main(String argv[]) {
        S s = null; // same if I use S<Byte>
        T t = null; // same if I use T<Byte>
        t = (T) s;
    }
}

This should not fail.  T is raw in the cast.
Compile-time error:

interface Super<P> {}
class Y<A> implements Super<Integer>{}
interface X<A> extends Super<Double>{}
class S extends Y<Object> {}
interface T extends X<Object>{}

public class Test{
    public static void main(String argv[]) {
        S s = null;
        T t = null;
        t = (T) s;
    }
}

Yes.  Distinct supertypes: Super<Integer> and Super<Double>.
Compile-time error:

interface Super<P> {}
class Y implements Super<Integer>{}
interface X extends Super<Double>{}
class S<L> extends Y {}
interface T<L> extends X{}

public class Test{
    public static void main(String argv[]) {
        S s = null; // also if I use S<Byte>
        T t = null; // also if I use T<Byte>
        t = (T) s;
    }
}

Looks like a bug.  The program should compile because you cast to the  
*raw* type T.
interface Super<P> {}
class Y<A> implements Super<Integer>{}
interface X<A> extends Super<Double>{}
class S extends Y {}
interface T extends X{}

public class Test{

    public static void main(String argv[]) {
        S s = null;
        T t = null;
        t = (T) s;
    }
}
interface Super<P> {}
class Y implements Super<Integer>{}
interface X extends Super<Double>{}
class S extends Y {}
interface T extends X{}

public class Test{
    public static void main(String argv[]) {
        S s = null;
        T t = null;
        t = (T) s;
    }
}

                                    

Comments
SUGGESTED FIX

see http://sa.sfbay.sun.com/projects/langtools_data/7/6559182/
                                     
2008-03-11
SUGGESTED FIX

Since javac resolves supertypes in a lazy fashion, the only way to fix this bag is to mark a supertype X of a given raw type in such a way that when javac needs to compute X's supertype, the erased version of such supertype is used.

The behavior should be as follows:

the supertype of T is the erasure of T's supertype if one of the following holds

a) T is a raw type
b) T is a supertype of a raw type R

However, detecting b) is not trivial, since when we are computing T's supertype we don't know anything about R (R is a *subtype* of T, so it cannot be accessed by simply browsing T). This is why we need to keep this information in a variable of ClassType.
                                     
2008-03-11
EVALUATION

Let's consider only example 3 (which is clearly buggy. In that example, T<L> has the following supertypes:

*) T<L>
*) X
*) Super<Double>

while S<L> has the following supertypes

*) S<L>
*) Y
*) Super<Integer>

In our example we have a cast from a type S (raw) to a type T (raw). Since the target type is an interface, the rule to be used for cast is the folowing (from JLS 5.5):

"If [source] is a class type:
    [...]
    * If [target] is an interface type:
          o If [source] is not a final class (??8.1.1), then, if there exists a supertype X of 	[target], and a supertype Y of [source], such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if [source] does not implement [target], a subclass of S might)." 

At first it may seems that a compiler error should be raised since there are indeed two provably distinct supertypes of T<L> and S<L>, namely Super<Double> and Super<Integer>. But in the example the target type is a raw type so JLS 4.8 applies

"The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of its parameterized invocations."

The supertypes of the target type T can be rewritten as follows:

*) T
*) X
*) Super

it can be seen how, after we pick the correct supertypes for the raw T, the conflict between Super<Integer> and Super<Double> simply disappear. So this code should indeed compile.

The problem with this code is due to the fact that javac exploits a lazy scheme for supertypes resolution. The decision whether the supertype should be erased or not is based upon the current type being a raw type or not. So javac thinks the correct supertype list for the raw T is:

*) T
*) X (erasure of X is X)
*) Super<Double> (no erasure here since we are computing the supertype of X that is not raw)!
                                     
2008-02-26



Hardware and Software, Engineered to Work Together