JDK-8064803 : Javac erroneously uses instantiated signatures when merging abstract most-specific methods
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2014-11-13
  • Updated: 2015-09-29
  • Resolved: 2014-11-17
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 8 JDK 9
8u60Fixed 9 b42Fixed
Related Reports
Duplicate :  
Relates :  
Description
Test includes 3 interfaces, 1 implementation and 1 test class. 
public interface ParentA<T> {
    T process() throws Exception;
}
public interface ParentB<T> {
    T process() throws Exception;
}
public interface Child<T> extends ParentA<T>, ParentB<T> {
   // everything works fine when this line is uncommented 
   // T process() throws Exception;
}
public class ChildImpl<T> implements Child<T> {
    @Override
    public T process() {
        return null;
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Child<String> child = new ChildImpl<String>();
        String result = child.process();
        System.err.println(result);
    }
}
1 - When I compile these with javac 8 and run the main method in Test class, it fails with the following exception:
Exception in thread "main" java.lang.NoSuchMethodError: Child.process()Ljava/lang/String;
at Test.main(Test.java:5)
2 - When I uncomment overriding method definition in `Child` interface, then it works fine.
public interface Child<T> extends ParentA<T>, ParentB<T> {
   T process() throws Exception;
}
3 - When I remove `throws Exception` declaration from `process` method, then it works fine again.
Then I looked at the disassembled bytecode of the Test.class and saw in Java 6 and 7 versions there is an additional `checkcast` instruction after `Child.process()` method call. See;
- With Java8: 
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #2                  // class ChildImpl
       3: dup
       4: invokespecial #3                  // Method ChildImpl."<init>":()V
       7: astore_1
       8: aload_1
       9: invokeinterface #4,  1            // InterfaceMethod Child.process:()Ljava/lang/String;
      14: astore_2
      15: getstatic     #5                  // Field java/lang/System.err:Ljava/io/PrintStream;
      18: aload_2
      19: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: return
}
- With Java7:
Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #2                  // class ChildImpl
       3: dup
       4: invokespecial #3                  // Method ChildImpl."<init>":()V
       7: astore_1
       8: aload_1
       9: invokeinterface #4,  1            // InterfaceMethod Child.process:()Ljava/lang/Object;
      14: checkcast     #5                  // class java/lang/String
      17: astore_2
      18: getstatic     #6                  // Field java/lang/System.err:Ljava/io/PrintStream;
      21: aload_2
      22: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      25: return
}
Javac 8 is expecting to call a method with return type of String (InterfaceMethod Child.process:()Ljava/lang/String), but on Java 7 version is expecting a method with return type of Object (InterfaceMethod Child.process:()Ljava/lang/Object) which is expected because of type-erasure then it's checking type of returning value with `checkcast` instruction.
Comments
The issue is in AmbuguityError.mergeAbstracts - instead of using the most specific method symbol signature, javac uses the instantiated method signature (i.e. symbol type when seen as a member of current receiver). This lead to missing synthetic cast.
13-11-2014