JDK-8193904 : Uninitialized final field access and qualified this
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2017-12-20
  • Updated: 2022-12-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
JLS 9 §16 includes:

> Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

> An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (§15.26.1).

javac 9.0.1+11 rejects uninitialized accesses like the following, as expected:

class A {
  final int x;
  A() {
    System.err.println(x); // x might not have been initialized
    System.err.println(this.x); // x might not have been initialized
    x = 42;
  }
}

However it accepts accesses using qualified this, e.g.:

class A {
  final int x;
  A() {
    System.err.println(A.this.x);
    x = 42;
  }
}

Should accesses to fields using qualified this be rejected?
Comments
The bug report seems ill-formed. Of course the answer to "Should accesses to fields using qualified this be rejected?" is "no", since there are many legitimate uses of qualified this to access fields. If I read between the lines, the reporter seems perturbed that there are ways to access uninitialized blank final fields, and but proposes, depending on the answer to the question, either to allow a new path by which such surprising access can be performed, or else to try to remove an existing path. Neither would be a good change, if it consists simply of a tweaking of the settled JLS rules about qualified "this". The rest of this comment will explain why things are the way they are, and what a true improvement over the definite (un-)assignment rules might look like. Java's rules for discouraging access to uninitialized variables are, by design, not exhaustive. (…Except for locals; you absolutely cannot access an uninitialized local.) They are all based on static analysis: If the compiler knows it is certain (or highly likely) that you are asking for a variable's value before it is uninitialized, you will be told to fix your code. But "fixing" your code could involve defeating the compiler's static analysis by obfuscating the analysis. We don't encourage users to do this, but we absolutely cannot prevent them, because no static analysis is strong enough to predict uninitialized access, if that access is complicated enough. If you put your variable access in a helper method, Java does not try to figure out if you are calling it too early; solving such a problem in general, at compile time, requires solving the Halting Problem. No amount of jiggling around the details of the JLS will fix this, and the JLS authors knew it well. Instead, they chose a small set of rules that help users statically detect common bugs. These rules only apply when the user is not actively trying to subvert the rules by obfuscating the code somehow. In the present case, the rather arbitrary choice was made (by Bill Joy and me, IIRC, back in Java 1.1 times) to perform static checking of blank final non-static field initialization by tracking uses of either the bare field name "x" or the simply qualified field name "this.x" but by no other, more complex expression. In other words, this "bug" is an intentional feature in the design of the JLS. Why do this, if it perturbs other parts of the language design? Well, we wanted to allow only the bare field name "x" to participate in the static analysis of DU/DA; this would be the simplest formulation of the DU/DA rules. We added exactly "this.x" as an alternative in order to respect the reasonable practice of giving constructor parameters and fields the same names (a practice confirmed by Java records). But keeping this names tracked by DU/DA as simple as possible is important, because they are already complex enough that the compiler has to train the user (with error messages like the one this bug reports) how to stay within the bounds of DU/DA correctness. Arbitrarily adding a more complex grammar of names that DU/DA works on adds to the complexity of an already hard-to-predict analysis. It makes code harder to read and mentally validate, in particular. In conclusion, there is no good path to improving the static analysis of uninitialize variable references, that consists of a one or more tweaks like this report suggests. On the other hand, a series of one or more such tweaks would make correct DU/DA code harder to specify and read with confidence, due to added complexity in a subsystem which is as radically simple as possible, by design. In this case, as in many case, global consistency, just for its own sake, is an anti-pattern. There is good research into extending DU/DA analysis (statically) through simple call chains that originate in the constructor. See Liu at al, "A Type-and-Effect System for Object Initialization", for example. So far, enhanced static analyses seem to be too complex to adopt in their totality into the JLS. But such research may uncover more tactics that are useful. The most conclusive way to improve on uninitialized variable checking, if we decide that is a priority, would be to use (what we have not yet used at all) dynamic initialization checks on fields. This would require both JLS and JVMS changes, but would provide a path (if we wished to take it) to make exclusion of uninitialized values airtight for all variables, not just locals. A tracking RFE for this sort of thing is JDK-8297156.
02-12-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/10956 Date: 2022-11-02 19:04:45 +0000
02-11-2022