Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 520650

Summary: Failure in overload resolution using functional interface with wildcard
Product: [Eclipse Project] JDT Reporter: Jens Auer <jens.auer>
Component: CoreAssignee: JDT-Core-Inbox <jdt-core-inbox>
Status: CLOSED WONTFIX QA Contact: Stephan Herrmann <stephan.herrmann>
Severity: normal    
Priority: P3 CC: jens.auer, stephan.herrmann
Version: 4.7   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard: stalebug

Description Jens Auer CLA 2017-08-08 02:39:34 EDT
Eclipse 4.7 with JDT 3.13.0.v20170612-0950 reports in error in the call map(oEmpty, Test::double2int):

import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;

public class Test {
    static class Generic<T> {
    }

    public static <T> String map(Generic<T> o, ToIntFunction<? super T> f) {
        return null;
    }

    public static <T> Number map(Generic<T> o, ToDoubleFunction<? super T> f) {
        return null;
    }

    private static int double2int(double x) {
        return 2;
    }

    public static void main(String[] args) {
        Generic<Double> oEmpty = new Generic<>();

        map(oEmpty, Test::double2int);
    }
}

Description	Resource	Path	Location	Type
The method map(Test.Generic<Double>, ToIntFunction<? super Double>) is ambiguous for the type Test	Test.java	/Playground/src/sandbox	line 25	Java Problem

Javac from jdk8 and pre-release jdk9 both accept the code.

I am not very familiar with the details of overload resolution of the Java language. double2int is assignable to both ToIntFunction and ToDoubleFunction, so intuitively I can see why it could be ambiguous. However, the assignment to ToDoubleFunction involves a conversion from the int result to double, so I would expect that ToIntFunction is a better match and the ambiguity is resolved.
Comment 1 Jens Auer CLA 2017-08-25 04:39:29 EDT
I could narrow the problem down to something related to type-inference with widlcards. The following code compiles on javac, but not with ecj:

    static final class A {}

    public static <T> String g(ToIntFunction<? extends A f) {
        return null;
    }

    public static <T> String g(ToDoubleFunction<? extends A> f) {
        return null;
    }

    private static int double2int(A x) {
        return 2;
    }

    public static void main(String[] args) {
        g(HelloWorld::double2int);
    }

Removing the wildcard arguments also removes the error:

    public static <T> String g(ToIntFunction<A> f) {
        return null;
    }

    public static <T> String g(ToDoubleFunction<A> f) {
        return null;
    }

    private static int double2int(Ax) {
        return 2;
    }

    public static void main(String[] args) {
        g(RadarSimulation::double2int);
    }

I am not sure what to make of this.
Comment 2 Jens Auer CLA 2017-08-25 04:53:05 EDT
I looked the rules of method reference type inference, and from my understanding, the method reference is assignable to both ToIntFunction<T> and To DoubleFunction<T> because the return type int is assignment compatible to double. So the method reference is congruent with both types and thus compatible in an invocation context. May be a javac bug?
Comment 3 Jens Auer CLA 2017-08-25 08:09:46 EDT
I've also asked on Stackoverflow to clarify if this is a Java or ECJ issue:
https://stackoverflow.com/questions/45878208/overload-resolution-with-method-references-and-function-interface-specialization
Comment 4 Stephan Herrmann CLA 2017-08-25 15:24:17 EDT
Thanks for your investigations. 

I'll wait if s.o. on SO makes an argument for javac's behavior in this case.
Comment 5 Jens Auer CLA 2017-08-28 02:35:09 EDT
Hi Stephan,
there was an answer on SO which IMHO confirms the behavior of javac:

https://stackoverflow.com/questions/45878208/overload-resolution-with-method-references-and-function-interface-specialization/45891522#45891522

The question is if ToIntFunction is more specific than ToDoubleFunction for the method reference Test::double2int during overload resolution. It turns that I forgot to take the transitivity of the supertype relation into account when looking at the return type:

double >₁ float

float >₁ long

long >₁ int
The supertype relation being a reflexive and transitive closure of the direct supertype relation means that from (double >₁ float)  ∧  (float >₁ long)  ∧  (long >₁ int) follows double :> int.

From this, it can be concluded that double >1 int and thus in 15.12.2.5's case for method references, ToIntFunction is more specific than ToDoubleFunction.
Comment 6 Jens Auer CLA 2017-08-28 02:36:31 EDT
I also want to point out that for the same code without wildcard arguments ecj does not find any ambiguities.

    public static <T> String g(ToIntFunction<A> f) {
        return null;
    }

    public static <T> String g(ToDoubleFunction<A> f) {
        return null;
    }

    private static int double2int(Ax) {
        return 2;
    }

    public static void main(String[] args) {
        g(RadarSimulation::double2int);
    }
Comment 7 Eclipse Genie CLA 2020-03-07 09:38:38 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.