JDK-8043703 : 13.4.12: Adding methods can break binary compatibility
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 8
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-05-22
  • Updated: 2017-01-12
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.
Other
tbd_majorUnresolved
Related Reports
Relates :  
Relates :  
Description
As pointed out at [1], JLS8 13.4.12 contains a false claim:

"Adding a method or constructor declaration to a class will not break compatibility with any pre-existing binaries, even in the case where a type could no longer be recompiled because an invocation previously referenced a method or constructor of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the method or constructor declared in a superclass."

It's false because adding a private method in a superclass can cause a resolution failure when invoking a public method inherited by a subclass. (JLS 15.12.4.3 and JVMS 5.4.3.* are in sync on this.)

The problem arises if the public method was originally declared as a default method in a superinterface. Then, the private method interposes between the superinterface and the subclass (the qualifying type of the invocation).

[1] http://weblog.ikvm.net/PermaLink.aspx?guid=cc9b6d6f-8f24-43b2-a6ef-18ba0758c35d
Comments
[~dlsmith] - In (1), I now acknowledge D, the subclass of where the method is added. - 13.4.8 and 13.4.12 both discuss recompiling the call site in their fix-modifiers-vary-descriptor case because it's more interesting than recompiling a subclass where an unrelated method (with the varied descriptor) has been added. - In (2), I now acknowledge that D must previously have been inheriting, not declaring, a method.
24-10-2016

13.4.12 was modified in JLS2 to drop the "even" in the first paragraph (revived in JLS7) and to add the "Assume a reference to a method m ..." paragraph. What's really going on is two scenarios: one scenario fixes the modifiers (e.g. 'public') but varies the descriptor, while another scenario fixes the descriptor but varies the modifiers ('public', 'static'). The first scenario presents a downstream source incompatibility ("where a type could no longer be recompiled") which is interesting but not essential for a student of binary compatibility. On the other hand, the second scenario ignores a crucial source incompatibility (adding a less accessible method to a subclass) which prevents the very creation of a class file able to exhibit the linkage error. (We assume ch.13 is concerned with binaries produced by compilers, not by hex-editing class files.) Still, the second scenario is important because it generalizes to the scenario in the Description: given a reference to method m with qualifying type T, it is possible to have a public version of m in a superinterface of T and then add a private method to a superclass of T that's unrelated to the superinterface. This addition is source compatible (unlike the class-only setup) but not binary compatible. It's unsatisfactory to say that adding a private method is NEVER binary compatible due to possible conflicts in subclasses, but it's also unsatisfactory to ignore the possibility of default methods and say that adding a private method is ALWAYS binary compatible (as the first paragraph would imply). In the absence of JDK-8021581, I plan to lay things out as follows: (note that (2) is only checkable in a closed world, but in practice that's what a developer has, so the text does have some predictive utility) 1. Adding a method or constructor declaration to a class C will not break compatibility with any pre-existing binary that used a qualifying type D (subclass of C) to reference a method or constructor declared above C with the same name and modifiers but different signature or return types. This applies even when the source code of the pre-existing binary could no longer be recompiled because an invocation references a method or constructor of a superclass of C with a signature or return type incompatible with that of the newly added method or constructor; the pre-existing binary with such a reference will continue to reference the method or constructor declared in a superclass of C. 2. Adding a method or constructor declaration to a class C may break compatibility with pre-existing binaries if a subclass D of C _already inherits_ a method or constructor with the same name, signature, and return types, but different modifiers. If a pre-existing binary contains a reference to a method with that name, signature, and return type, and the qualifying type of the reference is D or a subclass of D, then a linkage error will occur if the method in C is less accessible than the method in D, or if the method in C is static (respectively instance) while the method in D is an instance method (respectively static).
24-10-2016

13.4.8 "Field Declarations" is also confusing. It presents the same two scenarios as 13.4.12, but in the reverse order -- its first scenario ("Assume a reference to a field f ...") fixes the descriptor but varies the modifiers, and its second scenario ("In particular, no linkage error will occur ...") fixes the modifiers but varies the descriptor. 13.4.8 should be laid out like 13.4.12, where first we observe that varying the descriptor is harmless for B.C. even if client recompilation is not possible; then we observe that fixing the descriptor but varying the modifiers can break compatibility.
24-10-2016

Some suggestions for tweaks: - The scenario should be parallel: if we're talking about subclass D in the second bullet, we should talk about it in the first bullet, too. (I'd like a guarantee that, if I add a method to C, it won't break references to methods in D with a different descriptor.) - Not sure it's worth discussing source compatibility at all, but if you do, I think it's more interesting to discuss recompiling a subclass of C than it is to discuss recompiling some call site. (New compilation errors at the call site are not impossible, but pretty subtle.) - "declares or inherits" should just be "inherits": the error can only occur if the new declaration in C interferes with resolution that would have found something *after* C (that is, in a superclass of C or a superinterface of D)
20-10-2016

This may instead be a VM spec bug: the "right" thing to do may be to ignore private methods of supertypes entirely during resolution. See JDK-8021581.
28-07-2014