JDK-8321819 : generic type information lost on mandated parameters of record's compact constructors
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 23
  • Submitted: 2023-12-11
  • Updated: 2024-05-24
  • Resolved: 2024-05-24
Related Reports
CSR :  
Description
Summary
-------

Reflection is not retrieving generic type information for mandated parameters. Now that the parameters of compact constructors of record classes are mandated it makes sense to reconsider this decision.

Problem
-------

When mapping the generic type information from the `Signature` classfile attribute to parameters, code reflection is currently ignoring both mandated and synthetic (constructor) parameters. This works well for local classes: for local classes, the parameter holding the enclosing class is mandated, but is not represented in the `Signature` field.

It does not work well for records whose components are of generic types, and have compact constructors. The parameters of the compact constructors are marked mandated, and even though they are represented in the `Signature` attribute, the mapping of the generic type information from `Signature` to the parameters will ignore them. This is problematic, as the generic parameters of the compact constructor may be useful for core reflection users.

Solution
--------

For compact record constructors, the generic type information will be fetched from the `Signature` attribute for its parameters independently if they are mandated or not. This is a core-reflection only change, the compilation of records with compact constructor is not affected in any way.

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

This is the proposed change to the specification:

    diff --git a/src/java.base/share/classes/java/lang/reflect/Executable.java b/src/java.base/share/classes/java/lang/reflect/Executable.java
    index 420be5029b01a..b808e052fb294 100644
    --- a/src/java.base/share/classes/java/lang/reflect/Executable.java
    +++ b/src/java.base/share/classes/java/lang/reflect/Executable.java
    @@ -255,12 +255,17 @@ public Set<AccessFlag> accessFlags() {
          * represented by this object.  Returns an array of length
          * 0 if the underlying executable takes no parameters.
          * Note that the constructors of some inner classes
    -     * may have an implicitly declared parameter in addition to
    -     * explicitly declared ones.
    +     * may have an {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} parameter in addition to explicitly
    +     * declared ones.
    +     * Also note that compact constructors of a record class may have
    +     * {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} parameters.
          *
          * @return the parameter types for the executable this object
          * represents
          */
    +    @SuppressWarnings("doclint:reference") // cross-module links
         public abstract Class<?>[] getParameterTypes();
     
         /**
    @@ -280,18 +285,32 @@ public Set<AccessFlag> accessFlags() {
          * underlying executable takes no parameters.  Note that the
          * constructors of some inner classes may have an implicitly
          * declared parameter in addition to explicitly declared ones.
    -     * Also note that as a <a
    -     * href="{@docRoot}/java.base/java/lang/reflect/package-summary.html#LanguageJvmModel">modeling
    -     * artifact</a>, the number of returned parameters can differ
    +     * Compact constructors of a record class may also have
    +     * {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} parameters,
    +     * but they are a special case and thus considered as if they had
    +     * been explicitly declared in the source.
    +     * Finally note that as a {@link java.lang.reflect##LanguageJvmModel
    +     * modeling artifact}, the number of returned parameters can differ
          * depending on whether or not generic information is present. If
    -     * generic information is present, only parameters explicitly
    -     * present in the source will be returned; if generic information
    -     * is not present, implicit and synthetic parameters may be
    +     * generic information is present, parameters explicitly
    +     * present in the source or parameters of compact constructors
    +     * of a record class will be returned.
    +     * Note that parameters of compact constructors of a record class are a special case,
    +     * as they are not explicitly present in the source, and its type will be returned
    +     * regardless of the parameters being
    +     * {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} or not.
    +     * If generic information is not present, implicit and synthetic parameters may be
          * returned as well.
          *
          * <p>If a formal parameter type is a parameterized type,
          * the {@code Type} object returned for it must accurately reflect
    -     * the actual type arguments used in the source code.
    +     * the actual type arguments used in the source code. This assertion also
    +     * applies to the parameters of compact constructors of a record class,
    +     * independently of them being
    +     * {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} or not.
          *
          * <p>If a formal parameter type is a type variable or a parameterized
          * type, it is created. Otherwise, it is resolved.
    @@ -309,6 +328,7 @@ public Set<AccessFlag> accessFlags() {
          *     the underlying executable's parameter types refer to a parameterized
          *     type that cannot be instantiated for any reason
          */
    +    @SuppressWarnings("doclint:reference") // cross-module links
         public Type[] getGenericParameterTypes() {
             if (hasGenericInformation())
                 return getGenericInfo().getParameterTypes();
    @@ -748,13 +780,18 @@ Type parameterize(Class<?> c) {
          * Returns an array of length 0 if the method/constructor declares no
          * parameters.
          * Note that the constructors of some inner classes
    -     * may have an implicitly declared parameter in addition to
    -     * explicitly declared ones.
    +     * may have an
    +     * {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} parameter in addition to explicitly declared ones.
    +     * Also note that compact constructors of a record class may have
    +     * {@linkplain java.compiler/javax.lang.model.util.Elements.Origin#MANDATED
    +     * implicitly declared} parameters.
          *
          * @return an array of objects representing the types of the
          * formal parameters of the method or constructor represented by this
          * {@code Executable}
          */
    +    @SuppressWarnings("doclint:reference") // cross-module links
         public AnnotatedType[] getAnnotatedParameterTypes() {

Comments
Moving updated request to Approved.
24-05-2024

Moving to Provisional, not Approved. [~vromero], before this CSR is re-Finalized, please give some more context behind it. For example, if the situation is "javac used to do X, now it does Y. We're updated core reflection to give more information in both cases X and Y." please explain that. Also, the updated text of `getGenericParameterTypes()` now reads: "...If generic information is present, only parameters explicitly present in the source will be returned. Note that for compact constructors of a record class, its parameters which are implicitly declared (mandated) will be returned. If generic information is not present, implicit and synthetic parameters may be returned as well." As written, it is not clear to me which condition compact constructors of a record class fall under. In other words, is the statement "If generic information is present, only parameters explicitly present in the source will be returned." no longer true because of updates for compact constructors? If so, some larger edit is needed.
23-05-2024

Moving to Provisional, not Approved. [~vromero], please clarify in the CSR whether or not this is a core reflection only change, javac change, both, etc.
22-05-2024

[~darcy] I have clarified in the text that this is a core reflection only change. Thanks for your comments
20-05-2024