JDK-8056066 : JEP 216: Process Import Statements Correctly
  • Type: JEP
  • Component: tools
  • Sub-Component: javac
  • Priority: P3
  • Status: Closed
  • Resolution: Delivered
  • Fix Versions: 9
  • Submitted: 2014-08-26
  • Updated: 2016-07-12
  • Resolved: 2016-07-12
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Summary
-------

Fix `javac` to properly accept and reject programs regardless of the
order of `import` statements and `extends` and `implements` clauses.


Motivation
----------

In some cases `javac` will accept source code with a certain order of
imports and reject the same source code with just the imports reordered
(_e.g._,
[JDK-7177813](https://bugs.openjdk.java.net/browse/JDK-7177813)).  That
is wrong and confusing.


Description
-----------

`javac` uses several stages when compiling classes. Considering `import`
handling, the two important stages are:

  * Type resolution, which looks through the provided AST looking for
    class and interface declaration, and

  * Member resolution, which includes:
    - (**1a**) If `T` is toplevel, `import`s in the source file defining
      `T` are processed and imported members added in `T`'s scope
    - (**1b**) If `T` is nested, resolution of the class directly enclosing
      `T` (if any)
    - (**2**) `extends`/`implements` clauses of `T` are type-checked
    - (**3**) Type variables of `T` are type-checked

The above stages are part of `javac`'s process of _resolution_ of
classes, which includes determining a class's supertypes, type variables,
and members.

To see this in process in action, consider this code:

    package P;

    import static P.Outer.Nested.*;
    import P.Q.*;

    public class Outer {
        public static class Nested implements I {
        }
    }

    package P.Q;
    public interface I {
    }

During the type-resolution phase it is recognized that there exist types
`P.Outer`, `P.Outer.Nested`, and `P.Q.I`.  Then, if the `P.Outer` class
is to be analyzed, the member resolution phase works like this:

<table>
<tr><td>1.</td><td>Resolution of <code>P.Outer</code> starts</td></tr>
<tr><td>2.</td><td style='padding-left: 2em;'>Processing of the <code>import static P.Outer.Nested.*;</code> starts, per 1a, which means the members of <code>P.Outer.Nested</code>and its transitive supertypes are looked up.</td></tr>
<tr><td>3.</td><td style='padding-left: 4em;'>Resolution of the <code>P.Outer.Nested</code> class starts (the static imports can also import inherited types)</td></tr>
<tr><td>4.</td><td style='padding-left: 6em;'>Triggers resolution of <code>P.Outer</code>, which is skipped as it is already in progress</td></tr>
<tr><td>5.</td><td style='padding-left: 6em;'>Type checking of <code>I</code>(the <code>implements</code> clause) runs, but <code>I</code>cannot be resolved since it is not in the scope yet.</td></tr>
<tr><td>6.</td><td style='padding-left: 4em;'>Resolution of <code>import P.Q.*</code>starts, which takes all member types of <code>P.Q</code>(including the interface <code>I</code>) and imports them into the current file's scope</td></tr>
<tr><td>7.</td><td>Resolution of <code>P.Outer</code>and other classes continues</td></tr>
</table>

If the imports are swapped then step 6 happens before step 5 and so `I`
is found during step 5.

The above is not the only problem related to `import` handling. The other
known problem is that the bounds of a class's type parameters may validly
refer to possible inner classes of their declaring class.  In some cases,
this currently causes unresolvable cycles, for example:

    package P;

    import static P.Outer.Nested.*;

    public class Outer {
        public static class Nested<T extends I> {
            static class I { }
        }
    }

The envisioned solution to this problem is to split the existing first
phase of `javac`'s member resolution into three: The first will analyze
the enclosing file's imports, the second will only build the
class/interface hierarchy, without any type parameters, annotations,
_etc._, and the third will properly analyze the class headers, including
type parameters.

It is expected that this change will allow `javac` to accept programs
that are currently rejected but not reject ones that are currently
accepted.

Comments
No documentation impact.
23-10-2014

in the goals section, instead of "accepts and rejects" I think you just want "accepts".
03-09-2014

The phrasing of this is a little clumsy: "It is expected that this change will allow javac to accept programs that are currently not rejected, not reject ones that are currently accepted." At a minimum, an "and" in the middle would help. But then, we're left with "It is expected that this change will allow javac to accept programs that are currently not rejected" What is the difference between "accept" and "currently not rejected"? I think you are trying to say that the change will allow javac to accept more programs then before, not less, so maybe "not" is a typo for "now", as in "It is expected that this change will allow javac to accept programs that are currently now rejected, not reject ones that are currently accepted."
03-09-2014

Ok, thanks for the clarification.
26-08-2014

Is there any particular reason this bug report is confidential?
26-08-2014