Seemingly incorrect definite assignment rules regarding try-catch and try-catch-finally
Alex Buckley
alex.buckley at oracle.com
Fri Jun 10 17:02:24 UTC 2022
Filed https://bugs.openjdk.org/browse/JDK-8288230
On 5/4/2022 4:54 PM, Robbe Pincket wrote:
> Hello all
>
> It seems that with moving around some sentences, some errors slipt in.
> * In issue 3b, I mention “issue 2 also applies …”. This is supposed to mention issue 3
> * The example for issue 3 is not correct, and doesn’t show the issue, here is a correct version:
>
> ```java
> public void testIssue3(int x) {
> int var7;
>
> while (true) {
> try {
> if(x > 0) {
> break;
> }
> } finally {
> var7 = 100;
> }
> }
>
> // according to the spec, var7 is not considered to be
> // definite assigned, even though `var7 = 100` would
> // have been executed
> // javac does not give an error
> System.out.println(var7);
> }
> ```
>
> Greetings
> Robbe
>
> From: Robbe Pincket<mailto:robbepincket at live.be>
> Sent: woensdag 4 mei 2022 19:46
> To: jls-jvms-spec-comments at openjdk.java.net<mailto:jls-jvms-spec-comments at openjdk.java.net>
> Subject: Seemingly incorrect definite assignment rules regarding try-catch and try-catch-finally
>
> 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