Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 496511

Summary: Problem with final variable initialization and unreachable code
Product: [Eclipse Project] JDT Reporter: Felipe Pontes <felipepontes>
Component: CoreAssignee: Stephan Herrmann <stephan.herrmann>
Status: VERIFIED INVALID QA Contact:
Severity: normal    
Priority: P3 CC: stephan.herrmann
Version: 4.4.2   
Target Milestone: 4.7 M1   
Hardware: PC   
OS: Linux   
Whiteboard:

Description Felipe Pontes CLA 2016-06-21 16:49:37 EDT
Final variable is not initialized because of unreachable code but eclipse compiler only presents dead code warning.


Program

	class A {
		final int x;
		A() throws Exception {
			if (true) {
				throw new Exception();
			}
			x = 10;
		}
	}

Result

	----------
	1. WARNING in A.java (at line 7)
		x = 10;
		^^^^^^
	Dead code
	----------
	1 problem (1 warning)


Configuration

	Software

		O.S.: Linux Ubuntu 14.04 64bits
		ECJ: org.eclipse.jdt.core_3.10.2.v20150120-1634.jar (Eclipse Luna 4.4.2)

	Hardware: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Comment 1 Stephan Herrmann CLA 2016-06-28 14:09:53 EDT
I can't find any version of any compiler (ejc nor javac) that rejects this example.

I believe this code is "mostly OK" because the instance of A with uninitialized x can not normally be accessed by any code. A client assignment "a = new A()" will not complete, i.e., never assign anything to 'a'.
Of course, the half-initialized instance of A could leak to other code e.g. as a field in the exception:

//---
    class MyException extends Exception {
      public A a;
      public MyException(A a) {
        super();
        this.a = a;
      }
    }
    class A {
        final int x;
        A() throws MyException {
            if (true) {
                throw new MyException(this);
            }
            x = 10;
        }
        public static void main(String... args) {
          try {
            A a = new A();
          } catch (MyException me) {
            System.out.print(me.a.x);
          }
        }
    }
//---

prints the uninitialized field x.

But this is just the normal craze of messing with an object during its own initialization, I believe.

I haven't checked this against JLS, so if anyone can reason based on JLS why this should be rejected: in that case feel free to re-open.
Comment 2 Felipe Pontes CLA 2016-07-05 06:59:27 EDT
The Java Language Specification 8 presents in section 8.3.1.2 (final Fields) the following note:

"A blank final instance variable must be definitely assigned at the end of every
constructor of the class in which it is declared, or a compile-time error occurs..."

At the end of every constructor a blank final instance must be assigned. In the above example the final field "x" can be accessed even if it is uninitialized. In this way I think implementation doesn't match JLS.
Comment 3 Stephan Herrmann CLA 2016-07-05 15:04:10 EDT
(In reply to Felipe Pontes from comment #2)
> The Java Language Specification 8 presents in section 8.3.1.2 (final Fields)
> the following note:
> 
> "A blank final instance variable must be definitely assigned at the end of
> every
> constructor of the class in which it is declared, or a compile-time error
> occurs..."

Yes, and the entire section 16 of JLS defines what they mean by "definitely assigned".

Have a look at 16.2.13., which basically says:
- x is assigned after "throw new Exception()"
- x is unassigned after "throw new Exception()"
i.e., both statements are equally true, and they explain:

"The notion that a variable is "[un]assigned after" a statement or expression really means "is [un]assigned after the statement or expression completes normally". Because a break, continue, return, or throw statement never completes normally, it vacuously satisfies this notion."

Next look at 16.2.7.
"V is [un]assigned after if (e) S iff V is [un]assigned after S and V is [un]assigned after e when false."

So, when we ask is "x" assigned after the if statement in comment 0, we need two checks:
- is x assigned after "throw new Exception()"?
- is x assigned after "true" when the latter evaluates to false?
both conditions (surprisingly?) hold true.
(In particular it is true to say: "(true == false) implies (x == 42)" :) )
Hence saying "x is assigned after the if" is correct, meaning: when the if statement completes normally, x will be assigned. Again: ex falso sequitur quodlibet.

> At the end of every constructor a blank final instance must be assigned. In
> the above example the final field "x" can be accessed even if it is
> uninitialized. In this way I think implementation doesn't match JLS.

In comment 0 no runtime problem can be observed, JLS mandates to accept the code => all is fine.

In comment 1 analysis finds a proper assignment on all paths where the constructor completeness normally -> JLS mandates to accept the code. Still a runtime problem can observed, but not because x was not assigned at the end of the constructor, but because a semi-initialized instance of A leaked to the outside. Several patterns exist, how such leaking may happen, but those have nothing to do with the seeming problem in comment 0.
Comment 4 Sasikanth Bharadwaj CLA 2016-08-02 05:17:48 EDT
Verified for 4.7 M1 using Oxygen (4.7) I20160801-2000 build