JDK-7126754 : Generics compilation failure casting List to List
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 7
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_vista
  • CPU: x86
  • Submitted: 2012-01-03
  • Updated: 2014-09-23
  • Resolved: 2012-04-20
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 8
8 b23Fixed
Description
FULL PRODUCT VERSION :
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.0.6002]

A DESCRIPTION OF THE PROBLEM :
In the following code snippet

List<Set<? extends String>> a = null;
List<? extends Set<? extends String>> b = a;   // 1

List<? extends Set<? extends String>> c = null;
List<Set<? extends String>> d = (List<Set<? extends String>>)c;  // 2

(1) compiles but the reverse (2) doesn't compile.

This example is due to Remi Forax, who reduced the original sample I had posted to http://mail.openjdk.java.net/pipermail/compiler-dev/2011-December/003894.html.

REGRESSION.  Last worked in version 6u29

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Attempt to compile the following class under Java 7:

import java.util.List;
import java.util.Set;

public class Demo {
  public static void main(String[] args) {
    List<Set<? extends String>> a = null;
    List<? extends Set<? extends String>> b = a;

    List<? extends Set<? extends String>> c = null;
    List<Set<? extends String>> d = (List<Set<? extends String>>)c;
  }
}

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Should compile.
ACTUAL -
Fails to compile with:

Demo.java:10: error: inconvertible types
    List<Set<? extends String>> d = (List<Set<? extends String>>)c;
                                                                 ^
  required: List<Set<? extends String>>
  found:    List<CAP#1>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Set<? extends String> from capture of ? extends Set<? extends
String>
1 error

ERROR MESSAGES/STACK TRACES THAT OCCUR :
See "actual result".

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.List;
import java.util.Set;

public class Demo {
  public static void main(String[] args) {
    List<Set<? extends String>> a = null;
    List<? extends Set<? extends String>> b = a;

    List<? extends Set<? extends String>> c = null;
    List<Set<? extends String>> d = (List<Set<? extends String>>)c;
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Perform the cast in a separate method, as in the following sample:

import java.util.List;
import java.util.Set;

public class Demo {
  public static void main(String[] args) {
    List<Set<? extends String>> a = null;
    List<? extends Set<? extends String>> b = a;

    List<? extends Set<? extends String>> c = null;
    List<Set<? extends String>> d = castToSpecific(c);
  }
  
  static <T> List<T> castToSpecific(List<? extends T> input) {
    return (List<T>) input;
  }
}

Comments
SUGGESTED FIX A webrev of this fix is available at the following URL: http://hg.openjdk.java.net/jdk8/tl/langtools/rev/70d92518063e
11-01-2012

EVALUATION The problem lies in the logic for write'rewriting' quantifiers (Types.rewriteQuantifiers) that was added back in JDK 6. This logic is meant to transform a type like: Foo<X>, where X extends Number to: Foo<? extends Number> This bug is caused by the fact that the routine for rewriting captured variables is recursively calling the visitor method for wildcard types. This meant that not only captured variables were rewritten - but wildcard types were rewritten too. This was too much and resulted in some incompatibilities as pointed out by this issue (because, as a result, the variance of the generic type changed). The solution is to separate the logic for captured-type variable rewriting from the one that rewrites wildcard types. In addition, there should be no wildcard rewriting if the bound of the wildcard type is not updated by the rewriting visitor (meaning it does not contain type-variables/captured vars).
11-01-2012