JDK-4721499 : can a final variable be the operand of ++? --? +=?
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2002-07-26
  • Updated: 2014-02-26
  • Resolved: 2013-12-10
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.
JDK 7 JDK 8
7Fixed 8Fixed
Related Reports
Relates :  
Description
The last paragraph in JLS 15.14.1 suggests that this is illegal

	final int i;
	i++;

but it is not clear if that is a normative rule or intended to be
a consequence of other rules.  For example, one could imagine the
rule not applying in the following code, where i is both DA and DU:

	final int i;
	if (false) i++;

Similarly, section 15.26 (last paragraph) has similar language, but
it seems to make some allowance for blank finals.  Again, it is not
clear if this rule is intended to be normative.

We should have a consistent story for these two paragraphs, and we
should decide and make clear if this restriction is normative or a
consequence of rules elsewhere.  Whichever way this is resolved, 
the compiler will need to be checked for compliance.

Comments
JLS7 15.11.1 was incorrectly updated to say: If the field is static: ... If the field is not final, _or is a blank final and the field access occurs in a __constructor__, then the result is a variable_ ... It should be "in a __static initializer__". The body of a constructor is relevant when the field is not static, i.e. the next top-level bullet.
04-11-2013

PUBLIC COMMENTS The following from 8.1.3 is relevant, though redundant: "A blank final (��4.12.4) field of a lexically enclosing class may not be assigned within an inner class." Currently, 6.5.6 says that final fields are values, period. It should be fixed to say that final fields are variables "in" constructors. It should define "in" such that references from inner classes are excluded. At that point, the above text would still be redundant (but it could reasonably be read as informative, not normative).
05-03-2011

EVALUATION There's a test program below that tries to mutate various kinds of variables under "if (false)" in both a constructor and a method. In some places, javac and Eclipse disagree. In others, they both seem to disagree with the spec. I'm not sure how the previous conclusion that javac 6 prohibits ++ on a blank final local was derived -- that's not what I'm seeing in this test. Summary of observations: - Initialized final fields: Can never be assigned to or incremented. This is consistent with 6.5.6, which says they represent values rather than variables. - Blank final fields: Cannot be assigned to or incremented in method bodies. Can be assigned to in simple assignments in constructor bodies (when DU). Compilers disagree about whether += and ++ are allowed as well in constructor bodies, but are internally consistent between the two cases. From the spec, 6.5.6 says that final fields are values, which prohibits any assignment (clearly this is incorrect). 15.14.2 says that ++ is specifically disallowed, although the reason -- the name does not represent a variable -- is probably not valid once 6.5.6 is adjusted for constructor bodies. - final parameters: javac allows all assignments and increments; Eclipse prohibits them all. I don't think Eclipse's behavior is specified, but it would be reasonable to modify 6.5.6 to say final parameter names are not variables. It's strange that 16.3 talks about parameters, since the 16 intro excludes them from the relation's domain, and I don't think there's any specified error that would occur depending on the definition of DA/DU for parameters. - Initialized final local variables: Can never be assigned to or incremented. 6.5.6 says the name denotes a variable, so I don't think there's a basis for this in the spec; but the spec could be modified to say the name is a value, and then this would be okay. - Blank final local variables: Can always be assigned to or incremented (when DU). Oddly, Eclipse prohibits += and ++ for blank final fields but not for blank final local variables. The observed behavior is consistent with the spec: 6.5.6 says the name is a variable, and 16 says it can be assigned to if DU and referenced if DA. However, this is inconsistent with 15.14.2, if read as a normative statement; the reason given for prohibiting it in 15.14.2 is incorrect, per 6.5.6. - Nested classes (I tested this separately): No mutation is allowed on any final variable captured by a nested class. I cannot find where this is specified. - Order of "real" initialization (that is, initialization in non-dead code) doesn't matter in any case. Blank final fields and local variables can be initialized at the start or the end of the block. - this.foo: Both compilers treat fields the same whether they are referenced directly or with "this.foo". 15.11.1 says that final fields named in this way are values, not variables. But 16 (intro) suggests that "this.foo" is a valid way to initialize a blank final field. I didn't test static fields or static/instance initializers. I imagine the behavior is analogous to what I've seen. Suggested changes: - Initialized final variables, whether fields or local, should be defined in 6.5.6 as values, not variables. (No compiler impact.) - Blank final fields must be defined as variables in 6.5.6 (as "f") and 15.11.1 (as "this.f"), at least in the body of a constructor/initializer. (No compiler impact.) - Final parameters should probably be defined as variables is 6.5.6. (Matches Eclipse, requires a change in javac. Impacts programs with assignments to final parameters in dead code.) - References to local variables and parameters in an enclosing context (from a local/anonymous class) should perhaps always be treated as values, not variables, by 6.5.6. (No compiler impact.) - The 15.14.2 text should either be removed or clarified. As long as the definition of "variable" is clear, everything else should fall out nicely. If we just remove the text, then blank final locals can be incremented in dead code, and blank final fields can be incremented in dead code in constructors. (Matches javac, requires a change in Eclipse. No impact on existing programs.) Alternately, we can explicitly prohibit both += and ++ on final variables. (Requires a change in both compilers. Impacts programs that do += or ++ in dead code on blank final fields or blank final local variables.) Our take on the current spec -- that "++" is illegal for finals but "+=" is okay -- doesn't match the implementations at all, and I'm not keen on introducing that kind of inconsistency unnecessarily. - 16.3 should probably be removed unless we can find a reason for it to exist. (No compiler impact.) --- public class DefinitelyUnassigned { // Comments on compiler behavior are for // javac/eclipse (in that order) final int i; final int j = 4; final int k; public DefinitelyUnassigned(final int x) { i = 8; final int y; y = 1; final int z = 2; final int w; if (false) i=1; // ok/ok if (false) j=1; // bad/bad if (false) k=1; // ok/ok if (false) x=1; // ok/bad if (false) y=1; // ok/ok if (false) z=1; // bad/bad if (false) w=1; // ok/ok if (false) i+=1; // ok/bad if (false) j+=1; // bad/bad if (false) k+=1; // ok/bad if (false) x+=1; // ok/bad if (false) y+=1; // ok/ok if (false) z+=1; // bad/bad if (false) w+=1; // ok/ok if (false) i++; // ok/bad if (false) j++; // bad/bad if (false) k++; // ok/bad if (false) x++; // ok/bad if (false) y++; // ok/ok if (false) z++; // bad/bad if (false) w++; // ok/ok // Both bad/bad (javac won't complain while there are other errors) k = 9; w = 10; } void m(final int x) { final int y; y = 1; final int z = 2; final int w; if (false) i=1; // bad/bad if (false) j=1; // bad/bad if (false) k=1; // bad/bad if (false) x=1; // ok/bad if (false) y=1; // ok/ok if (false) z=1; // bad/bad if (false) w=1; // ok/ok if (false) i+=1; // bad/bad if (false) j+=1; // bad/bad if (false) k+=1; // bad/bad if (false) x+=1; // ok/bad if (false) y+=1; // ok/ok if (false) z+=1; // bad/bad if (false) w+=1; // ok/ok if (false) i++; // bad/bad if (false) j++; // bad/bad if (false) k++; // bad/bad if (false) x++; // ok/bad if (false) y++; // ok/ok if (false) z++; // bad/bad if (false) w++; // ok/ok // bad/bad (javac won't complain while there are other errors) w = 10; } }
04-02-2011

EVALUATION The previous evaluation states: "I'm happier stating that a final variable access yields a value unless it is definitely unassigned, when it yields a variable." JLS 3ed 6.5.6.1 ended up being slightly looser, presumably to avoid mentioning definite [un]assignment. A final field always yields a value, even if it is definitely unassigned. (A field always has a value, of course.) A final local or final parameter always yields a variable, even if it is definitely assigned. Re: "Also all the cases that deal with final local variables would have to make allowances for blank finals." - JLS 3ed 15.14, 15.15 and 15.26 were updated to disallow increment of/decrement of/assignment to a final variable *unless it is a definitely unassigned blank final variable*. It is OK to say that a final variable can't be assigned to unless it is definitely unassigned and blank - this is definitional. But it is odd to allow a DU blank final field to be incremented/decremented, and 6.5.6.1 rules it out since the field evaluation will yield a value rather than a variable. (javac 1.6 disallows it too.) And it is bizarre to allow a DU blank final local to be incremented/decremented, even if 6.5.6.1 technically supports it by yielding a variable. (javac 1.6 disallows it too.) The right thing to do is disallow increment/decrement of final variables altogether, i.e. drop "(unless it is a definitely unassigned blank final variable)" in 15.14.2, 15.14.3, 15.15.1, and 15.15.2. (This takes us back to JLS 2ed.)
09-01-2008

EVALUATION If this rule is normative, the compiler will need to be modified. ###@###.### 2002-07-26 The intent was that this be a consequence of existing rules. Specifically, the evaluation of a final variable yields a value, not a variable. This is specified in JLS 6.5.6.1, 6.5.6.2 and 15.11.1. However, the introduction of blank finals and final locals required the rules to be adapted, an dthis was not consistently done. Mea culpa. In particular, there is a bug in the spec in JLS 6.5.6.1 which gives the meaning of simple expression names. It does not deal with final local variables or parameters. This is an oversight; I should have changed that section when adapting the JLS for nested classes and the related changes. Also all the cases that deal with final local variables would have to make allowances for blank finals. Or, we could drop the distinction between finals and non-finals wrt returning a variable, and rely on DA/DU to prevent such assignments. This would liberalize things, so that such assignments would be allowed in dead code, and is probably what users want. It would be a spec change. But apparently, this is the implemented behavior. We should check whether most compilers agree with the reference implementation on this. ###@###.### 2002-07-26 I'm not sure that allowing assignments to finals in dead code is what people should/would want. I'm also somewhat concerned about the systemic effects of changing the spec so that final variable accesses yield variables wholesale. I'm happier stating that a final variable access yields a value unless it is definitely unassigned, when it yields a variable. This corrects the spec in a localized manner without changing its intent. ###@###.### 2002-08-02
02-08-2002