jspecify: Crash during compilation on JDK 8 when using JSpecify 0.3.0-alpha-1

NullAway branch is here:

https://github.com/uber/NullAway/tree/update-jspecify

This is the crash (see here), which comes when running ./gradlew :test-java-lib:compileJava on a JDK 8 JVM:

warning: unknown enum constant java.lang.annotation.ElementType.MODULE
warning: unknown enum constant java.lang.annotation.ElementType.MODULE
An exception has occurred in the compiler (1.8.0_345). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.
java.lang.AssertionError: annotationType(): unrecognized Attribute name MODULE (class com.sun.tools.javac.util.UnsharedNameTable$NameImpl)
        at com.sun.tools.javac.util.Assert.error(Assert.java:133)
        at com.sun.tools.javac.code.TypeAnnotations.annotationType(TypeAnnotations.java:231)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.separateAnnotationsKinds(TypeAnnotations.java:294)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.visitMethodDef(TypeAnnotations.java:1066)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:778)
        at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.scan(TypeAnnotations.java:275)
        at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:57)

From #34 I can’t tell if this was intended or not. Do we not support using JSpecify annotations when building on JDK 8? Building on JDK 8 is not a requirement for NullAway (we’re planning to remove JDK 8 support soon); I am just wondering if this was intentional.

About this issue

Commits related to this issue

Most upvoted comments

Update: thanks to help from @cushon and @cpovirk, we discovered this bug still exists in the latest version of javac if you create a .class file for an annotation with an invalid ElementType in its @Target meta-annotation. This has been filed as a new bug against JDK 19:

https://bugs.openjdk.org/browse/JDK-8296010

Our hope is that the new bug gets fixed in the latest Java version, and that eventually we can backport the fix all the way to JDK 8.

Seen in the wild: https://stackoverflow.com/a/76254522/28465

I will continue feeling bad about this and eventually do something about it.

Oh, thanks for that report. It would be nice if we had a workaround like you describe, but I’m not excited about trying to figure one out right now… 😃

As it happens, I did release 0.3.0-alpha-3 (because I missed a problem with our module-info). And while I forgot to mention it in the initial release notes, it does additionally contain the change you’re looking for. I eventually noticed that and updated https://github.com/jspecify/jspecify/releases/tag/v0.3.0-alpha-3 to mention it.

  • NEW: Maybe we can even recommend continuing to build with JDK8 but switching to use the JDK8-compatible javac9 codebase, as we do in Guava for other reasons?

I really like this as a temporary workaround! It’s probably the least disruptive option for those who still need to build using JDK8. I just used it to unblock https://github.com/uber/NullAway/pull/673 and get the build green on JDK 8 there. And Error Prone’s javac9 is battle tested and reliable. Hopefully in the medium term, we will get a fix backported to JDK 8 so even this workaround is not needed. And then the only remaining issue is not being able to compile with -Werror on JDK 8, which I think we’ll just have to live with.

Trying to summarize where we are, which we discussed some in today’s meeting:

  • We’ll of course want to document this incompatibility, along with the workaround of using a newer JDK to build with --release 8 or similar (though that’s not always trivial).
    • NEW: Maybe we can even recommend continuing to build with JDK8 but switching to use the JDK8-compatible javac9 codebase, as we do in Guava for other reasons?
  • Even once the incompatibility is fixed, we’ll want to document that people need to upgrade to <whichever JDK8 patch release> to pick up the fix, and we’ll want to document that -Werror will still be a no-no unless you upgrade to JDK9+.
  • We should write something about our the versions of JDK and Android that we support more generally (build and runtime, with whatever caveats we need to give).
  • There aren’t any wildly appealing workarounds available on the JSpecify side:

I do indeed have an openjdk.org account. Let me know if “submit additional info” doesn’t produce the desired results and I can comment on the bug.

I will report this upstream and then report back. Let’s hope for a positive response 😃

Thank you! That is a better thought than any of the thoughts in my post 😃

It does seem likely that upstream would like the compiler to not crash, regardless of how strange the code we feed it is.

It’s possible that they could respond by changing the crash into a more normal but still fatal error, or it’s possible that they don’t deem this fix worth backporting. But maybe they’ll see the value in making it easier for libraries to use the MODULE target. (As noted in #245, we tried solving this problem with multi-release jars instead. But it’s not clear if multi-release jars are supposed to solve this problem, and regardless, they don’t under some major tools today.)

Reproducing does look easy. If you’re up for taking things to upstream, that would be terrific.

$ tail -n +1 *.java 
==> A.java <==
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.MODULE;

import java.lang.annotation.Target;

@Target({METHOD, MODULE})
@interface A {}

==> B.java <==
class B {
  @A void b() {}
}
$ $HOME/openjdk-11.0.16/bin/javac -source 8 -target 8 -sourcepath doesnotexist A.java && $HOME/openjdk-8u342/bin/javac -cp . -sourcepath doesnotexist B.java
warning: [options] bootstrap class path not set in conjunction with -source 8
1 warning
warning: unknown enum constant ElementType.MODULE
An exception has occurred in the compiler (1.8.0_342). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.
java.lang.AssertionError: annotationType(): unrecognized Attribute name MODULE (class com.sun.tools.javac.util.SharedNameTable$NameImpl)
        at com.sun.tools.javac.util.Assert.error(Assert.java:133)
        at com.sun.tools.javac.code.TypeAnnotations.annotationType(TypeAnnotations.java:231)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.separateAnnotationsKinds(TypeAnnotations.java:294)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.visitMethodDef(TypeAnnotations.java:1066)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:778)
        at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.scan(TypeAnnotations.java:275)
        at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:57)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.visitClassDef(TypeAnnotations.java:1042)
        at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:693)
        at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
        at com.sun.tools.javac.code.TypeAnnotations$TypeAnnotationPositions.scan(TypeAnnotations.java:275)
        at com.sun.tools.javac.code.TypeAnnotations$1.run(TypeAnnotations.java:127)
        at com.sun.tools.javac.comp.Annotate.flush(Annotate.java:152)
        at com.sun.tools.javac.comp.Annotate.enterDone(Annotate.java:129)
        at com.sun.tools.javac.comp.Enter.complete(Enter.java:512)
        at com.sun.tools.javac.comp.Enter.main(Enter.java:471)
        at com.sun.tools.javac.main.JavaCompiler.enterTrees(JavaCompiler.java:982)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:857)
        at com.sun.tools.javac.main.Main.compile(Main.java:523)
        at com.sun.tools.javac.main.Main.compile(Main.java:381)
        at com.sun.tools.javac.main.Main.compile(Main.java:370)
        at com.sun.tools.javac.main.Main.compile(Main.java:361)
        at com.sun.tools.javac.Main.compile(Main.java:56)
        at com.sun.tools.javac.Main.main(Main.java:42)