RFR: 8264258: Unknown lookups in the java package give misleading compilation errors

Jan Lahoda jlahoda at openjdk.java.net
Tue Apr 20 08:29:09 UTC 2021

On Tue, 20 Apr 2021 06:42:45 GMT, Srikanth Adayapalam <sadayapalam at openjdk.org> wrote:

>> Unknown lookups in the java package give misleading compilation errors
> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java line 4064:
>> 4062:             if (!location.name.isEmpty()) {
>> 4063:                 if (location.kind == PCK && !site.tsym.exists() && location.name != names.java) {
>> 4064:                     return diags.create(dkind, log.currentSource(), pos,
> This works, but I am not sure it is the fix we want. I would be curious to find out why site.tsym.exists() returns false for the java PackageSymbol. What I find with some preliminary debugging is that we end up creating a PackageSymbol for `java' that is distinct from the PackageSymbol java that is the owner of the PackageSymbol java.lang.
> In this block of code
> // Import-on-demand java.lang.
>                 PackageSymbol javaLang = syms.enterPackage(syms.java_base, names.java_lang);
>                 if (javaLang.members().isEmpty() && !javaLang.exists())
>                     throw new FatalError(diags.fragment(Fragments.FatalErrNoJavaLang));
>                 importAll(make.at(tree.pos()).Import(make.QualIdent(javaLang), false), javaLang, env);
> com.sun.tools.javac.comp.TypeEnter.ImportsPhase#resolveImports
> at the importAll call if you have a breakpoint and query the javaLang.exists() and javaLang.owner.exists() they both answer true.
> So it looks fishy to me that we somehow end up with a distinct PackageSymbol for java that answers false for exists() even as there exist other symbols that answer true.
> If you debug through javac as it attributes:
> Object o1 = java.lang.String.class;
> while attributing the FieldSelect node you can see that the PackageSymbol for java.lang has a owner that is distinct from the PackageSymbol for siteSym - so we do right thing by coalescing the symbols somehow - how is what needs further analysis.

There is an issue with the PackageSymbol for `java`: it is not clear what is the correct PackageSymbol. There is no module that would export package `java`. There are multiple modules that export direct sub-packages of `java` (e.g. java.base and java.desktop). In case of package `java`, we could say the PackageSymbol should be from java.base, as that is the most base module that contains subpackages of `java`, but that does not work generally. Consider, e.g., three modules: m1 exporting api.a, m2 exporting api.b (with no relation between m1 and m2), and m3 using both m1 and m2. There is no way to have a PackageSymbol for `api` in `m3` such that it would be the same time the owner of `api.a` in `m1` and `api.b` in `m2`. So, what javac is doing is that it synthesizes a new PackageSymbol in the current module (for `java` or `api`). Most of the few places where this caused issues were hopefully fixed, so this may be one of the remaining. 

One thing we could try is to look into the `visiblePackages` of a reasonable module and see if any of them is existing and is a sub-package of the current package (there may be a nicer way to do this, tough):

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
index db416413588..6e4db4bc49f 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
@@ -4060,7 +4060,8 @@ public class Resolve {
                 location = site.tsym;
             if (!location.name.isEmpty()) {
-                if (location.kind == PCK && !site.tsym.exists()) {
+                if (location.kind == PCK && !site.tsym.exists() &&
+                    location.packge().modle.visiblePackages.values().stream().filter(ps -> ps.exists()).flatMap(this::owners).noneMatch(ps -> ps.flatName() == site.tsym.flatName())) {
                     return diags.create(dkind, log.currentSource(), pos,
                         "doesnt.exist", location);
@@ -4087,6 +4088,23 @@ public class Resolve {
         private Object args(List<Type> args) {
             return args.isEmpty() ? args : methodArguments(args);
+        private Stream<Symbol> owners(PackageSymbol ps) {
+            Iterable<Symbol> ownersIt = () -> new Iterator<>() {
+                private Symbol current = ps.owner;
+                @Override
+                public boolean hasNext() {
+                    return !current.name.isEmpty();
+                }
+                @Override
+                public Symbol next() {
+                    Symbol result = current;
+                    current = current.owner;
+                    return result;
+                }
+            };
+            return StreamSupport.stream(ownersIt.spliterator(), false);
+        }
         private String getErrorKey(KindName kindname, boolean hasTypeArgs, boolean hasLocation) {
             String key = "cant.resolve";


PR: https://git.openjdk.java.net/jdk/pull/3428

More information about the compiler-dev mailing list