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