JDK-8254609 : Change 'final' error checks to throw ICCE
  • Type: CSR
  • Component: hotspot
  • Sub-Component: runtime
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 16
  • Submitted: 2020-10-12
  • Updated: 2020-11-02
  • Resolved: 2020-10-26
Related Reports
CSR :  
CSR :  
Description
Summary
-------

Change the exception thrown by the JVM when a class being loaded extends a `final` class or overrides a `final` instance method from `VerifyError` to `IncompatibleClassChangeError`.

Problem
-------

The enforcement of `ACC_FINAL` flags on classes and methods has long been specified as part of verification. Accordingly, JVM implementations throw `VerifyError` when a class extends a `final` class or overrides a `final` instance method.

However, the enforcement of `ACC_FINAL` flags has nothing to do with verification. JVM implementations perform these checks on a class when it is being derived (part of class loading, prior to verification). This makes sense, because it is best not to permit classes to be loaded if they violate the basic subclassing constraints of another class.

It is incongruous for the JVM Spec to enforce `ACC_FINAL` flags as part of verification when JVM implementations enforce them as part of derivation.

In the future, [sealed classes](https://bugs.openjdk.java.net/browse/JDK-8246775) will need these details to be ironed out so that their checks can behave consistently.

Solution
--------

Update Hotspot to throw an `IncompatibleClassChangeError` rather than a `VerifyError`.

Update JVMS to specify the check when a class is derived in 5.3.5, throwing an `IncompatibleClassChangeError`.

Specification details:

If a superclass has its `ACC_INTERFACE` flag set, class loading throws an `IncompatibleClassChangeError`. The `ACC_FINAL` check is similar, and so it makes sense to throw the same error.

The _timing_ of this check is tricky, in relation to the `ClassCircularityError` check. Interfaces are required (as a format check) to extend `java/lang/Object`, so the relative timing is irrelevant in that case. For the `final` check, it makes more sense to check a class's attributes after the class has been successfully loaded (which can't happen if there's a circularity error). This is what HotSpot does: the circularity error has precedence over the `final` superclass class error.

Compatibility discussion:

Most programs do not attempt to catch and handle `LinkageError`s. Among those that do, typical behavior would be to attempt to load an arbitrary class and then respond to all forms of errors that might arise, including `VerifyError`s and `IncompatibleClassChangeError`s. Those programs will still work properly.

The biggest risk is for a theoretical program that attempts to load a class knowing that its superclass might be `final`, and also knowing that its superclass will never be an interface. Its unlikely that this scenario would arise in real-world code.

Specification
-------------

As described in [JDK-8243582](https://bugs.openjdk.java.net/browse/JDK-8243582), changes to JVMS 5.3.5 (interpret "~~" as "strikeout" indicators, and "**" as insertion indicators):

    If *C* has a direct superclass, the symbolic reference from *C* to its 
    direct superclass is resolved using the algorithm of [5.4.3.1]. 
    Note that if *C* is an interface it must have `Object` as its direct 
    superclass, which must already have been loaded. 
    Only `Object` has no direct superclass. 
         
    Any exceptions that can be thrown due to class or interface resolution can 
    be thrown as a result of this phase of loading. 
    In addition, this phase of loading must detect the following errors: 

    - ~~If the class or interface named as the direct superclass of *C* is in 
        fact an interface, loading throws an `IncompatibleClassChangeError`.~~ 

    - ~~Otherwise, if~~ **If** any of the superclasses of *C* is *C* itself, 
        loading throws a `ClassCircularityError`. 

    - **Otherwise, if the class or interface named as the direct superclass of 
        *C* is an interface or a `final` class, derivation throws a 
        `IncompatibleClassChangeError`.** 

    - **Otherwise, if *C* is a class and some non-`static` method declared in 
        *C* can override ([5.4.5]) a `final`, non-`static` method declared in a 
        superclass of *C*, derivation throws an `IncompatibleClassChangeError`.** 


Also update JLS 13.4.2:

> If a class that was not declared final is changed to be declared
> final, then ~~a VerifyError~~ **an IncompatibleClassChangeError** is
> thrown if a binary of a pre-existing subclass of this class is loaded,
> because final classes can have no subclasses; such a change is not
> recommended for widely distributed classes.

And JLS 13.4.17:

> If Super is recompiled but not Test, then running the new binary with
> the existing binary of Test results in ~~a VerifyError~~ **an
> IncompatibleClassChangeError** because the class Test improperly tries
> to override the instance method out.
Comments
Moving to Approved.
26-10-2020

A quick discussion of process matters. it is true that a more consistent implementation of the CSR review would be for JLS and JVMS issues to be the parent issue of the CSR. However, the CSR process has used the same alternate arrangement as the predecessor ccc process where JLS and JVMS review occurs under the *implementation* issue in javac or HotSpot, respectively. (In most other areas, such as libraries, the implementation and specification nearly always occur bound together.) This arrangement could be renegotiated and updated, but I don't see a reason to do so now in the context of this bug.
21-10-2020

Process note: I've linked JDK-8243582, the JVMS component of this work, as a "csr of" issue���the review is for both HotSpot and JVMS changes. If this is not the intended use of the "csr of" link type, I can move it to a "relates to" link.
16-10-2020

This CSR proposes changing the type of LinkageError exceptions that get thrown in a few 'final' related cases from VerifyError to IncompatibleClassChangeError. I don't see how this would impact low-level bytecode manipulation tools.
15-10-2020

Changing estimated compatibility risk to "low" rather than "minimal." Are there low-level tools that would be expected to be impacted by this? The sort of tools that manipulate bytecode, etc? If so, checking through the outreach channels would be prudent.
13-10-2020

So that looks like no impact on the JDK outside of tests specifically designed to validate the error being thrown. Is there any further testing you're looking for?
13-10-2020

This change causes 80+ JCK tests to fail and 3 hotspot JTReg tests to fail when run against mach5 tiers 1-5.
13-10-2020

Moving to Provisional, *not* Approved. Added a release-note=yes label to the parent issue. What testing has been done on the behavioral compatibility impact of this change?
13-10-2020