Seemingly incorrect definite assignment rules regarding try-catch and try-catch-finally

Robbe Pincket robbepincket at live.be
Wed May 4 17:46:10 UTC 2022


Hello experts

I was reading the specifications today regarding definite (un)assignment rules (Chapter 16), to confirm a mental image I had of the control flow logic while trying to fix bug in the java decompiler I help maintaining. However when checking the spec it seemed like my assumption were not correct. After checking whether the code I though should be valid, but should according to my interpretation of the spec, and seeing that in fact it does compile, I started reading the whole definite assignment specification a few times.

It has happened before that I thought there was an error in the spec, but after rereading it again, or checking for other relevant specifications regarding the topic, I usually notice that I had misread, or just missed part of the specification. Sadly when doing this for the issue at hand, I seemed to only notice more issues with the relevant specs.

So the issues I noticed:
1.
Both the "unassigned before a catch" and the "unassigned before a finally" rule (16.2.15.) mention the following:
> V is definitely unassigned before every return statement that belongs to the try block.
However V could get assigned inside the expression of the return if there is such an expression
I think the following line should be added:
> V is definitely unassigned after e in every statement of the form return e that belongs to the try block.
The original line could get update to mention only returns without an expression, but this is not needed to be correct (I think)

2.
Both the "unassigned before a catch" and the "unassigned before a finally" rule (16.2.15.) seem to not take yield statements into consideration.
I think the following line should be added:
> V is definitely unassigned after e in every statement of the form yield e that belongs to the try block.

3. (the original issue)
All of the following:
* 16.2.5. Labeled Statements
* 16.2.9. switch Statements
* 16.2.10. while Statements
* 16.2.11. do Statements
* 16.2.12. for Statements
All mention something along the lines of
> V is [un]assigned before every break statement for which the ... statement is the break target.
However, this does not seem to take into account that a "break" (as 14.15. mentions) only "attempts to transfer control", because if the break "jumps" out of a try with a finally, that finally block gets run first. This finally block can assign values to variables, changing which variables should be considered definite assigned and which ones are definite unassigned.
It seems that javac understands this, even though this seems to be contradicting the specs.

3b.
issue 2 also applies to try Statements (16.2.15.) with a nested try finally with regards to definite unassignment, but with all statements that could break out of the try block/catch block (return, throw, assert, break, yield and continue)

I have an example for each of these below.
If I made any errors in interpreting the specifications I’d be happy to hear it.

Greetings
Robbe

```java
class Examples {
    public int testIssue1(int x) {
        final int var7;

        try {
            return var7 = 8;
        } finally {
            // according to the spec, var7 is considered to be
            // definite unassigned, even though `var7 = 8` could
            // (and would) have been executed
            // javac does give an error
            var7 = 100;
        }
    }

    public void testIssue2(int x) {
        final int var7;

        int y = switch(x) {
            case 1 -> var7 = 0;
            default -> {
                try {
                    yield var7 = 8;
                } finally {
                    // according to the spec, var7 is considered to be
                    // definite unassigned, even though `var7 = 8` could
                    // (and would) have been executed
                    // javac does give an error
                    var7 = 100;
                }
            }
        };
    }

    public int testIssue3(int x) {
        final int var7;

        while (true) {
            try {
                return var7 = 8;
            } finally {
                // according to the spec, var7 is considered to be
                // definite unassigned, even though `var7 = 8` could
                // (and would) have been executed
                // javac does give an error
                var7 = 100;
            }
        }
    }

    public void testIssue3b(int x) {
        final int var7;

        l: try {
            try {
                break l;
            } finally {
                var7 = 8;
            }
        } finally {
            // according to the spec, var7 is considered to be
            // definite unassigned, even though `var7 = 8` could
            // (and would) have been executed
            // javac does give an error
            var7 = 100;
        }
    }
}
```



More information about the jls-jvms-spec-comments mailing list