JDK-8321415 : ClassSignature should have superclass and superinterfaces as ClassTypeSig
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang.classfile
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 23
  • Submitted: 2023-12-05
  • Updated: 2024-02-29
  • Resolved: 2024-02-06
Related Reports
CSR :  
Relates :  
Relates :  
Description
Summary
-------

Represent the `superclass` and `superinterfaces` in `ClassTypeSig` with `ClassTypeSig` and `List<ClassTypeSig>`.

Problem
-------

In JVMS 4.7.9.1, the superclass and superinterfaces of a class signature is required to be class signatures. Yet Class-File API uses a `RefTypeSig`, allowing potentially invalid signatures, like using an array class as a superclass.

Solution
--------

Change the `superclass` and `superinterfaces` in `ClassTypeSig` from `RefTypeSig` and `List<RefTypeSig>` to `ClassTypeSig` and `List<ClassTypeSig>`.

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

    --- a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java
    +++ b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java
    @@ -42,10 +42,10 @@ public sealed interface ClassSignature
         List<Signature.TypeParam> typeParameters();
     
         /** {@return the instantiation of the superclass in this signature} */
    -    Signature.RefTypeSig superclassSignature();
    +    Signature.ClassTypeSig superclassSignature();
     
         /** {@return the instantiation of the interfaces in this signature} */
    -    List<Signature.RefTypeSig> superinterfaceSignatures();
    +    List<Signature.ClassTypeSig> superinterfaceSignatures();
     
         /** {@return the raw signature string} */
         String signatureString();
    @@ -55,8 +55,8 @@ public sealed interface ClassSignature
          * @param superclassSignature the superclass
          * @param superinterfaceSignatures the interfaces
          */
    -    public static ClassSignature of(Signature.RefTypeSig superclassSignature,
    -                                    Signature.RefTypeSig... superinterfaceSignatures) {
    +    public static ClassSignature of(Signature.ClassTypeSig superclassSignature,
    +                                    Signature.ClassTypeSig... superinterfaceSignatures) {
             return of(List.of(), superclassSignature, superinterfaceSignatures);
         }
     
    @@ -67,8 +67,8 @@ public static ClassSignature of(Signature.RefTypeSig superclassSignature,
          * @param superinterfaceSignatures the interfaces
          */
         public static ClassSignature of(List<Signature.TypeParam> typeParameters,
    -                                    Signature.RefTypeSig superclassSignature,
    -                                    Signature.RefTypeSig... superinterfaceSignatures) {
    +                                    Signature.ClassTypeSig superclassSignature,
    +                                    Signature.ClassTypeSig... superinterfaceSignatures) {
             return new SignaturesImpl.ClassSignatureImpl(
                     requireNonNull(typeParameters),
                     requireNonNull(superclassSignature),

    

Comments
Thanks for the comments [~jrose]. With the additional information, moving to Approved.
06-02-2024

What Dan Smith said. Supers will always be classes or interfaces. In a hypothetical future Parametric VM, a species of a class or interface may have supers which are themselves species. We cannot easily predict a reflective API for species, except to say it will resemble current APIs for current types.
06-02-2024

Thanks [~dlsmith]; [~liach], I'll consider this CSR again. Cheers.
06-02-2024

Moving to proposed; since Dan says this should be fine with Valhalla, Joe, can you think of any other future development where the superclass or superinterfaces are beyond declared types?
22-01-2024

No concrete plans in Valhalla to make any changes to what would be allowed by the language in 'extends' or 'implements'. I understand the hesitancy, and it's hard to predict the future, but I do think Liang is right that superclass types and superinterface types will always be standard named class/interface types, possibly with type arguments. Nullness, in particular, is not relevant in this context (except as it applies to type arguments).
19-01-2024

[~liach], I've asked the Valhalla team to take a look at this request to make sure it is avoiding anticipated JVM changes for Valhalla.
19-01-2024

Hmm, I don't think the Java programming language or the JVM will ever allow users to extend or implement non-class-or-interface (namely, array classes), even with generic specialization. What do you think? Looking at JDK-8317767, I am not sure about how the signature will change, but I don't think the new additions of null-restricted types will affect superclass or superinterface representations as null restriction on those types are meaningless (type arguments exempt, which will affect all ClassTypeSig so is out of scope)
19-01-2024

Let me use another analogy. Say an API to model Java abstract syntax trees was being designed prior to JDK 5. In such an API it would be reasonable to have the type for a switch expression to be "PrimitiveType" or "IntegralPrimitiveType" since for many years only int and similar values could be switched over. JDK 5 expanded the set of values that could be switched over to include boxed types (java.lang.Integer, etc.) as well as enum types. JDK 7 added String as well. How should the old AST API be updated to accommodate the change in the language? If the original API had returned a less-precise type, no update would be needed, at the cost of extra casts for uses of the API in the intervening years. However, with a PrimitiveType return value, some second "possiblyBroaderSwitchType" method with a different return type would be needed. So my general feedback is, if there is the possibility of anticipated JVM changes modifying the constraints these methods are replying on, to factor that into the API design now. HTH
19-01-2024

I don't think the analogy from reflection completely applies here. To begin, a ClassTypeSig roughly models a DeclaredType in javac's API, or a "class or interface" in Reflection. In Class-File API, we wish to perform validation so that the superclass and superinterfaces of classes are always declared types, and prohibit arrays or primitives. The true interest is the type variables in signatures; it currently makes no sense for a class to extend a type argument or implement a type argument. I am not sure if we will allow such usages in the future. If we do make such a change in the future, there is an existing interface ThrowableSig, which indicates anything that can be evaluated to a declared type. But I don't think this is likely, as this hardly works with erasure. Another crucial difference is that while reflection does not allow users to create instances of methods or fields, Class-File API does. We don't want users to pass in, say, array superclasses for declared classes. Having explicit types to enforce safety at compile time is better than failing at runtime.
18-01-2024

Moving to Provisional, not Approved. The change in and of itself looks reasonable at this point in time give the current state of the JVMS . However, before approving the request, I wanted to share some experience in evolving the language model APIs for annotation processing in case that is helpful here. To condense a longer story, going from the first to the second generation language model, we found it helpful to be less specific in the API, for example using one type to model Executables rather than two or three types to model methods, constructors, method or constructor, etc. In particular, I would advise against this change if any planned work in Valhalla or other on-going project would invalidate the assumptions behind the typing constraints. HTH
17-01-2024