RFR: 8294461: wrong effectively final determination by javac

Archie L. Cobbs duke at openjdk.org
Tue Oct 25 15:45:34 UTC 2022


This bug involves DA/DU analysis and the concept of "effectively final".

Here's the test case, which the compiler currently but incorrectly accepts:

    for (int i = 0; i < 10; i++) {
        Runnable r = () -> System.out.println(i);   // variable i is NOT effectively final
        break;                                      // even though "i++" is never reached
    }


For the purposes of "effectively final", it doesn't matter whether an assignment statement that makes a variable no longer effectively final is actually reachable or not. In cases like the `i++` above, a statement can be actually unreachable yet not "unreachable" according to the JLS, and so it does not generate an "unreachable statement" compiler error (which would otherwise hide this bug).

JLS §4.12.4 states:

> A local variable declared by a statement and whose declarator has an initializer ... is effectively final if all of the following are true:
> ...
> • It never occurs as the operand of a prefix or postfix increment or decrement operator (§15.14, §15.15).

So clearly `i` is not effectively final.

However, the way we calculate effective finality does not involve actually looking for increment/decrement operators. Instead, it's determined during DA/DU analysis: non-final variables have an `EFFECTIVELY_FINAL` flag which is initialized to `true` and then cleared if/when we encounter contrary evidence - e.g., an assignment when not DU.

For simplicity, the DA/DU analysis works like this:
* Variables with initializers are treated like variables without initializers followed by an assignment
* Increment/decrement operators are treated as a normal read followed by a normal assignment.

These are reasonable. However, it means this clause of JLS §4.12.4 effectively applies:

> A local variable declared by a statement and whose declarator lacks an initializer is effectively final if all of the following are true:
> ...
> • Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment...

The bug with the current code is that when we see an assignment to a variable with the `EFFECTIVELY_FINAL` flag still set, we clear the flag if the variable is not DU, but we are _not_ clearing the flag if the variable is DA, as required above. This happens with the `i++` statement because, by virtue of it being actually unreachable, `i` is both DA and DU before that statement.

This patch corrects that omission.

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

Commit messages:
 - A for loop variable is not effectively final even if the loop never increments.

Changes: https://git.openjdk.org/jdk/pull/10856/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=10856&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8294461
  Stats: 23 lines in 3 files changed: 17 ins; 2 del; 4 mod
  Patch: https://git.openjdk.org/jdk/pull/10856.diff
  Fetch: git fetch https://git.openjdk.org/jdk pull/10856/head:pull/10856

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


More information about the compiler-dev mailing list