Integrated: 8294461: wrong effectively final determination by javac

Archie L. Cobbs duke at openjdk.org
Thu Oct 27 16:01:31 UTC 2022


On Tue, 25 Oct 2022 15:36:05 GMT, Archie L. Cobbs <duke at openjdk.org> wrote:

> 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.

This pull request has now been integrated.

Changeset: b8ad6cd9
Author:    Archie L. Cobbs <archie.cobbs at gmail.com>
Committer: Vicente Romero <vromero at openjdk.org>
URL:       https://git.openjdk.org/jdk/commit/b8ad6cd98a7e4b577b888dc5f9d93c2e4d3bf177
Stats:     23 lines in 3 files changed: 17 ins; 2 del; 4 mod

8294461: wrong effectively final determination by javac

Reviewed-by: vromero

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

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


More information about the compiler-dev mailing list