Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8299416

javac complains about "local variables referenced from an inner class must be final or effectively final" although variable is only assigned once

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P3 P3
    • 25
    • 20, 21, 22, 23, 24
    • tools
    • openjdk version "20-ea" 2023-03-21
      OpenJDK Runtime Environment (build 20-ea+29-2280)
      OpenJDK 64-Bit Server VM (build 20-ea+29-2280, mixed mode, sharing)

    • b22
    • 20
    • generic
    • generic

      When we updated the preview builds on Apache Lucene's nightly runs from Java 20-ea+17 to Java 20-ea+29, one of our test classes no longer compiled: https://github.com/apache/lucene/blob/c180c5cdabed7a64783515ec3086a3f7965b8cc6/lucene/replicator/src/test/org/apache/lucene/replicator/nrt/TestStressNRTReplication.java

      I extracted the essence of this class (one method) into a new file and replaced some external packages/classes by dummy values or java.lang.Object. So please ignore if the code is "senseful", it should just show the problem, it won't run. It is only there to compile.

      When compiling the attached CompileFailure.java with Java 20-ea+29, it fails like the following:

      $ javac CompileFailure.java
      CompileFailure.java:269: error: local variables referenced from an inner class must be final or effectively final
                      if (childLog != null) {
                          ^
      CompileFailure.java:271: error: local variables referenced from an inner class must be final or effectively final
                          childLog.write("process done; exitValue=" + exitValue + "\n");
                          ^
      CompileFailure.java:272: error: local variables referenced from an inner class must be final or effectively final
                          childLog.close();
                          ^
      CompileFailure.java:282: error: local variables referenced from an inner class must be final or effectively final
                        if (childLog != null) {
                            ^
      CompileFailure.java:289: error: local variables referenced from an inner class must be final or effectively final
                                  + childLog
                                    ^
      5 errors

      When looking at the code, "childLog" is only assigned once with an if/else statement and never changed later. Previous versions (Java 8, 11, 17, and also Java 20-ea+17) compile that file sucessfully. It can be easily tested with above command line.

      To my understanding, the code is valid and the variable *is* effectively final.

      As workaround you can declare the variable as final and then compilation passes.

      This is a major regression, as the code looks not too complicated and patterns like this may happen more often in productive code out there (declaring variable without final and assign a value with if/else and later use it in anonymous class). I have to say, that it was not easily to reproduce with a simple 10-liner (only the if/else and the Runnable), but still this looks like a big problem.

      In Apache Lucene we fixed this by adding a final before the variable: https://github.com/apache/lucene/commit/e32b95e3161cbc2abf68d6e3bb3f609ebaa5b6ea

      The problem seems to be caused by JDK-8294461, which was added in build 22. That is the only commit in javac that touched the code around "effectively final". The code mentoned here does not have the described problem (as far as I understand).

            jlahoda Jan Lahoda
            uschindler Uwe Schindler
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: