JDK-6665356 : Cast not allowed when both qualifying type and inner class are parameterized
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u10,7
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,linux
  • CPU: generic,x86
  • Submitted: 2008-02-20
  • Updated: 2012-01-13
  • Resolved: 2012-01-13
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 b44Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Description:
Compiler throws error for what seems to be a valid cast to a type in which both the qualifying type and inner class are parameterized. 

<code>
bash-3.00$ 
<code>
bash-3.00$ cat GenericOuter.java 
public class GenericOuter<S> {

    class GenericInner<T> extends LinkedList<T>{

    }
    public static void main(String...args){
       GenericOuter.GenericInner g1 = ( GenericOuter.GenericInner)new LinkedList<String>();
       GenericOuter<String>.GenericInner<String> g2 = ( GenericOuter<String>.GenericInner<String>)new LinkedList<String>(); //Compilation Error
    }

}


</code>
Compilation result is :
<output>
GenericOuter.java:8: ')' expected
       GenericOuter<String>.GenericInner<String> g2 = ( GenericOuter<String>.GenericInner<String>)new LinkedList<String>(); //Compilation Error
                                                                            ^
GenericOuter.java:8: ';' expected
       GenericOuter<String>.GenericInner<String> g2 = ( GenericOuter<String>.GenericInner<String>)new LinkedList<String>(); //Compilation Error
                                                                             ^
GenericOuter.java:8: illegal start of expression
       GenericOuter<String>.GenericInner<String> g2 = ( GenericOuter<String>.GenericInner<String>)new LinkedList<String>(); //Compilation Error

</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)

bash-3.00$ uname -a
SunOS hrajan 5.10 Generic sun4u sparc SUNW,Sun-Blade-100

Comments
SUGGESTED FIX 2) isCastable fix diff -r 9a66ca7c79fa src/share/classes/com/sun/tools/javac/code/Types.java --- a/src/share/classes/com/sun/tools/javac/code/Types.java Sat Dec 01 00:00:00 2007 +0000 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Fri Feb 29 18:01:44 2008 +0000 @@ -976,10 +976,10 @@ public class Types { if (highSub != null) { assert a.tsym == highSub.tsym && a.tsym == lowSub.tsym : a.tsym + " != " + highSub.tsym + " != " + lowSub.tsym; - if (!disjointTypes(aHigh.getTypeArguments(), highSub.getTypeArguments()) - && !disjointTypes(aHigh.getTypeArguments(), lowSub.getTypeArguments()) - && !disjointTypes(aLow.getTypeArguments(), highSub.getTypeArguments()) - && !disjointTypes(aLow.getTypeArguments(), lowSub.getTypeArguments())) { + if (!disjointTypes(aHigh, highSub) + && !disjointTypes(aHigh, lowSub) + && !disjointTypes(aLow, highSub) + && !disjointTypes(aLow, lowSub)) { if (upcast ? giveWarning(a, highSub) || giveWarning(a, lowSub) : giveWarning(highSub, a) || giveWarning(lowSub, a)) warnStack.head.warnUnchecked(); @@ -1073,6 +1073,16 @@ public class Types { } return false; } + + // <editor-fold defaultstate="collapsed" desc="disjointTypes"> + public boolean disjointTypes(Type t, Type s) { + while (t != Type.noType && s != Type.noType) { + if (disjointTypes(t.getTypeArguments(), s.getTypeArguments())) return true; + t = t.getEnclosingType(); + s = s.getEnclosingType(); + } + return false; + } /** * Two types or wildcards are considered disjoint if it can be @@ -1091,7 +1101,7 @@ public class Types { private Set<TypePair> cache = new HashSet<TypePair>(); - public Boolean visitType(Type t, Type s) { + public Boolean visitType(Type t, Type s) { if (s.tag == WILDCARD) return visit(s, t); else @@ -1190,6 +1200,7 @@ public class Types { s = upperBound(s); if (s.tag == TYPEVAR) s = s.getUpperBound(); + return !isSubtype(t, s); } // </editor-fold> @@ -2817,7 +2828,7 @@ public class Types { return erasure(t); // some "rare" type involved if (captured) - return new ClassType(cls.getEnclosingType(), S, cls.tsym); + return new ClassType(cls.getEnclosingType(), S, cls.tsym); else return t; } @@ -2920,7 +2931,12 @@ public class Types { private boolean giveWarning(Type from, Type to) { // To and from are (possibly different) parameterizations // of the same class or interface - return to.isParameterized() && !containsType(to.getTypeArguments(), from.getTypeArguments()); + while (to != Type.noType && from != Type.noType) { + if (to.isParameterized() && !containsType(to.getTypeArguments(), from.getTypeArguments())) return true; + to = to.getEnclosingType(); + from = from.getEnclosingType(); + } + return false; } private List<Type> superClosure(Type t, Type s) { diff -r 9a66ca7c79fa src/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Sat Dec 01 00:00:00 2007 +0000 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Fri Feb 29 18:01:44 2008 +0000 @@ -1714,6 +1714,7 @@ public class Attr extends JCTree.Visitor public void visitTypeCast(JCTypeCast tree) { Type clazztype = attribType(tree.clazz, env); + chk.validate(tree.clazz); Type exprtype = attribExpr(tree.expr, env, Infer.anyPoly); Type owntype = chk.checkCastable(tree.expr.pos(), exprtype, clazztype); if (exprtype.constValue() != null)
29-02-2008

EVALUATION I tried the code with Eclipse. It compiles without problems. Also, Eclipse seems to be able to deal with qualified generic cast as well. The behaviour seems to be X1.Y1 is castable to X2.Y2 if X1 is castable to Y1 && X2 is castable to Y2
29-02-2008

EVALUATION Waiting for JLS to specify how generic cast expressions involving nested types should be handled
25-02-2008

SUGGESTED FIX A webrev of this fix is available at the following URL http://hg.openjdk.java.net/jdk7/tl/langtools/rev/d57378c34fdb
21-02-2008

EVALUATION Note that we should also change the type-system in order to deal with generic qualified casts. E.g. the following statement should NOT compile: (Outer<String>.Inner<Integer>)new Outer<Integer>().new Inner<Integer>(); because no cast conversion is exists from Outer<Integer>.Inner<Integer> to Outer<String>.Inner<Integer>.
21-02-2008

EVALUATION This problem is due to the parser expecting type arguments only at the end of a given qualified identifier. So the following expressions are parsed without problems: Outer.Inner Outer.Inner<String> while the following expressions cannot be parsed Outer<String>.Inner Outer<String>.Inner<String> This behaviour is caused by the fact that when javac parses an identifier between parenthesis (which could indeed be the target type of a cast expression) the parsing of type arguments is temporarily switched off, so that parsing the type "Outer<String>.Inner" results in parsing only the identifier "Outer". At this point a patch in the compiler tries to recover the actual type arguments, thus attaching "<String>" to the previously parsed "Outer" but then parsing fails because a closed parenthesis ")" is expected while we still have to finish parsing the entire select (the next token is a "."). Since we cannot change the parser so to enable type arguments parsing within cast expression (this would lead to ambiguity wrt the "<" binary operator in a parenthesized expression) we should extend the patch so to deal with qualified identifiers as well. As a result we have to delay the consumption of the ")" token until all the "."-leading tokens have been parsed (possibly along with their type arguments). This solution doesn't introduce any ambiguity in the parser as if we already have parsed something of the kind "(" Ident "<" TypeArguments ">" at is point is safe to assume that we are indeed parsing a type cast expression and not some kind of parenthesized expression like "boolean b = (a < b && c > b)" (note that "b && c" cannot be consumed by TypeArguments).
21-02-2008