JDK-8207320 : Wrong type order for intersection lambdas with multiple abstract methods
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8,9,10.0.1,11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2018-07-14
  • Updated: 2018-11-09
  • Resolved: 2018-08-15
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 12
12 b07Fixed
Related Reports
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
When specifying multiple lambda intersection types where there are multiple interfaces having abstract methods, however there are default implementations so that there is only one abstract method, the ordering matters. Abstract method default implementations should be before abstract methods.

I'm not sure if it's Javac bug or a Java bug, but either this code should be rejected or it should work. If it should work, either javac should reorder types or LambdaMetafactory should support unusual type order.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It should work or fail to compile.
ACTUAL -
It fails at runtime.

---------- BEGIN SOURCE ----------
public class Main {
    public static void main(String[] args) {
        Runnable a = ((A & B & AB & C) () -> System.out.println("Called"))::b;
        a.run();
    }

    interface A {
        void a();
    }

    interface B {
        void b();
    }

    interface C {
        void c();
    }

    interface AB extends A, B {
        @Override
        default void a() {
            System.out.println("A");
        }

        @Override
        default void b() {
            System.out.println("B");
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use (AB & A & B & C) instead of (A & B & AB & C)

FREQUENCY : always



Comments
review thread: http://mail.openjdk.java.net/pipermail/compiler-dev/2018-August/012283.html
15-08-2018

Underlying issue is that javac uses the first component of the intersection as its LambdaMetafactory capture type: InvokeDynamic #0:run:(LA;)Ljava/lang/Runnable; But the referenced method handle, of course, is expecting an instance of the referenced class, which may not be the first component of the intersection: REF_invokeInterface AB.b:()V The parameter type of the invokedynamic ought to be AB: '(LAB;)Ljava/lang/Runnable;'; and javac needs to ensure that the last checkcast before the invokedynamic call has a compatible type.
23-07-2018

Simplified test, which makes no use of an intersection type as a lambda target type���the relevance of the intersection type is as the type of the method reference expression receiver. interface A { void a(); } interface AB extends A { default void a() { System.out.println("A"); } default void b() { System.out.println("B"); } } public static void main(String... args) { Object o = new AB() {}; Runnable r = ((A & AB) o)::b; }
23-07-2018

Specified behavior is to accept the program. - 'A & B & AB & C' is a functional interface type (JLS 9.8), because it induces an interface (JLS 4.9) with only one abstract method (per JLS 9.4.1, A.a and B.b are not inherited; C.c is an abstract member, and AB.a and AB.b have defaults). - The function type of 'A & B & AB & C' is '()->void' (JLS 9.9). - The lambda expression is congruent with '()->void' (JLS 15.27.3).
23-07-2018

This looks like issue, as Eclipse compiler rejects this code. Javac compiles fine leading to runtime error Below is the snapshot of the error executed on 11 ea b21 -sh-4.2$ /scratch/fairoz/JAVA/jdk11/jdk-11-ea+21/bin/javac Main.java -sh-4.2$ /scratch/fairoz/JAVA/jdk11/jdk-11-ea+21/bin/java Main Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:194) at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:307) at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:258) at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:248) at Main.main(Main.java:3) Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type interface Main$A; not a subtype of implementation type interface Main$AB at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:254) at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:328) at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:127) ... 4 more -sh-4.2$
15-07-2018