JDK-8172222 : javac does not verify generics usage correctly when wrapped in an additional type
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 7,8,9
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • CPU: x86
  • Submitted: 2017-01-03
  • Updated: 2019-08-02
  • Resolved: 2017-01-04
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.10586]

A DESCRIPTION OF THE PROBLEM :
This code gives a compiler error:

import java.util.List;
import java.util.function.Function;
 
class Test {
    interface Mapper<T, U extends MapperProvider<U>> extends Function<T, U> {}
 
    interface MapperProvider<V> {
    	Mapper<V, ?> provide();
    }
 
    private <V extends MapperProvider<V>> void use(V c) {
    	use2(c.provide().apply(c));
    }
 
    private <W extends MapperProvider<W>> void use2(W c) {
    }
}


When I wrap U and W in a List<>, the code compiles without error:


import java.util.List;
import java.util.function.Function;
 
class Test {
    interface Mapper<T, U extends MapperProvider<U>> extends Function<T, List<U>> {}
 
    interface MapperProvider<V> {
    	Mapper<V, ?> provide();
    }
 
    private <V extends MapperProvider<V>> void use(V c) {
    	use2(c.provide().apply(c));
    }
 
    private <W extends MapperProvider<W>> void use2(List<W> c) {
    }
}

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile each of the code samples.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Both code samples should yield the same compiler status.
ACTUAL -
Different compiler status.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Teust.java:12: error: method use2 in class Test cannot be applied to given types;
    	use2(c.provide().apply(c));
    	^
  required: W
  found: CAP#1
  reason: inference variable W has incompatible bounds
    equality constraints: CAP#1
    lower bounds: MapperProvider<CAP#1>
  where W is a type-variable:
    W extends MapperProvider<W> declared in method <W>use2(W)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends MapperProvider<CAP#1> from capture of ?
1 error

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.util.List;
import java.util.function.Function;
 
class Test {
    interface Mapper<T, U extends MapperProvider<U>> extends Function<T, U> {}
 
    interface MapperProvider<V> {
    	Mapper<V, ?> provide();
    }
 
    private <V extends MapperProvider<V>> void use(V c) {
    	use2(c.provide().apply(c));
    }
 
    private <W extends MapperProvider<W>> void use2(W c) {
    }
}





import java.util.List;
import java.util.function.Function;
 
class Test {
    interface Mapper<T, U extends MapperProvider<U>> extends Function<T, List<U>> {}
 
    interface MapperProvider<V> {
    	Mapper<V, ?> provide();
    }
 
    private <V extends MapperProvider<V>> void use(V c) {
    	use2(c.provide().apply(c));
    }
 
    private <W extends MapperProvider<W>> void use2(List<W> c) {
    }
}
---------- END SOURCE ----------


Comments
Both variants should compile, but the first is rejected because of a long standing compiler bug.
04-01-2017

This is a duplicate of JDK-6391995. A long standing behavior of javac is to compute upper bounds of actual argument types ahead of performing the overload resolution check. This behavior, while generally harmless, can sometimes affect the outcome of inference. In this case, c.provide() has type Mapper<V, ?> - then, c.provide().apply(c) has type #CAP, where #CAP <: MapperProvider<#CAP>. Now, a correct derivation would set up a lower bound for W as follows: W :> #CAP But instead, javac computes the upper bound of #CAP ahead of determining the lower bound - hence: W :> upper(#CAP) = MapperProvider<#CAP> This leads to an inconsistency: from the upper bound, and the above lower, we can derive that: MapperProvider<#CAP> <: MapperProvider<U> (a lower bound must be smaller than an upper bound) --> U == #CAP This new equality constraint is incompatible with the previous lower bound: MapperProvider<#CAP> <: #CAP --> false Hence the error. If javac did not lift #CAP to its upper bound ahead of resolution, the lower bound would have been #CAP, which would have satisfied the compiler. #CAP <: MapperProvider<W> --> u(#CAP) = MapperProvider<#CAP> <: MapperProvider<W> --> W = #CAP
04-01-2017

Eclipse handles properly and there is no compilation error. Javac shows compiler error on 7,8 and 9 == -sh-4.1$ /opt/java/jdk-9_ea-149/bin/javac Test.java Test.java:12: error: method use2 in class Test cannot be applied to given types; use2(c.provide().apply(c)); ^ required: W found: CAP#1 reason: inference variable W has incompatible bounds equality constraints: CAP#2 lower bounds: MapperProvider<CAP#2> where W is a type-variable: W extends MapperProvider<W> declared in method <W>use2(W) where CAP#1,CAP#2 are fresh type-variables: CAP#1 extends MapperProvider<CAP#1> from capture of ? CAP#2 extends MapperProvider<CAP#2> from capture of ? 1 error ==
04-01-2017