Incorrect "variable might not have been initialized"

L

Lee Fesperman

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.
 
P

Patrick

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:

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) ;
}
}

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".
 
C

Chris Uppal

Lee said:
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:

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) ;
}
}

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
 
R

Robert Klemme

Chris said:
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.

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
 
G

Greg R. Broderick

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 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

--
---------------------------------------------------------------------
Greg R. Broderick [rot13] (e-mail address removed)

A. Top posters.
Q. What is the most annoying thing on Usenet?
---------------------------------------------------------------------
 
O

Oliver Wong

Greg R. Broderick said:
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 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.

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
 
O

Oliver Wong

Robert Klemme said:
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.

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
 
P

Patricia Shanahan

Oliver Wong wrote:
....
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
 
C

Chris Uppal

Patricia said:
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.
[...]
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
 
L

Lee Fesperman

Chris said:
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've just checked (JDK 1.5.0_06-b05), same thing happens.


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:

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) ;
}
}

After constant-folding "1 > 0", the compiler interprets that as:

if (retain = ( (ref = System.getProperty("user.home")) != null ))
System.out.println(ref);

Yep, I shoulda realized that.
 
C

Chris Uppal

Lee said:
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
 
L

Lee Fesperman

Chris said:
Here's now I read it:


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 ?

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.
 
C

Chris Uppal

Lee said:
A new version without constant expressions:


Thanks for clearing up that red-herring.

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.

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
 
D

Dale King

Chris said:
Lee said:
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.

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....

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.

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.

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 ;-)

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.
 
J

jmcgill

Dale said:
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.
 
D

Dale King

jmcgill said:
Dale said:
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.

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.
 
L

Lee Fesperman

Dale said:
Chris said:
Lee said:
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.

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....

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.
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.

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.
 
D

Dale King

Lee said:
Dale said:
Chris said:
Lee Fesperman wrote:

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.
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....

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.
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.

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.

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.
 
L

Lee Fesperman

Dale said:
Lee said:
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.

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.

Yes, you are right. Thanks for the clarifications.
 
C

Chris Uppal

Dale said:
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 ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top