Java Forum / General / May 2006
Incorrect "variable might not have been initialized"
Lee Fesperman - 26 Apr 2006 09:59 GMT Sun's 1.4 javac gave me a "variable might not have been initialized" error on the code below ... when it shouldn't have.
I don't have 1.5 installed. Could someone try it on 1.5 for me?
Code:
public class Test120 { public static void main(String[] args) { boolean retain ; long physicalId ; String ref ; if (retain = (physicalId = 1) > 0 && (ref = String.valueOf(physicalId)) != null) System.out.println(ref) ; } }
1.4 javac output:
Test120.java:10: variable ref might not have been initialized System.out.println(ref) ; ^ 1 error
If Test120 works on 1.5, try this one:
public class Test121 { public static void main(String[] args) { Long id ; boolean retain ; long physicalId ; String ref ; if (retain = (id = new Long(1)) != null && (physicalId = id.longValue()) > 0 && (ref = String.valueOf(physicalId)) != null) System.out.println(ref) ; } }
Lastly, this one works on 1.4 but not 1.2, so Sun seems to be making progress in this area:
public class Test122 { public static void main(String[] args) { boolean retain ; String ref ; if (retain = 1 > 0 && (ref = System.getProperty("user.home")) != null) System.out.println(ref) ; } }
Thanks.
 Signature Lee Fesperman, FFE Software, Inc. (http://www.firstsql.com) ============================================================== * The Ultimate DBMS is here! * FirstSQL/J Object/Relational DBMS (http://www.firstsql.com)
Patrick - 26 Apr 2006 10:55 GMT Lee Fesperman a écrit :
> 1.4 javac output: > > Test120.java:10: variable ref might not have been initialized > System.out.println(ref) ; Same error with 1.5.
> Lastly, this one works on 1.4 but not 1.2, so Sun seems to be making progress in this > area: [quoted text clipped - 9 lines] > } > } This one may work because the compiler detects here that ref is always assigned a value. But this is not always the case, this is why the error message says "*might* not".
 Signature Patrick
Chris Uppal - 26 Apr 2006 12:25 GMT > Sun's 1.4 javac gave me a "variable might not have been initialized" > error on the code below ... when it shouldn't have. The compiler is correct. The JLS lays down very specific rules for when a variable is deemed to be "definitely initialised", and your code doesn't satisfy those conditions. See JLS Section 16.
I know that "ref" /will/ have been initialised, and so do you, but that has nothing to do with the rules which define the compiler's behaviour.
> I don't have 1.5 installed. Could someone try it on 1.5 for me? I've just checked (JDK 1.5.0_06-b05), same thing happens.
> If Test120 works on 1.5, try this one: Also rejected.
> Lastly, this one works on 1.4 but not 1.2, so Sun seems to be making > progress in this area: [quoted text clipped - 9 lines] > } > } After constant-folding "1 > 0", the compiler interprets that as:
if (retain = ( (ref = System.getProperty("user.home")) != null )) System.out.println(ref);
and, correctly, is quite happy with it. Add extra brackets to change the meaning:
if ((retain = 1 > 0) && (ref = System.getProperty("user.home")) != null) System.out.println(ref) ;
and the compiler... ... accepts it !
I believe that to be incorrect under the rules for definite assignment (Section 16.1.1 -- Boolean constant expressions), since I don't believe that (retain = true) is a "Boolean constant expression" under the rules (Section 15.28). If [I'm wrong and] it /is/ considered to be a constant expression, then the rules do require the compiler to treat "ref" as definitely assigned.
Another example where the compiler (wrongly IMO) treats an assignment as a constant expression:
int number ; String ref ; if (((number = 6) > 0) && (ref = System.getProperty("user.home")) != null) System.out.println(ref) ;
So at least it's consistent ;-) Maybe the JLS (I'm looking at version 3) should be changed. Whatever it is /intended/ to mean, it doesn't seem to consider this case specifically, and I think it should.
-- chris
Robert Klemme - 26 Apr 2006 13:11 GMT > Another example where the compiler (wrongly IMO) treats an assignment as a > constant expression: [quoted text clipped - 7 lines] > should be changed. Whatever it is /intended/ to mean, it doesn't seem to > consider this case specifically, and I think it should. Do you think that because of formal reasons (completeness of the spec) or for practical reasons? IMHO the code presented in this thread is not something one (at least I) would usually write.
Kind regards
robert
Oliver Wong - 26 Apr 2006 17:59 GMT >> Another example where the compiler (wrongly IMO) treats an assignment as >> a [quoted text clipped - 13 lines] > for practical reasons? IMHO the code presented in this thread is not > something one (at least I) would usually write. The compiler is being pessimistic. That is, if it can't be sure that assignment will have occured, it just assumes that assignment did not occur, and reports an error. The alternative is for the compiler to be optimistic (if it isn't sure, just assume the code is correct and compile it, possibly leading to runtime errors later on). In terms of practicality, the more we can minimize this pessimism (or optimism, if you have a compiler designed with optimism), the more "useful" the compiler will be. However, we can never make the compiler 100% correct (in the sense of never pessimistic nor optimistic, but always exactly correct), because it's been proven that this is equivalent to solving the Halting Problme, which has been proven to be impossible to solve.
With that in mind, if the compiler is "correctly" detecting a definite assignment, even though the JLS says it shouldn't, we should probably change the JLS to reflect that we can expect this extra bit of usefulness, rather than changing the compiler to become "less useful" and rigidly following the spec.
- Oliver
Patricia Shanahan - 26 Apr 2006 19:06 GMT ...
> With that in mind, if the compiler is "correctly" detecting a > definite assignment, even though the JLS says it shouldn't, we should > probably change the JLS to reflect that we can expect this extra bit of > usefulness, rather than changing the compiler to become "less useful" > and rigidly following the spec. I think the JLS's philosophy is to try to avoid the following situation:
Program compiles clean on compiler A, with no deliberate warning or error suppression.
Program needs to be compiled with compiler B. It spews out a ton of error messages that have to be fixed before the programmer can get on with whatever motivated the compiler change.
This could happen if compiler A were an optimizing compiler that did elaborate data and control flow analysis, and decided that all uses of a variable were dominated by assignments to that variable. Meanwhile, compiler B is doing the minimum required by the spec.
The idea is to make "valid Java program" independent of the choice of compiler. I'm not sure whether it is the right policy or not.
The spec is a little ambiguous in the assignment area. Assignment is not specifically listed as one of the things that preserves constantness. On the other hand, the result of the assignment, the value of the left hand side variable, is, by definition, the result of converting the right hand side. Casts to primitive types or String do preserve constantness.
Patricia
Chris Uppal - 27 Apr 2006 13:13 GMT > I think the JLS's philosophy is to try to avoid the following situation: > [quoted text clipped - 3 lines] > The idea is to make "valid Java program" independent of the choice of > compiler. I think there's another dimension to the languages specification authors' approach. If the "intuitively obvious" behaviour can be specified precisely, even at the cost of a very complex spec, then that's what they do. But when, as in the case of definite assignment, the "intuitively obvious" behaviour cannot be specified, they don't aim for a complex "near-miss", but instead take the opposite route of ensuring that the specification is relatively simple and the resulting behaviour is easy for working programmers to remember (albeit somewhat arbitrary).
(Needless to say, they have failed to follow this admirable practise with generics.)
> I'm not sure whether it is the right policy or not. Personal opinion: it is the right policy, very much so.
> The spec is a little ambiguous in the assignment area. Assignment is not > specifically listed as one of the things that preserves constantness. On > the other hand, the result of the assignment, the value of the left hand > side variable, is, by definition, the result of converting the right > hand side. Casts to primitive types or String do preserve constantness. I suspect that this may be another case where the compiler is in advance of the specification. There have been times (for instance the method resolution algorithm) where the spec, as written, required unnecessarily restricted or unexpected behaviour, but where the compiler didn't implement the anomaly. In this case, although I remain convinced that (x = true) is not a constant expression according to the wording of the spec, there doesn't seem to be any harm at all in allowing to be used as one. By "no harm" I don't only mean that interpreting it as one would not result in unsafe/incorrect code, but also that it wouldn't make the language harder to understand or use.
So the current compiler may be -- as it were -- implementing the next version of the spec ;-)
-- chris
Lee Fesperman - 30 Apr 2006 00:09 GMT > > Sun's 1.4 javac gave me a "variable might not have been initialized" > > error on the code below ... when it shouldn't have. > > The compiler is correct. The JLS lays down very specific rules for when a > variable is deemed to be "definitely initialised", and your code doesn't > satisfy those conditions. See JLS Section 16. I read JLS 16 several times. I'm obviously not a "language lawyer" because I could not see why my code causes the error message. Would you care to explain further? Perhaps, you could point to the relevant sub-section(s).
BTW, it works if I leave out the assignment to 'retain'.
> > I don't have 1.5 installed. Could someone try it on 1.5 for me? > [quoted text clipped - 3 lines] > > Also rejected. Thanks ... also, to others who posted their results.
> > Lastly, this one works on 1.4 but not 1.2, so Sun seems to be making > > progress in this area: [quoted text clipped - 14 lines] > if (retain = ( (ref = System.getProperty("user.home")) != null )) > System.out.println(ref); Yep, I shoulda realized that.
 Signature Lee Fesperman, FFE Software, Inc. (http://www.firstsql.com) ============================================================== * The Ultimate DBMS is here! * FirstSQL/J Object/Relational DBMS (http://www.firstsql.com)
Chris Uppal - 02 May 2006 13:58 GMT > I read JLS 16 several times. I'm obviously not a "language lawyer" > because I could not see why my code causes the error message. Would you > care to explain further? Perhaps, you could point to the relevant > sub-section(s). Here's now I read it:
> boolean retain ; > long physicalId ; > String ref ; > if (retain = (physicalId = 1) > 0 && > (ref = String.valueOf(physicalId)) != null) > System.out.println(ref) ; I take it that we agree that important thing is whether the antecedent to the && operator is a "Boolean constant expression" (Section 16.1.2) with value true. ref is definitively assigned iff
retain = (physicalId = 1) > 0
is a Boolean constant expression. It's clearly Boolean ;-) And true. But is it constant ?
For it to be so, we must at least have that expressions of the form:
retain = true
are formally constant. And for the first expression to reduce to the second, we must also have that:
physicalId = 1
is formally a constant integer-valued expression. I contend that under the JLS3 (or 2, come to that) there is no wording in the definition of "Constant Expression" (Section 15.28) which allows /either/ of those to be considered to be a constant expression.
I don't think that the wording of 15.28 is wrong (consider that constant expressions are used as labels for switch statements, and so on). However a special case could be made in 16.1 to allow a more general idea of a constant Boolean expression than is defined in 15.28. But there is no such provision (or if it is, I haven't noticed it). So I believe that the compiler is correct (or if the compiler is incorrect then so is the JLS in this matter).
-- chris
Lee Fesperman - 09 May 2006 02:36 GMT > > I read JLS 16 several times. I'm obviously not a "language lawyer" > > because I could not see why my code causes the error message. Would you [quoted text clipped - 18 lines] > is a Boolean constant expression. It's clearly Boolean ;-) And true. But is > it constant ? Thanks for your introductory remark clarifying the intent of the original posting. However, that was not my intention. I did not mean to use an expression that could be construed as a Boolean constant expression.
It is certainly my fault for causing the confusion by improperly constructing my sample code. I was trimming down a longer if statement and at the same time trying to figure out what the actual problem was ... I think I was getting bleary eyed ;^)
Anyway, I was concerned with an if statement consisting of a boolean assignment and a && expression which did an assignment in its right hand operand with no constant expressions.
A new version without constant expressions:
public class Test123 { public static void main(String[] args) { boolean retain ; int size ; String ref ; if (retain = (size = args.length) > 0 && (ref = System.getProperty(args[0])) != null) System.out.println(ref) ; } }
Based on my reading of Section 16 in JLS 3rd edition, I consider the "variable might not have been initialized" error that 1.4 javac throws for the above code to be incorrect.
Note: Apologies for the delay in responding. My ISP (Earthlink) decided to get squirrely about usenet access.
 Signature Lee Fesperman, FFE Software, Inc. (http://www.firstsql.com) ============================================================== * The Ultimate DBMS is here! * FirstSQL/J Object/Relational DBMS (http://www.firstsql.com)
Chris Uppal - 09 May 2006 12:43 GMT > A new version without constant expressions: Thanks for clearing up that red-herring.
> public class Test123 > { [quoted text clipped - 12 lines] > "variable might not have been initialized" error that 1.4 javac throws > for the above code to be incorrect. FYI, 1.5.0_6 also rejects that code. I've come to share your conclusion that it's wrong to do so.
FWIW, and on the off-chance that anyone's interested in the details....
We can simplify it a bit, and get the same behaviour:
if (retain = (args.length > 0) && (ref = System.getProperty(args[0])) != null) System.out.println(ref) ;
is also rejected. But (as you previously noted), removing the assignment to "retain":
if ((args.length > 0) && (ref = System.getProperty(args[0])) != null) System.out.println(ref) ;
also removes the error message. Now that, I believe, has to indicate a compiler error of /some/ sort. If ref is definitely assigned after the evaluation of an expression <E>, then it is also definitely assigned after the execution of: x = <E>; for unrelated x. (section 16.1.8). So the compiler must either be in wrong in rejecting the first form, or be wrong in accepting the second.
Next question. Should the compiler accept the second form ? I agree with you (and the compiler), I think it should.
Under 16.2.7, ref is assigned before: System.out.println(ref) ; iff it is assigned after: (args.length > 0) && (ref = System.getProperty(args[0])) != null) when true. Undert 16.1.2, ref is assigned after that when true iff it is assigned after: (ref = System.getProperty(args[0])) != null when true. That (under 16.1.7 and 16.1.10) is the same as asking whether ref is definitely assigned after; ref = System.getProperty(args[0]) Which it is ;-)
Therefore, I think it is wrong to reject the form with the extra assignment.
-- chris
Dale King - 15 May 2006 06:50 GMT >> public class Test123 >> { [quoted text clipped - 15 lines] > FYI, 1.5.0_6 also rejects that code. I've come to share your conclusion that > it's wrong to do so. Nope, it is correct to do so.
> FWIW, and on the off-chance that anyone's interested in the details.... > [quoted text clipped - 18 lines] > for unrelated x. (section 16.1.8). So the compiler must either be in wrong in > rejecting the first form, or be wrong in accepting the second. Nope, it is required to reject the first form and required to accept the second form.
> Next question. Should the compiler accept the second form ? I agree with you > (and the compiler), I think it should. [quoted text clipped - 10 lines] > ref = System.getProperty(args[0]) > Which it is ;-) That analysis is completely correct for the case withougt assignment. The boolean assignment inserts TWO rules into the mix. You likely overlooked a subtle, but important rule there.
> Therefore, I think it is wrong to reject the form with the extra assignment. Nope, it is required to reject the form with the extra assignment. The case with assignment goes as follows:
Under 16.2.7, ref is assigned before: System.out.println(ref) ; iff it is assigned after: retain = (args.length > 0) && (ref = System.getProperty(args[0])) != null) when true.
We then have to go to the rule that is probably the one you overlooked. We have to go to rule 16.1.7 because we have a boolean expression (the boolean assignment) and that expression is not constant, nor is it the !, &&, ||, or ?: operator. Under that section it tells us that:
ref is assigned after: retain = (args.length > 0) && (ref = System.getProperty(args[0])) != null) when true. iff it is assigned after retain = (args.length > 0) && (ref = System.getProperty(args[0])) != null)
So what section 16.1.7 does is strip off the "when true" part of the expression.
We then go to section 16.1.8 which says
ref is assigned after: retain = (args.length > 0) && (ref = System.getProperty(args[0])) != null) if it is assigned after (args.length > 0) && (ref = System.getProperty(args[0])) != null)
That takes us to 16.1.2 and with further analysis we find that it is definitely assigned when this expression is true and not when this expression is false. But we are no longer limited to only the when true case because of rule 16.1.7. With the boolean assignment it has to be true whether the value is true or false.
So there is no bug and the compiler is following the JLS to the letter.
 Signature Dale King
jmcgill - 15 May 2006 17:02 GMT >> if ((args.length > 0) >> && (ref = System.getProperty(args[0])) != null) >> System.out.println(ref) ; Correct or not, if I was given this to maintain, I would break the conditional into as many statements as were needed to remove any assignments from the scope of the condition. Yeah, that makes the one line into three or four. I'd also wrap the action of the condition in curly braces, even if it is just one line.
I definitely consider several elements of that style to be a false economy. Others are free to disagree, and I would not mind hearing their reasons. Understand that I do similar things myself, in C, in the pointer arithmetic idiom, but that there are things I believe are appropriate in C that are less appropriate in java, even if legal.
It doesn't kill me to bring an assignment into its own statement instead of using the side effect shortcut. It also doesn't kill me to use two nested if's, where the logic for a single if is complicated at all, and it doesn't kill me to put curly braces on every if. If you could make the case that it *would* kill you, I wouldn't criticize your style.
Dale King - 15 May 2006 17:31 GMT >>> if ((args.length > 0) >>> && (ref = System.getProperty(args[0])) != null) [quoted text clipped - 17 lines] > it doesn't kill me to put curly braces on every if. If you could make > the case that it *would* kill you, I wouldn't criticize your style. I totally agree that it is definitely poor style and not something I would do personally. I am definitely a zealot about readable code (see my many past discussions on readability in this group). Nowhere did I or anyone else that I see advocate this code (although I only got the end of the thread). Heck I didn't even post the code myself.
I too am a big proponent of introducing local variables to simplify conditionals (such as the Introduce Explaining Variable refactoring http://www.refactoring.com/catalog/introduceExplainingVariable.html).
But the question posed here was not one of style, but about the rules the compiler must adhere to. Whether it is good style or not, the compiler can be presented with this code and must make decisions about definite assignment of variables and there was some confusion about what the rules were in this case. It was claimed that the compiler had a bug, but in fact it is following the rules correctly.
 Signature Dale King
Lee Fesperman - 19 May 2006 08:01 GMT > >> public class Test123 > >> { [quoted text clipped - 27 lines] > > > > is also rejected. <Chris's arguments snipped>
> > Therefore, I think it is wrong to reject the form with the extra assignment. > [quoted text clipped - 38 lines] > > So there is no bug and the compiler is following the JLS to the letter. Though 16.1.7 does seem subtle, I think I understand your point. So, I did another simplification:
public class Test124 { public static void main(String[] args) { boolean retain ; String ref ; if (retain = (ref = System.getProperty(args[0])) != null) System.out.println(ref) ; } }
I only tried Sun's 1.4 javac, but it did accept this revision. That is, it compiled without error and ran successfully.
It seems to me that JLS 16.1.7 would require rejecting this one also.
 Signature Lee Fesperman, FFE Software, Inc. (http://www.firstsql.com) ============================================================== * The Ultimate DBMS is here! * FirstSQL/J Object/Relational DBMS (http://www.firstsql.com)
Dale King - 19 May 2006 16:21 GMT >>>> public class Test123 >>>> { [quoted text clipped - 88 lines] > > It seems to me that JLS 16.1.7 would require rejecting this one also. And why wouldn't it accept it?
Let's simplify this as:
boolean retain; String ref; if( retain = expr ) System.out.println( ref );
By the rules as I showed above, ref is only considered definitely assigned for the body of the if iff ref is assigned after expr (and because of the assignment it has to be assigned whether it is true or false).
For your example, expr is (ref = System.getProperty(args[0])) != null which definitely assigns ref whether the expression is true or false so the rules are satisfied.
In the previous example where expr was (args.length > 0) && (ref = System.getProperty(args[0])) != null) then ref is only assigned when the expr is true. That is not strong enough to satisfy the requirement of the assignment.
If you remove the assignment to retain then only assigning ref when the expression is true is enough.
The key factor here is that boolean assignment requires definite assignment whether the value is true or false.
 Signature Dale King
Lee Fesperman - 20 May 2006 20:49 GMT > > Though 16.1.7 does seem subtle, I think I understand your point. So, I did another > > simplification: [quoted text clipped - 43 lines] > The key factor here is that boolean assignment requires definite > assignment whether the value is true or false. Yes, you are right. Thanks for the clarifications.
 Signature Lee Fesperman, FFE Software, Inc. (http://www.firstsql.com) ============================================================== * The Ultimate DBMS is here! * FirstSQL/J Object/Relational DBMS (http://www.firstsql.com)
Chris Uppal - 21 May 2006 14:16 GMT > We then have to go to the rule that is probably the one you > overlooked. We have to go to rule 16.1.7 because we have a boolean > expression (the boolean assignment) and that expression is not constant, > nor is it the !, &&, ||, or ?: operator. I'm getting confused, but I don't /think/ I agree. I think you are considering the assignment retain = xxxxx && yyyyy to be "caught" by 16.1.7 (I agree that it is an expression of type boolean, etc), whereas my breakdown considers it to be caught by 16.1.8. Having applied 16.1.8, the remainder is reduced to an "expression of type boolean", but one which /does/ have an &&.
There seems to be an ambiguity in the JLS here in that two rules apply, but lead to different results depending on which one is used.
-- chris
P.S. Sorry for the long delay -- caused purely by my strong disinclination to re-read, or even to look at, the relevant passages of the JLS ;-)
Dale King - 22 May 2006 04:49 GMT >> We then have to go to the rule that is probably the one you >> overlooked. We have to go to rule 16.1.7 because we have a boolean [quoted text clipped - 11 lines] > There seems to be an ambiguity in the JLS here in that two rules apply, but > lead to different results depending on which one is used. Both rules do apply. You can logically apply the rules in the order I applied them, but there is no way to apply them in the reverse order.
Note that boolean assignment is not the only operator where 16.1.7 is going to apply with another rule. Consider the non short-circuiting and/or operators (& |) or boolean exclusive-or (^). In those cases 16.1.7 applies along with 16.1.10. But once again you have to apply 16.1.7 first.
16.1.7 bridges between the boolean expressions where you have to consider when the expression is true and when the expression is false and other expressions where you don't.
Basically 16.1.7 says that we only have to consider the when-true and when-false information for a limited set of operators. When it is other operators it must be definitely assigned either way.
You can still get to the same result without 16.1.7. 16.1.7 just makes it a little smoother. We start with this from the if-statement rule 16.2.7:
ref is definitely assigned before the if block if it is definitely assigned after retain = XXXX && YYYY when true.
16.1.8 gives us this
ref is definitely assigned after retain = XXXX && YYYY if it is definitely assigned after XXXX && YYYY.
Rule 16.1.8 says nothing about "when true". You aren't allowed to carry the when true part from 16.2.7 into evaluating the assignment. Rule 16.1.7 is there to make that explicitly clear by saying you drop the when true unless it is one of a limited set of boolean operators (the ones that have a control flow nature to them).
 Signature Dale King
Greg R. Broderick - 26 Apr 2006 15:14 GMT > public class Test120 > { [quoted text clipped - 15 lines] > ^ > 1 error If the first boolean part of the if statement evaluates to false, then the second part will not be evaluated. Therefore, if
retain = (physicalId = 1) > 0 == false
then
ref = String.valueOf(physicalId)) != null
will not be evaluated, and ref will not be assigned a value.
Therefore the javac compiler output is correct.
Cheers GRB
 Signature --------------------------------------------------------------------- Greg R. Broderick [rot13] terto@oynpxubyvb.qlaqaf.bet
A. Top posters. Q. What is the most annoying thing on Usenet? ---------------------------------------------------------------------
Oliver Wong - 26 Apr 2006 17:54 GMT >> public class Test120 >> { [quoted text clipped - 28 lines] > > Therefore the javac compiler output is correct. If the first part of the if-statement is false, then the line the compiler is complaining about won't execute, so the output is "incorrect" in that sense (though see Chris Uppal's reply).
- Oliver
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|