JDK-8323708 : Adjust Classfile API's type arg model to better represent the embodied type
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang.classfile
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 23
  • Submitted: 2024-01-14
  • Updated: 2024-04-30
  • Resolved: 2024-04-30
Related Reports
CSR :  
Relates :  
Relates :  
Description
Summary
-------

Remodel the type arguments in Class-File API to make it an algebraic data type, so users can access the bound type more easily and reliably.

Problem
-------

Currently, when the wildcard indicator in TypeArg is not UNBOUNDED, users can safely trust the bound, wrapped in optional, is non-null; but users still have to use a "risky" unwrap code.

Solution
--------

Remodel the TypeArg to have Bounded and Unbounded subinterfaces; the wildcard indicator and bound will only be present in the Bounded subinterface. This model also more closely aligns with the model from JLS: https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.7.9.1-300-B.4

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


    --- a/src/java.base/share/classes/java/lang/classfile/Signature.java
    +++ b/src/java.base/share/classes/java/lang/classfile/Signature.java
    @@ -185,80 +185,92 @@ public static ClassTypeSig of(ClassTypeSig outerType, String className, TypeArg.
         /**
          * Models the type argument.
          *
    +     * @sealedGraph
          * @since 22
          */
         @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
    -    public sealed interface TypeArg
    -            permits SignaturesImpl.TypeArgImpl {
    +    public sealed interface TypeArg {
     
             /**
    -         * Indicator for whether a wildcard has default bound, no bound,
    -         * an upper bound, or a lower bound
    -         *
    -         * @since 22
    +         * Models an unbounded type argument {@code *}.
    +         * @since 23
              */
             @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
    -        public enum WildcardIndicator {
    -
    -            /**
    -             * default bound wildcard (empty)
    -             */
    -            DEFAULT,
    -
    -            /**
    -             * unbounded indicator {@code *}
    -             */
    -            UNBOUNDED,
    +        public sealed interface Unbounded extends TypeArg permits SignaturesImpl.UnboundedTypeArgImpl {
    +        }
     
    -            /**
    -             * upper-bounded indicator {@code +}
    -             */
    -            EXTENDS,
    +        /**
    +         * Models a type argument with an explicit bound type.
    +         * @since 23
    +         */
    +        @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
    +        public sealed interface Bounded extends TypeArg permits SignaturesImpl.TypeArgImpl {
     
                 /**
    -             * lower-bounded indicator {@code -}
    +             * Models a type argument's wildcard indicator.
    +             * @since 23
                  */
    -            SUPER;
    +            @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
    +            public enum WildcardIndicator {
    +
    +                /**
    +                 * No wildcard (empty), an exact type. Also known as
    +                 * {@index invariant}.
    +                 */
    +                NONE,
    +
    +                /**
    +                 * Upper-bound indicator {@code +}. Also known as
    +                 * {@index covariant}.
    +                 */
    +                EXTENDS,
    +
    +                /**
    +                 * Lower-bound indicator {@code -}. Also known as
    +                 * {@index contravariant}.
    +                 */
    +                SUPER;
    +            }
    +
    +            /** {@return the kind of wildcard} */
    +            WildcardIndicator wildcardIndicator();
    +
    +            /** {@return the signature of the type bound} */
    +            RefTypeSig boundType();
             }
     
    -        /** {@return the wildcard indicator} */
    -        WildcardIndicator wildcardIndicator();
    -
    -        /** {@return the signature of the type bound, if any} */
    -        Optional<RefTypeSig> boundType();
    -
             /**
              * {@return a bounded type arg}
              * @param boundType the bound
              */
    -        public static TypeArg of(RefTypeSig boundType) {
    +        public static TypeArg.Bounded of(RefTypeSig boundType) {
                 requireNonNull(boundType);
    -            return of(WildcardIndicator.DEFAULT, Optional.of(boundType));
    +            return bounded(Bounded.WildcardIndicator.NONE, boundType);
             }
     
             /**
              * {@return an unbounded type arg}
              */
    -        public static TypeArg unbounded() {
    -            return of(WildcardIndicator.UNBOUNDED, Optional.empty());
    +        public static TypeArg.Unbounded unbounded() {
    +            return SignaturesImpl.UnboundedTypeArgImpl.INSTANCE;
             }
     
             /**
              * {@return an upper-bounded type arg}
              * @param boundType the upper bound
              */
    -        public static TypeArg extendsOf(RefTypeSig boundType) {
    +        public static TypeArg.Bounded extendsOf(RefTypeSig boundType) {
                 requireNonNull(boundType);
    -            return of(WildcardIndicator.EXTENDS, Optional.of(boundType));
    +            return bounded(Bounded.WildcardIndicator.EXTENDS, boundType);
             }
     
             /**
              * {@return a lower-bounded type arg}
              * @param boundType the lower bound
              */
    -        public static TypeArg superOf(RefTypeSig boundType) {
    +        public static TypeArg.Bounded superOf(RefTypeSig boundType) {
                 requireNonNull(boundType);
    -            return of(WildcardIndicator.SUPER, Optional.of(boundType));
    +            return bounded(Bounded.WildcardIndicator.SUPER, boundType);
             }
     
             /**
    @@ -266,7 +278,9 @@ public static TypeArg superOf(RefTypeSig boundType) {
              * @param wildcard the wild card
              * @param boundType optional bound type
              */
    -        public static TypeArg of(WildcardIndicator wildcard, Optional<RefTypeSig> boundType) {
    +        public static TypeArg.Bounded bounded(Bounded.WildcardIndicator wildcard, RefTypeSig boundType) {
    +            requireNonNull(wildcard);
    +            requireNonNull(boundType);
                 return new SignaturesImpl.TypeArgImpl(wildcard, boundType);
             }
         }

    

Comments
Moving to Approved.
30-04-2024

[~darcy] I just noticed the 5 static factory methods in TypeArg (of, unbounded, extendsOf, superOf, bounded) are missing @since 23 tags. Should I get another round of CSR or can I simply add them to the specs?
30-04-2024

I have already advanced this CSR to finalized. May I request another phase of review?
23-04-2024

[~liach] and [~asotona], as reminder, when you are ready to do so, please advance this CSR to Finalized to request the second phase of CSR review.
30-03-2024

Moving to Provisional, not Approved.
08-03-2024

This is now advanced to proposed. We seek to include this change in JEP 466, Class-File API (Second Preview), when this is approved. Original mailing list discussion for context: https://mail.openjdk.org/pipermail/classfile-api-dev/2023-November/000419.html
26-02-2024