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