I was recently removing a block of code from our code base before a release and used an if(false) statement to prevent execution:
if (false) {
ArrayList<String> list = new ArrayList<String>();
...
}
This compiles fine and will prevent execution of the offending block of code (right or wrong, that's not the current argument).
However, kind of by accident, I changed the block above to:
while (false) {
ArrayList<String> list = new ArrayList<String>();
...
}
and received an unreachable statement compilation error.
I appreciate the compilation error and understand the reasons, however, I'm struggling to comprehend the difference between the two blocks and why the former compiles fine but the latter does not when they both have unreachable statements.
In both case the compiler should raise an error, because the code between braces is essentially pointless, but if (false) was kept in Java to simulate C/C++ preprocessor #if 0, quite a common way of disabling parts of code for testing or debugging.
EDIT: for reference, "conditional compiling" is detailed at the end of chapter 14.21 of the Java Language Specification.
"Java uses a simple flow analysis algorithm to find most common cases of unreachable code, and all such unreachable code blocks will be flagged as compile-time errors. That's why your "while (false) { ... }" statement produces an error.
However, Java makes a special exception for "if (false) { ... }", because programmers often use this construct during development to temporarily disable part of the program. That's why the compiler accepts this statement.
If you're interested in the nitty-gritty details, refer to the Java Language Specification's description of unreachable statements # http://docs.oracle.com/javase/specs/#14.21."
Quoted from http://www.coderanch.com/t/266678/java-programmer-SCJP/certification/false-false
Related
Consider this function :
public boolean foo(){
System.exit(1);
//The lines beyond this will not be read
int bar = 1; //L1
//But the return statement is required for syntactically correct code
return false; //L2
//error here for unreachable code
//int unreachable = 3; //L3
}
Can someone please explain why L1 and L2 visibly not reachable does not give warnings but L3 does.
Because as far as the compiler is concerned, System.exit() is just another method call.
The fact that what it does is end the process can only be found out from the implementation (which is native code, not that it makes any difference).
If you have to put System.exit() in your code (usually it's best to avoid it, unless you want to return a code other than 0), it should really be in a method that returns void, main() for example. It's nicer that way.
As for the reachability, the explanation is the same: return is a keyword of the Java language, so the compiler or the parser the IDE uses can tell that it's theoretically impossible for code after the return statement to be executed. These rules are defined here.
The Java compiler doesn't know anything about System.exit. It's just a method as far as it's concerned - so the end of the statement is reachable.
You say that L1 and L2 are "visibly not reachable" but that's only because you know what System.exit does. The language doesn't - whereas it does know what a return statement does, so it knows that L3 really isn't reachable.
I sometimes think it would be useful to be able to declare that a method isn't just void, but never terminates normally - it never just returns (although it may throw an exception). The compiler would then be able to use that information to make the end of any calling expression unreachable, preventing this sort of thing from being a problem. However, that's just my dreams around language design - Java doesn't have anything similar, and it would be a very bad idea for the compiler to "know" that particular JRE methods will never return normally, when that concept can't be expressed directly within the language.
Instead, the compiler is bound by the rules of section 14.21 of the JLS, including:
The first statement in a non-empty block that is not a switch block is reachable iff the block is reachable.
Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.
...
An expression statement can complete normally iff it is reachable.
(A method call is an expression statement.)
Then from section 8.4.7:
If a method is declared to have a return type, then a compile-time error occurs if the body of the method can complete normally (§14.1).
and in 14.1:
Unless otherwise specified, a statement completes normally if all expressions it evaluates and all substatements it executes complete normally.
So the call to System.exit() can complete normally as far as the compiler is concerned, which means the body of the foo method can complete normally, which leads to the error.
Me: "Can anything be executed after a return statement?"
Java: "NO."
Me: "Can anything be executed after I call System.exit?"
Java: "That's a method, I don't know what it does - it's not a reserved keyword, it doesn't affect program flow as far as I know" (and it may not even work (I don't know if exit can throw exceptions (or future variants of it)))
From the language standpoint, there are only 2 ways to escape current scope: return and throw. Method calls are never considered the same way, even if they consist of the only line of code:
void method() {
throw new RuntimeException();
}
Even more. In theory, any method call can cause RuntimeException to be thrown. In this case, compiler should probably give you warnings for absolutely any method call which is not inside a try block:
...
method(); // WARNING: may throw in theory
anotherMethod(); // WARNING: possible unreachable code, also may throw
anotherMethod2(); // WARNING: possible unreachable code, also may throw
// etc
...
For your question logic is the same.
If a method is declared to return a non-void value, then it must contain a return statement somewhere, even if it's never reached (like in the code in the question).
From the compiler's point of view, System.exit() is just another method call, with nothing special about it that indicates that the program ends as soon as it's reached. Only you, as the programmer, know this fact - but it's something outside of the compiler's knowledge.
About the second part of your question - nothing can go after a return statement in a block of code inside a method, as that will always be unreacheable code. That explains why the compiler complains about the L3 line.
I know this does not make sense, and still this is how the Java compiler works, when you're calling a method.
The compiler does not know at this point what Sytem.exist does (why should rt.jar be different than other jars you compile with , in that sense?).
This is in contrast for example to the next piece of code -
public int test() {
throw new NullPointerException("aaaa");
}
Where the compiler can tell that an exception is always thrown, and therefore no return is needed.
The compiler checks whether some code is reachable only with respect to the return keyword (also throw and break(in case of loops), in general). For the compiler the exit method call is just another call, it doesn't know its meaning, so it doesn't know that the code afterwards will never be reached.
It's possible that the static code analysis tool is not taking into account that the application is terminating.
I tried the following in Eclipse:
if (false) {}: warning 'dead code'
while (false) {}: compilation error 'unreachable code'
I was wondering whether there is a real 'reason' for this difference. I already found this...
Unreachable code compiler error
...but why not allow while (false) for the same debugging purpose?
The JLS section on unreachable code explains the rationale. Essentially, Java normally shouldn't use conditional compilation like C routinely does with #ifdef, but there are some situations (such as debugging, and in particular backward binary compatibility) where allowing the compiler to entirely strip out code is needed, and so the specific construct if(false) is permitted for that purpose.
You must read the Unreachable Statements. Although with while(false) the compiler will throw an error but with if(false) it wil show a warning to the user.
Although if (false) was kept in Java to simulate C/C++ preprocessor #if 0
The specification says that:
if (false) { x=3; }
does not result in a compile-time error. An optimizing compiler may
realize that the statement x=3; will never be executed and may choose
to omit the code for that statement from the generated class file, but
the statement x=3; is not regarded as "unreachable" in the technical
sense specified here.
The rationale for this differing treatment is to allow programmers to
define "flag variables" such as:
static final boolean DEBUG = false; and then write code such as:
if (DEBUG) { x=3; } The idea is that it should be possible to change
the value of DEBUG from false to true or from true to false and then
compile the code correctly with no other changes to the program text.
However, the 'do-while loop' will compile and execute.
do {
System.out.println("its work!");
} while (false); //compile and execute
This question already has answers here:
Unreachable code: error or warning? [closed]
(12 answers)
Closed 9 years ago.
class For1
{
public static void main(String args[])
{
int a = 0;
for(;;)
{
break;
System.out.println(a); //Line 1
++a;//Line 2
}
}
}
I know that Line 1/Line 2 will never be executed.
But still I don't understand why a compile time error is thrown.
I am getting "unreachable statement" compile error.
Does it mean that compiler checks whether it is able to compile for all branches/lines of code ?
Does it mean that compiler checks whether it is able to compile for all branches/lines of code ?
It means the compiler checks that every statement is reachable.
From section 14.21 of the JLS:
It is a compile-time error if a statement cannot be executed because it is unreachable.
This section is devoted to a precise explanation of the word "reachable." The idea is that there must be some possible execution path from the beginning of the constructor, method, instance initializer, or static initializer that contains the statement to the statement itself. The analysis takes into account the structure of statements.
The section then documents how reachability is defined.
In particular, the relevant points in your case are:
Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.
A break, continue, return, or throw statement cannot complete normally.
So your "line 1" statement is preceded by a statement (break;) which cannot complete normally, and therefore it's unreachable.
The compiler is also able to make that conclusion, and assumes you are making a mistake. And yes, the Java compiler does a pretty good amount of "Data-Flow Analysis". The most common related message is the one about variables not initialized. The second most frequent is, I believe, precisely this one, about code not reachable.
Does it mean that compiler checks whether it is able to compile for
all branches/lines of code ?
Yes compiler compiles the whole body of code and make byte code according to your code, it smarter enough to detects unreachable code also dead code. Immediate break in the for-loop makes unreachable other statements.
for(;;){
break;
... // unreachable statement
}
int i=1;
if(i==1)
...
else
... // dead code
Unreachable code is meaningless and redundant. If you have some unreachable code in your program it is a mistake and needs to be fixed. Hence compiler throws an error.
You can refer to similar questions below
Unreachable code: error or warning?
and
Why does Java have an "unreachable statement" compiler error?
The compiler is able to determine that these two statement will never, ever be executed, and helps you write correct code by refusing to compile it, because this has 99.9% chance of being an error rather than a conscious choice to add statements that will never be executed.
The compiler will check if there is more code after certain keywords. Another keyword which will cause a similar message is if you replace break by return.
I'd like to write some troubleshooting code which i can easily remove from later non debug versions of my program. I came up with:
final static boolean debug_on=true;
...
if (debug_on) { system.out.println() or logger.log(...) }
Is Java smart enough to drop the if statement from the final bytecode if debug==false ?
Is there a better practice to achieve the goal of keeping debug code out of the final version of a program ?
See the end of the Java language specification chapter 14.21. Unreachable Statements for a description of if(false):
if (false) { x=3; }
does not result in a compile-time error. An optimizing compiler may
realize that the statement x=3; will never be executed and may choose
to omit the code for that statement from the generated class file, but
the statement x=3; is not regarded as "unreachable" in the technical
sense specified here.
The rationale for this differing treatment is to allow programmers to
define "flag variables" such as:
static final boolean DEBUG = false;
and then write code such as:
if (DEBUG) { x=3; }
The idea is that it should be possible to change the value of DEBUG
from false to true or from true to false and then compile the code
correctly with no other changes to the program text.
tl;dr; It depends on the compiler whether the statements inside your if are omitted in the bytecode.
This question already has answers here:
Why does Java have an "unreachable statement" compiler error?
(8 answers)
Closed 6 years ago.
The following code gives an unreachable statement compiler error
public static void main(String[] args) {
return;
System.out.println("unreachable");
}
Sometimes for testing purposes a want to prevent a method from being called, so a quick way to do it (instead of commenting it out everywhere it's used) is to return immediately from the method so that the method does nothing. What I then always do to get arround the compiler error is this
public static void main(String[] args) {
if (true) {
return;
}
System.out.println("unreachable");
}
I'm just curious, why is it a compiler error?? Will it break the Java bytecode somehow, is it to protect the programmer or is it something else?
Also (and this to me is more interesting), if compiling java to bytecode does any kind of optimization (or even if it doesn't) then why won't it detect the blatant unreachable code in the second example? What would the compiler pseudo code be for checking if a statement is unreachable?
Unreachable code is meaningless, so the compile-time error is helpful. The reason why it won’t be detected at the second example is, like you expect, for testing / debugging purposes. It’s explained in The Specification:
if (false) { x=3; }
does not result in a compile-time error. An optimizing compiler may
realize that the statement x=3; will never be executed and may choose
to omit the code for that statement from the generated class file, but
the statement x=3; is not regarded as "unreachable" in the technical
sense specified here.
The rationale for this differing treatment is to allow programmers to
define "flag variables" such as:
static final boolean DEBUG = false;
and then write code such as:
if (DEBUG) { x=3; }
The idea is that it should be possible to change the value of DEBUG
from false to true or from true to false and then compile the code
correctly with no other changes to the program text.
Reference: http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21
Its because the compiler writer assumed that the human at the controls is dumb, and probably didn't mean to add code that would never be executed - so by throwing an error, it attempts to prevent you from inadvertently creating a code path that cannot be executed - instead forcing you to make a decision about it (even though, as you have proven, you still can work around it).
This error is mainly there to prevent programmer errors (a swap of 2 lines or more). In the second snippet, you make it clear that you don't care about the system.out.println().
Will it break the Java bytecode somehow, is it to protect the programmer or is it something else?
This is not required as far as Java/JVM is concerned. The sole purpose of this compilation error is to avoid silly programmer mistakes. Consider the following JavaScript code:
function f() {
return
{
answer: 42
}
}
This function returns undefined as the JavaScript engine adds semicolon at the end of the line and ignores dead-code (as it thinks). Java compiler is more clever and when it discoveres you are doing something clearly and obviously wrong, it won't let you do this. There is no way on earth you intended to have dead-code. This somehow fits into the Java premise of being a safe language.
http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21
says:
14.21. Unreachable Statements
It is a compile-time error if a statement cannot be executed because it is unreachable.
Example 1:
In this case you are return before any statement because of it compiler never going to execute that code.
public static void main(String[] args) {
return;
System.out.println("unreachable");
}
In second code I have put the statement above of return and its work now :)
Example 2:
public static void main(String[] args) {
System.out.println("unreachable"); // Put the statement before return
return;
}
The reason behind this is that if you return sometime then code after it never going to execute because you already return the function data and as so it is shown unreachable code.
It's because it's a waste of resources for it to even be there. Also, the compiler designers don't want to assume what they can strip out, but would rather force you to remove either the code that makes it unreachable or the unreachable code itself. They don't know what is supposed to be there. There's a difference between the optimizations where they tweak your code to be a bit more efficient when it's compiled down to machine code and blatantly just removing code "you didn't need."