JDK-8039214 : Inference should not map capture variables to their upper bounds
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 9
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2014-04-03
  • Updated: 2017-05-17
  • Resolved: 2015-02-21
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 9
9 b53Fixed
Related Reports
Blocks :  
Blocks :  
Blocks :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Sub Tasks
JDK-8173583 :  
Description
When Types.containsType encounters a capture variable, testing for containment by an upper-bounded wildcard, it tests that the capture variable's upper bound is a subtype of the wildcard bound.  This leads to incorrect inference results.

E.g.,

[CAP extends String] contained by [? extends alpha]
becomes
String <: alpha

Here's an example that should compile but does not:

  interface I<X1,X2> {}
  class C<T> implements I<T,T> {}

  <X> void m(I<? extends X, X> arg) {}

  void test(C<?> arg) {
    m(arg);
  }

Here's an example that should not compile, but does (see also JDK-8039210):

  static class D<T> {
    void inject(T arg) {}
    static <T> D<T> make(Class<? extends T> c) { return new D<T>(); }
  }

  void test(Object o) {
    D.make(o.getClass()).inject(o);
  }

javac behavior appears to be the same in 6, 7, and 8.

The offending implementation code is in 'visitWildcardType', which maps 's' to its capture var upper bound.

IMPORTANT: fixing this bug will break the build of jax-ws.  So JDK-8039210 must be resolved first.
Comments
Release notes suggested text: The javac compiler's behavior when handling wildcards and "capture" type variables has been improved for conformance to the language specification. This improves type checking behavior in certain unusual circumstances. It is also a source-incompatible change: certain uses of wildcards that have compiled in the past may fail to compile because of a program's reliance on the javac bug.
09-02-2015

Status update: this bug will be fixed in 9. Some workarounds have been applied to 8u. This can be treated as the "master bug" for all related issues.
12-01-2015

An unexpected impact of the fix is to cause most-specific testing failures. This is because most-specific currently works by capturing the left type, then testing subtyping against the right type. If the right type has inference variables, after the fix for this bug they may be instantiated with a new capture variable. Then inference will try to verify that the type on the left really is a subtype of the instantiated type on the right, produce a _different_ capture variable, and fail. Solution is to stop performing capture when doing the most-specific test. This exposes the test to the problems of JDK-8016196 (inference should use capture to find supertypes, but not allow those capture variables to leak into the surrounding context), but that bug needs to be addressed separately. In the mean time, a program that depends on the old capture behavior would be unusual.
06-06-2014

Another regression from the Netbeans code base, below. This fails after the JDK-8033718 patch. public class NetbeansWildcards { public static void test(Box<String> b) { foo(bar(b)); } private static <X> Box<? extends X> foo(Box<? extends X> ts) { return null; } public static <Y> Box<? extends Y> bar(Box<? extends Y> language) { return null; } interface Box<T> {} }
14-05-2014

Here's another failure that's traced to the same problem. This one is a regression starting with 8. --- import java.util.List; abstract class Test { abstract <T> List<T> copyOf(List<? extends T> lx); abstract <E> List<E> filter(List<E> lx); // This works: <U> void f(List<U> lx) { copyOf(filter(lx)); } // This doesn't: void g(List<?> lx) { copyOf(filter(lx)); } } --- Reported by Liam Miller-Cushon to compiler-dev.
09-05-2014