| Summary: | Problem with final variable initialization and unreachable code | ||
|---|---|---|---|
| Product: | [Eclipse Project] JDT | Reporter: | Felipe Pontes <felipepontes> |
| Component: | Core | Assignee: | 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: | |||
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.
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. (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. Verified for 4.7 M1 using Oxygen (4.7) I20160801-2000 build |
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