JDK-8253168 : Surprising 'multiple elements' behaviour from getTypeElement when cross-compiling with --release
  • Type: CSR
  • Component: core-libs
  • Sub-Component: javax.lang.model
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 16
  • Submitted: 2020-09-15
  • Updated: 2020-10-29
  • Resolved: 2020-10-29
Related Reports
CSR :  
Description
Summary
-------

Avoid surprising `null` result from `Elements.getTypeElement(CharSequence)` and `Elements.getPackageElement(CharSequence)` when a (uniquely) visible element clashes with an invisible element.

Problem
-------

Unqualified `Elements.getTypeElement(CharSequence)` and `Elements.getPackageElement(CharSequence)` search for elements across all modules in the module graph, and only return a value when they find exactly one element. This is troublesome, as an element (uniquely) visible from a root module may be "hidden" by an invisible element.

For example, consider:

    ---m1:
    module m1 {}
    package test; public class Test {}

    ---m2:
    module m2 { exports test; }
    package test; public class Test{}

    ---mt:
    module mt { requires m1; requires m2; }

While compiling "mt", `Elements.getTypeElement("test.Test")` will return `null`, as it will find two copies of `test.Test` (one in each `m1` and `m2`). But, in the context of `mt` compilation, only the element from `m2` makes sense.

Solution
--------

Prefer elements (uniquely) visible from root modules. Only if none is found, search for invisible elements. Root modules are the modules javac is currently compiling.

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

Proposed change to `javax.lang.model.util.Elements`:

    diff --git a/src/java.base/share/classes/java/lang/module/package-info.java b/src/java.base/share/classes/java/lang/module/package-info.java
    index ff7448c9c7f..2750c99a216 100644
    --- a/src/java.base/share/classes/java/lang/module/package-info.java
    +++ b/src/java.base/share/classes/java/lang/module/package-info.java
    @@ -147,7 +147,7 @@
      * <p> Otherwise, resolution succeeds, and the result of resolution is the
      * readability graph.
      *
    - * <h3> Root modules </h3>
    + * <h3><a id="root-modules"></a> Root modules </h3>
      *
      * <p> The set of root modules at compile-time is usually the set of modules
      * being compiled. At run-time, the set of root modules is usually the
    diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
    index 81344311869..aad204336e8 100644
    --- a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
    +++ b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
    @@ -51,11 +51,30 @@
     public interface Elements {

         /**
    -     * Returns a package given its fully qualified name if the package is unique in the environment.
    -     * If running with modules, all modules in the modules graph are searched for matching packages.
    -     *
    -     * @param name  fully qualified package name, or an empty string for an unnamed package
    -     * @return the specified package, or {@code null} if it cannot be uniquely found
    +     * Returns a package given its fully qualified name if the package is uniquely
    +     * determinable in the environment.
    +     *
    +     * If running with modules, packages of the given name are searched in a
    +     * two-stage process:
    +     * <ul>
    +     *     <li>find non-empty packages with the given name returned by
    +     *         {@link #getPackageElement(ModuleElement, CharSequence)},
    +     *         where the provided ModuleSymbol is any
    +     *         <a href="../../../../../java.base/java/lang/module/package-summary.html#root-modules">root module</a>,
    +     *     </li>
    +     *     <li>if the above yields an empty list, search
    +     *         {@link #getAllModuleElements() all modules} for observable
    +     *         packages with the given name
    +     *     </li>
    +     * </ul>
    +     *
    +     * If this process leads to a list with a single element,
    +     * the single element is returned, otherwise null is returned.
    +     *
    +     * @param name fully qualified package name,
    +     *             or an empty string for an unnamed package
    +     * @return the specified package,
    +     *         or {@code null} if no package can be uniquely determined.
          */
         PackageElement getPackageElement(CharSequence name);

    @@ -119,12 +138,29 @@ default PackageElement getPackageElement(ModuleElement module, CharSequence name
         }

         /**
    -     * Returns a type element given its canonical name if the type element is unique in the environment.
    -     * If running with modules, all modules in the modules graph are searched for matching
    -     * type elements.
    -     *
    -     * @param name  the canonical name
    -     * @return the named type element, or {@code null} if it cannot be uniquely found
    +     * Returns a type element given its canonical name if the type element is uniquely
    +     * determinable in the environment.
    +     *
    +     * If running with modules, type elements of the given name are
    +     * searched in a two-stage process:
    +     * <ul>
    +     *     <li>find type elements with the given name returned by
    +     *         {@link #getTypeElement(ModuleElement, CharSequence)},
    +     *         where the provided ModuleSymbol is any
    +     *         <a href="../../../../../java.base/java/lang/module/package-summary.html#root-modules">root module</a>,
    +     *     </li>
    +     *     <li>if the above yields an empty list, search
    +     *         {@link #getAllModuleElements() all modules} for observable
    +     *         type elements with the given name
    +     *     </li>
    +     * </ul>
    +     *
    +     * If this process leads to a list with a single element,
    +     * the single element is returned, otherwise null is returned.
    +     *
    +     * @param name the canonical name
    +     * @return the named type element,
    +     *         or {@code null} if no type element can be uniquely determined.
          */
         TypeElement getTypeElement(CharSequence name);

Rendered javadoc with these changes is available for convenience here:

   * http://cr.openjdk.java.net/~jlahoda/8236842/docsv2/api/java.compiler/javax/lang/model/util/Elements.html#getTypeElement(java.lang.CharSequence)
   * http://cr.openjdk.java.net/~jlahoda/8236842/docsv2/api/java.compiler/javax/lang/model/util/Elements.html#getPackageElement(java.lang.CharSequence)


Comments
Changing bug area to core-libs:javax.lang.model and moving to Approved.
29-10-2020