RFR: 8349132: javac Analyzers should handle non-deferrable errors

Jan Lahoda jlahoda at openjdk.org
Fri Jan 31 12:49:24 UTC 2025


Consider this test script:

$ cat JDK8349132.sh 
mkdir -p JDK-8349132
cd JDK-8349132
cat >Utils.java <<EOF
package test;
public class Utils {
    public static void run(Task<Param> uat) {}
}
interface Task<T> {
    public void run(T t) throws Exception;
}
class Param {}
EOF
cat >Test.java <<EOF
package test;
public class Test {
    private static void test() {
        Utils.run(new Task<Param>() {
            @Override
            public void run(Param parameter) throws Exception {
            }
        });
    }
}
EOF

javac -d out Utils.java
rm out/test/Param.class
javac -XDfind=diamond -XDshould-stop.at=FLOW -classpath out Test.java


It fails with:

$ bash JDK8349132.sh 
Test.java:4: error: cannot find symbol
        Utils.run(new Task<Param>() {
                           ^
  symbol:   class Param
  location: class Test
Test.java:6: error: cannot find symbol
            public void run(Param parameter) throws Exception {
                            ^
  symbol: class Param
Test.java:4: error: cannot access Param
        Utils.run(new Task<Param>() {
             ^
  class file for test.Param not found
3 errors
An exception has occurred in the compiler (21.0.5). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
java.lang.AssertionError: Analyzer error when processing: Utils.run(new Task<Param>(){
    
    () {
        super();
    }
    
    @Override
    public void run(Param parameter) throws Exception {
    }
});:java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.code.Type.getTypeArguments()" because "com.sun.tools.javac.util.List.get(int).type" is null
jdk.compiler/com.sun.tools.javac.comp.Analyzer$DiamondInitializer.process(Analyzer.java:258)
jdk.compiler/com.sun.tools.javac.comp.Analyzer$DiamondInitializer.process(Analyzer.java:228)
jdk.compiler/com.sun.tools.javac.comp.Analyzer.doAnalysis(Analyzer.java:577)
jdk.compiler/com.sun.tools.javac.comp.Analyzer$2.flush(Analyzer.java:547)
jdk.compiler/com.sun.tools.javac.comp.Analyzer.flush(Analyzer.java:591)
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1425)
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1393)
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:976)
jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319)
jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178)
jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64)
jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50)
        at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:162)
        at jdk.compiler/com.sun.tools.javac.comp.Analyzer.doAnalysis(Analyzer.java:579)
        at jdk.compiler/com.sun.tools.javac.comp.Analyzer$2.flush(Analyzer.java:547)
        at jdk.compiler/com.sun.tools.javac.comp.Analyzer.flush(Analyzer.java:591)
        at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1425)
        at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1393)
        at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:976)
        at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:319)
        at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:178)
        at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:64)
        at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:50)
printing javac parameters to: /tmp/JDK-8349132/javac.20250131_133856.args


The reason for this is as follows: Some errors reported by javac have the "NON_DEFERRABLE" flag, which means the error should not be stored in `DeferredDiagnosticHandler`, but rather should be reported immediately.

This is particularly the case when e.g. entering the sources before annotation processing - the errors found during this enter run may be resolved by the AP. So, ordinary errors are stored in `DeferredDiagnosticHandler` and reported later if needed. Non-deferrable errors are not stored, but reported immediately, as those are typically serious errors that cannot be fixed by AP.

javac also has an `Analyzer`, which looks for code simplifications by speculatively simplifying the code, and re-attributing it to see if it would still compile. It is using `DeferredDiagnosticHandler`, so that if any errors are found during the speculative attribution, the code simplification is rejected.

But, if a non-deferrable error is reported during the speculative attribution, it is passed through `DeferredDiagnosticHandler` undetected, and the `Analyzer` may try to do further evaluations - which may crash the compiler.

The proposal in the PR is to catch all errors while doing this speculative attribution for the `Analyzer`. As a consequence, the speculative code rewrite is found to be problematic, is rejected, and there's no crash.

Note this will also avoid the third error reported in the above case - that error comes directly from the speculative attribution, and it seems incorrect to report it.

-------------

Commit messages:
 - 8349132: javac Analyzers should handle non-deferrable errors

Changes: https://git.openjdk.org/jdk/pull/23387/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=23387&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8349132
  Stats: 155 lines in 3 files changed: 151 ins; 1 del; 3 mod
  Patch: https://git.openjdk.org/jdk/pull/23387.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/23387/head:pull/23387

PR: https://git.openjdk.org/jdk/pull/23387


More information about the compiler-dev mailing list