JDK-8195598 : Reference to overloaded method is ambiguous with 3 methods but works with 2
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 9,10
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2018-01-16
  • Updated: 2018-02-16
  • Resolved: 2018-01-18
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 11
11 b01Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux fede-peralta 4.13.0-25-generic #29-Ubuntu SMP Mon Jan 8 21:14:41 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
Consider the following code:

public class Ambiguous3 {
    public CompletableFuture<?> test() {
        return ok(() -> System.out.append("aaa"));
    }
    public <T> CompletableFuture<T> ok(Supplier<T> action) {
        return CompletableFuture.supplyAsync(action);
    }
    public <T> CompletableFuture<T> ok(T body) {
        return CompletableFuture.completedFuture(body);
    }
    public CompletableFuture<Void> ok(Runnable action) {
        return CompletableFuture.runAsync(action);
    }
}

This fails in the release version of Java 9 with ���reference to ok is ambiguous���, stating ���both method <T>ok(Supplier<T>) in Ambiguous3 and method ok(Runnable) in Ambiguous3 match���.

But just changing the order of the methods:

public class Ambiguous3 {
    public CompletableFuture<?> test() {
        return ok(() -> System.out.append("aaa"));
    }
    public <T> CompletableFuture<T> ok(T body) {
        return CompletableFuture.completedFuture(body);
    }
    public <T> CompletableFuture<T> ok(Supplier<T> action) {
        return CompletableFuture.supplyAsync(action);
    }
    public CompletableFuture<Void> ok(Runnable action) {
        return CompletableFuture.runAsync(action);
    }
}

causes the compiler to accept the code without any errors.

Also, removing the ok(T) method makes the code accepted.

Note that whenever the compiler accepts the code, it considers ok(Supplier) to be more specific than ok(Runnable), which is the expected behavior for a function parameter that matches both.

See the issue in Stackoverflow here:
https://stackoverflow.com/a/48260046/1876620


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Just try to compile the first version of the class.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Code should compile irrespective of methods' declaration order.
ACTUAL -
Code doesn't compile in some cases, depending on the order of declaration of methods.

REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
Arrange the methods in a different order within the class, use casts to hint the compiler about which method should be chosen, turn lambda expression to statement instead of expression and the like...


Comments
This is caused by JDK-8154180. There's a single shared state variable on the lambda expression called 'pertinentToApplicability' - unfortunately it is possible for this flag to be true/false depending on the target, so here introducing a non-pertinent method between the two good ones causes an error.
17-01-2018

This issue is a regression started from 9 ea b120, below are the results 8u162 - Pass 9 ea b119 - Pass 9 ea b120 - Fail 9 GA - Fail 10 ea b39 - Fail Below is the result on 10 ea b39 == -sh-4.2$ /scratch/fairoz/JAVA/jdk10/jdk-10-ea+39/bin/javac Ambiguous3.java Ambiguous3.java:6: error: reference to ok is ambiguous return ok(() -> System.out.append("aaa")); ^ both method <T>ok(Supplier<T>) in Ambiguous3 and method ok(Runnable) in Ambiguous3 match where T is a type-variable: T extends Object declared in method <T>ok(Supplier<T>) 1 error = Changing the order of 2nd and 3rd method makes compilation successful.
17-01-2018

The problem seems related to the most specific disambiguation logic for functional interfaces; if the second method is included, the speculative tree used during that logic is empty, hence the algorithm falls back to the standard subtype check (which produces the ambiguity).
17-01-2018