JDK-8059632 : Method reference compilation uses incorrect qualifying type
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8,8u40,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-10-02
  • Updated: 2022-11-28
  • Resolved: 2022-11-21
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 20
20 b25Fixed
Related Reports
CSR :  
Relates :  
Relates :  
Relates :  
Description
public class InterfaceMethodref {

  interface I { void m(); }
  abstract class C implements I {}

  public void test(C arg) {
    Runnable r = arg::m;
  }

}

The method reference 'arg::m' compiles to:

         6: invokedynamic #3,  0              // InvokeDynamic #0:run:(LInterfaceMethodref$C;)Ljava/lang/Runnable;

With bootstrap method:

  0: #22 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #23 ()V
      #24 invokeinterface InterfaceMethodref$I.m:()V
      #23 ()V

Note that the metafactory call's implMethod is "invokeinterface InterfaceMethodref$I.m:()V", not "invokevirtual InterfaceMethodref$C.m:()V".  This is incorrect: the compiler must refer to the type of the receiver, not the type of the declaring class/interface, per JLS 13.1 (see "the qualifying type of the method invocation").

Also, FWIW, this combination of implMethod and invokedType violates the invariants claimed to be enforced by LambdaMetafactory (specifically, referring to the Javadoc, D1 != A1).  Apparently this isn't currently enforced.
Comments
Changeset: 544e3172 Author: Srikanth Adayapalam <sadayapalam@openjdk.org> Date: 2022-11-21 03:02:29 +0000 URL: https://git.openjdk.org/jdk/commit/544e31722528d12fae0eb19271f85886680801a6
21-11-2022

Using the patch below: diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 7bd195701cf..d5cd12c0ea8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -519,7 +519,13 @@ public class LambdaToMethod extends TreeTranslator { } if (init != null) { + MethodSymbol refSymWas = refSym; refSym = (MethodSymbol) types.binaryQualifier(refSym, init.type); + if (refSymWas != refSym) { + log.note(tree, Notes.BinaryQualifierDiffers(refSymWas, refSymWas.owner, refSym, refSym.owner)); + } else { + log.note(tree, Notes.BinaryQualifierUnchanged); + } } List<JCExpression> indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 1c6cdd5429a..4e1111991c8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -1630,6 +1630,13 @@ compiler.note.deprecated.filename.additional=\ compiler.note.deprecated.plural.additional=\ Some input files additionally use or override a deprecated API. +# 0: symbol, 1: symbol, 2: symbol, 3: symbol +compiler.note.binary.qualifier.differs=\ + Binary qualifier of the method reference changed from {0} of {1} to {2} of {3}. + +compiler.note.binary.qualifier.unchanged=\ + Binary qualifier of the method reference stays unchanged. + # 0: file name compiler.note.removal.filename=\ {0} uses or overrides a deprecated API that is marked for removal. to instrument a complete clean build of OpenJDK, triggers a total of 899 notes. Of these 859 record that binary qualifier hasn't been subjected to any change by the current fix while the remaining cases of 40/899 (4.45%) the binary qualifier has undergone a change due to the code fix.
08-11-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/10809 Date: 2022-10-21 09:31:11 +0000
21-10-2022

ECJ seems to have the same defect.
19-11-2015

Note that javac gets it right for a normal method invocation: arg.m(); becomes 1: invokevirtual #2 // Method InterfaceMethodref$C.m:()V
02-10-2014