JDK-8146362 : Type inference fails with Function.identity() as params
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8u66
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: os_x
  • CPU: x86
  • Submitted: 2015-12-29
  • Updated: 2018-05-03
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.
Other
tbd_majorUnresolved
Related Reports
Blocks :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Darwin eno.local 15.2.0 Darwin Kernel Version 15.2.0: Fri Nov 13 19:56:56 PST 2015; root:xnu-3248.20.55~2/RELEASE_X86_64 x86_64


A DESCRIPTION OF THE PROBLEM :
Type inference fails for a generic method with Function.identity() provided as argument twice. Does not fail when lambda 'x -> x' is substituted for identity() instead.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
$ javac GenericsTest.java

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected source file to compile without errors.
ACTUAL -
Compilation fails.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Error:(17, 58) java: method toSortedMap in class com.pexlabs.util.GenericsTest cannot be applied to given types;
  required: java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends V>
  found: java.util.function.Function<java.lang.Object,java.lang.Object>,java.util.function.Function<java.lang.Object,java.lang.Object>
  reason: inferred type does not conform to upper bound(s)
    inferred: java.lang.Object
    upper bound(s): K,java.lang.Comparable<? super K>,java.lang.Object

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package x;

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collector;

public class GenericsTest {
    public static <T, K extends Comparable<? super K>, V>
    Collector<T, ?, Map<K, V>> toSortedMap(
            Function<? super T, ? extends K> keyMapper,
            Function<? super T, ? extends V> valueMapper) {
        return null;
    }

    public static void foo() {
        // *** Following statement does not compile ***
        Collector<Integer, ?, Map<Integer, Integer>> a = toSortedMap(Function.identity(), Function.identity());

        // However this statement compiles just fine
        Collector<Integer, ?, Map<Integer, Integer>> b = toSortedMap(i -> i, i -> i);
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use lambda 'x -> x' instead of Function.identity()


Comments
The problem with well-formedness of bounds is covered by JDK-8039222. Need a resolution to JLS before proceeding with a fix in javac.
27-05-2016

JDK-8134711 tracks the issue of equality constraints on fresh type variables.
27-05-2016

Problem with the 'makeClass' test case is that the specified fresh type variable strategy fails to account for equality constraints. Type variables that are equivalent can't be given different fresh type variables. As noted above, problem with the 'toSortedMap' test case is that a type variable with multiple other type variables as upper bounds is, potentially, malformed.
27-05-2016

Here's a simplified test that still fails: <K extends Comparable<K>> void m(Class<K> c) {} <Z> Class<Z> makeClass() { return null; } void test() { m(makeClass()); }
27-05-2016

In the lambda case, the program compiles, as inference is basically postponed until *after* applicability (as both lambdas are not pertinent to applicability - see 15.12.2.2) - after applicability the target type will be available, meaning Z1 = Integer and Z2 = Integer and this will unblock the whole inference machinery.
04-01-2016

This is indeed a bug - but a very subtle one, and one that has to do with the fact that JLS is a bit underspecified when it comes to glb/intersection types containing multiple type-variables. The above example can be rewritten as this, for clarity: class GenericsTest { public static <T, K extends Comparable<? super K>, V> Collector<T, ?, Map<K, V>> toSortedMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) { return null; } static <Z1> Function<Z1, Z1> identity1() { return null; } static <Z2> Function<Z2, Z2> identity2() { return null; } public static void foo() { // *** Following statement does not compile *** Collector<Integer, ?, Map<Integer, Integer>> a = toSortedMap(identity1(), identity2()); } } Here, you get the following constraints (during method applicability): T <: Z1 <: K T <: Z2 <: V This means that all 5 type-variables depend on each other and there's no order in which resolution can successfully resolve them. Furthermore, if we simply instantiate such variables with proper bounds (18.4 step 1), we fail to satisfy declared bound as K = Object does not satisfy Comparable<? super K>. So we are left with step 2, which creates synthetic variables with glb of upper bounds, and lub of lower bounds. When performing this step there are several issues: * the logic for step 2 is not implemented correctly and it ignores lower bounds * javac fails to compute the bound of T' = glb(Z1, Z2) - this is because javac cannot handle glb where more than one independent type-var occurs * even if the above problems are fixed, the program would still fail, as javac can't see that Z1' & Z2' <: Z2' - this is due to the fact that Types.asSuper does not expect a type-variable to be in the 'interfaces' part of an intersection class type (this restriction probably comes from the one that applies to type-variable declared bounds, see 4.4).
04-01-2016

Moving it to JDK for further investigation by dev-team.
31-12-2015

Test Result: OS: Generic JAVAC: ###### 8u0 b132 : Fail 8u65 b17 : Fail 8u66 b18 : Fail 8u72 b05 : Fail 9u0 b96 : Fail Netbeans: ######### Both are reporting error at compile time [Function.identity() & x->x] Eclipse: ####### 8u0 b132 : Pass 8u65 b17 : Pass 8u66 b17 : Pass 8u72 b05 : Pass 9u0 b96 : Pass Compile Error Message: #################### GenericsTest.java:17: error: method toSortedMap in class GenericsTest cannot be applied to given types; Collector<Integer, ?, Map<Integer, Integer>> a = toSortedMap(Function.identity(), Function.identity()); ^ required: Function<? super T,? extends K>,Function<? super T,? extends V> found: Function<Object,Object>,Function<Object,Object> reason: inferred type does not conform to upper bound(s) inferred: Object upper bound(s): K,Comparable<? super K>,Object where T,K,V are type-variables: T extends Object declared in method <T,K,V>toSortedMap(Function<? super T,? extends K>,Function<? super T,? extends V>) K extends Comparable<? super K> declared in method <T,K,V>toSortedMap(Function<? super T,? extends K>,Function<? super T,? extends V>) V extends Object declared in method <T,K,V>toSortedMap(Function<? super T,? extends K>,Function<? super T,? extends V>) 1 error
31-12-2015