|
Relates :
|
|
|
Relates :
|
|
|
Relates :
|
|
|
Relates :
|
|
|
Relates :
|
|
|
Relates :
|
|
|
Relates :
|
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.
|