JDK-6691741 : JLS membership algorithm is too strong for JVMS method resolution
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Future Project
  • OS: generic
  • CPU: generic
  • Submitted: 2008-04-21
  • Updated: 2014-07-31
  • Resolved: 2008-04-21
Related Reports
Relates :  
Relates :  
Description
This program compiles without error. It fails with an IllegalAccessError at runtime because A2#test does an invokevirtual A2#getI; since A2 doesn't declare getI, A1#getI method is resolved and is inaccessible from A2. It may appear that A2#test should do an invokeinterface I1#getI but the JLS doesn't want this to happen (for good reason - A2 logically has a member getI).

class A1 {
  private int getI() {
     return 1;
  }
}
interface I1 {
   int getI();
}
abstract class A2 extends A1 implements I1 {
  public void test() {
     System.out.println(getI());
  }
}
class A3 extends A2 {
  public int getI() {
     return 2;
  }
}

Comments
EVALUATION The fundamental question is whether getI should undergo "method resolution" (JVMS 5.4.3.3) or "interface method resolution" (JVMS 5.4.3.4). The JLS is arguably at fault for "forgetting" that getI is a member of A2 only because of inheritance from I1. But everything in 15.12.3 and 15.12.4 indicates that getI should be considered a member of A2. An invocation 'getI()' is seen as targeting a member method of the class containing the invocation, i.e. A2. The definition of "qualifying type" in 13.1 unsurprisingly selects A2 also. invokevirtual is right. The VM must therefore do "method resolution" not "interface method resolution". I concede that if A2 didn't implement an interface, then the code wouldn't have compiled (because A2 wouldn't have had a getI member)...so the VM has to search interfaces if classes don't provide a good member. The penultimate para in 5.4.3.3 could say: "If method lookup fails ... throws an AbstractMethodError. Otherwise, ***if the result of successfully looking up the referenced method was a method inaccessible to D and declared in a class, then step 3 above is applied for the first time. If that method lookup fails, method resolution throws a NoSuchMethodError. If that method lookup succeeds and ... throws an AbstractMethodError. Otherwise, if that method lookup finds a method inaccessible to D, method resolution throws an IllegalAccessError.***" (You may wonder when a method found in an interface might not be accessible to D when all interface methods are public - well, soon interface methods can be module-private too!) This change promotes binary compatibility because you can flip A1#getI between private and non-private and it makes no difference. But it's an awful, ugly change with unpredictable consequences. I don't want to make it, but there's a real discrepancy between the JLS and JVMS without it.
21-04-2008