I was doing some small programs in java. I know that if I write while(true); the program will freeze in this loop. If the code is like that:
Test 1:
public class While {
public static void main(String[] args) {
System.out.println("start");
while (true);
System.out.println("end");
}
}
The compiler throws me the error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unreachable code
at While.main(While.java:6)
I didn't know that this error exists. But I got why it is thrown. Of course, line 6 was unreachable, causing a compilation problem. Then I tested this:
Test 2:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
b();
}
static void a() {
while(true);
}
static void b() {
System.out.println("end");
}
}
For some reason the program ran normally (The console printed "start" and then froze). The compiler couldn't check inside of void a() and see that it isn't reachable. To be sure I tried:
Test 3:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
System.out.println("end");
}
static void a() {
while(true);
}
}
Same result as Test 2.
After some research I found this question. So, if the code inside the parentheses is a variable the compiler wouldn't throw the exception. That makes sense, but I don't think that the same applies to voids.
Q: So, why does the compiler just throw me the error at Test 1, if void b() (Test 2) and System.out.println("end"); (Test 3) isn't reachable?
EDIT: I tried Test 1 in C++:
#include <iostream>
using namespace std;
int main()
{
cout << "start" << endl;
while(true);
cout << "end" << endl;
return 0;
}
The compiler didn't throw any errors, then I got the same result as Test 2 and as Test 3. So I suppose this is a java thing?
The language spec has an exact definition what the compiler should treat as unreachable code, see also https://stackoverflow.com/a/20922409/14955.
In particular, it does not care about if a method completes, and it does not look inside other methods.
It won't do more than that.
However, you could get static code analysis tools like FindBugs to get a "deeper" analysis (not sure if they detect the pattern you described, though, either, and, as has been pointed out by others, the halting problem in all generality cannot be algorithmically solved anyway, so one has to draw the line at some reasonably definition of "best effort").
In general, it is not possible to determine with absolute certainly whether or not something is reachable.
Why? It is the equivalent to the Halting Problem.
The halting problem asks:
Given a description of an arbitrary computer program, decide whether the program finishes running or continues to run forever.
This problem has been proven to be unsolvable.
Whether or not X piece of code is reachable is the same as saying whether the code before it will halt.
Because it is an unsolvable problem, the compiler (in Java or any other language) doesn't try very hard to solve it. If it happens to determine that it really is unreachable, then you get the warning. If not, it may or may not be reachable.
In Java, unreachable code is a compiler error. So in order to maintain compatibility, the language spec defines exactly "how hard" the compiler should try. (Which according to the other answers, is "don't go inside another function".)
In other languages (such as C++), the compiler may go further subject to optimizations. (Unreachable code may be detected after inlining the function and discovering that it never returns.)
Unreachable code is a compile time error that simply says 'the flow of this program doesn't make sense; something will never ever be reached'.
Obviously your tests perform how they do due to an endless loop, but why does the first fail with a compile time error?
A while statement can complete normally if at least one of the
following is true:
The while statement is reachable and the condition expression is not a
constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
Alright, but what about methods invocations (such as a()) - why do tests 2 and 3 successfully compile?
An expression statement can complete normally if it is reachable.
Since a method invocation is considered an expression, they will always be reachable so long as nothing before it blocks its path of logical execution.
To better illustrate some reasoning behind this compilation mechanism, let's take an if statement, for example.
if(false)
System.out.println("Hello!"); // Never executes
The above will be correct at compile time (though many IDE's will definitely whine!).
The Java 1.7 Specification talks about this:
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.
Further, there is actually a backwards compatibility reason as well:
This ability to "conditionally compile" has a significant impact on,
and relationship to, binary compatibility (§13). If a set of classes
that use such a "flag" variable are compiled and conditional code is
omitted, it does not suffice later to distribute just a new version of
the class or interface that contains the definition of the flag. A
change to the value of a flag is, therefore, not binary compatible
with pre-existing binaries (§13.4.9). (There are other reasons for
such incompatibility as well, such as the use of constants in case
labels in switch statements; see §13.4.9.)
Most (per the spec), if not all, implementations of the Java compiler do not traverse into methods. When parsing your Java code itself, it sees a() as just a MethodInvocationElement, meaning 'This code calls other code. I really don't care, I'm just looking at syntax.'. Syntactically, it makes sense for subsequent code to belong after a call to a().
Keep in mind performance costs. Compilation already takes a considerable amount of time. In order to keep things quick, the Java compiler doesn't actually recurse into methods; that would take ages (the compiler would have to evaluate many, many paths of code -- in theory).
To further reiterate that it's syntactically driven is to add a return; statement directly after your loop in a(). Doesn't compile, does it? Syntactically, though, it makes sense without it.
The answer lies in the rules set out for reachability by the Java Language Specification. It first states
It is a compile-time error if a statement cannot be executed because it is unreachable.
And then
A while statement can complete normally iff at least one of the
following is true:
The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
and
An expression statement can complete normally iff it is reachable.
In your first example, you have a while loop that cannot complete normally because it has a condition which is a constant expression with value true and there is no reachable break within it.
In your second and third examples, the expression statement (method invocation) is reachable and can therefore complete normally.
So I suppose this is a java thing?
The rules above are Java's rules. C++ probably has its own rules, as do other languages.
Related
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."
I encountered a situation where a non-void method is missing a return statement and the code still compiles.
I know that the statements after the while loop are unreachable (dead code) and would never be executed. But why doesn't the compiler even warn about returning something? Or why would a language allow us to have a non-void method having an infinite loop and not returning anything?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
If I add a break statement (even a conditional one) in the while loop, the compiler complains of the infamous errors: Method does not return a value in Eclipse and Not all code paths return a value in Visual Studio.
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
This is true of both Java and C#.
Why would a language allow us to have a non-void method having an infinite loop and not returning anything?
The rule for non-void methods is every code path that returns must return a value, and that rule is satisfied in your program: zero out of zero code paths that return do return a value. The rule is not "every non-void method must have a code path that returns".
This enables you to write stub-methods like:
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
That's a non-void method. It has to be a non-void method in order to satisfy the interface. But it seems silly to make this implementation illegal because it does not return anything.
That your method has an unreachable end point because of a goto (remember, a while(true) is just a more pleasant way to write goto) instead of a throw (which is another form of goto) is not relevant.
Why doesn't the compiler even warn about returning something?
Because the compiler has no good evidence that the code is wrong. Someone wrote while(true) and it seems likely that the person who did that knew what they were doing.
Where can I read more about reachability analysis in C#?
See my articles on the subject, here:
ATBG: de facto and de jure reachability
And you might also consider reading the C# specification.
The Java compiler is smart enough to find the unreachable code ( the code after while loop)
and since its unreachable, there is no point in adding a return statement there (after while ends)
same goes with conditional if
public int get() {
if(someBoolean) {
return 10;
}
else {
return 5;
}
// there is no need of say, return 11 here;
}
since the boolean condition someBoolean can only evaluate to either true or false, there is no need to provide a return explicitly after if-else, because that code is unreachable, and Java does not complain about it.
The compiler knows that the while loop will never stop executing, hence the method will never finish, hence a return statement is not necessary.
Given your loop is executing on a constant - the compiler knows that it's an infinite loop - meaning the method could never return, anyway.
If you use a variable - the compiler will enforce the rule:
This won't compile:
// Define other methods and classes here
public int doNotReturnAnything() {
var x = true;
while(x == true) {
//do something
}
//no return statement - won't compile
}
The Java specification defines a concept called Unreachable statements. You are not allowed to have an unreachable statement in your code (it's a compile time error). You are not even allowed to have a return statement after the while(true); statement in Java. A while(true); statement makes the following statements unreachable by definition, therefore you don't need a return statement.
Note that while Halting problem is undecidable in generic case, the definition of Unreachable Statement is more strict than just halting. It's deciding very specific cases where a program definitely does not halt. The compiler is theoretically not able to detect all infinite loops and unreachable statements but it has to detect specific cases defined in the specification (for example, the while(true) case)
The compiler is smart enough to find out that your while loop is infinite.
So the compiler cannot think for you. It cannot guess why you wrote that code. Same stands for the return values of methods. Java won't complain if you don't do anything with method's return values.
So, to answer your question:
The compiler analyzes your code and after finding out that no execution path leads to falling off the end of the function it finishes with OK.
There may be legitimate reasons for an infinite loop. For example a lot of apps use an infinite main loop. Another example is a web server which may indefinitely wait for requests.
In type theory, there is something called the bottom type which is a subclass of every other type (!) and is used to indicate non-termination among other things. (Exceptions can count as a type of non-termination--you don't terminate via the normal path.)
So from a theoretical perspective, these statements that are non-terminating can be considered to return something of Bottom type, which is a subtype of int, so you do (kind of) get your return value after all from a type perspective. And it's perfectly okay that it doesn't make any sense that one type can be a subclass of everything else including int because you never actually return one.
In any case, via explicit type theory or not, compilers (compiler writers) recognize that asking for a return value after a non-terminating statement is silly: there is no possible case in which you could need that value. (It can be nice to have your compiler warn you when it knows something won't terminate but it looks like you want it to return something. But that's better left for style-checkers a la lint, since maybe you need the type signature that way for some other reason (e.g. subclassing) but you really want non-termination.)
There is no situation in which the function can reach its end without returning an appropriate value. Therefore, there is nothing for the compiler to complain about.
Visual studio has the smart engine to detect if you have typed a return type then it should have a return statement with in the function/method.
As in PHP Your return type is true if you have not returned anything. compiler get 1 if nothing has returned.
As of this
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
Compiler know that while statement itself has a infinte nature so not to consider it. and php compiler will automatically get true if you write a condition in expression of while.
But not in the case of VS it will return you a error in the stack .
Your while loop will run forever and hence won't come outside while; it will continue to execute. Hence, the outside part of while{} is unreachable and there is not point in writing return or not. The compiler is intelligent enough to figure out what part is reachable and what part isn't.
Example:
public int xyz(){
boolean x=true;
while(x==true){
// do something
}
// no return statement
}
The above code won't compile, because there can be a case that the value of variable x is modified inside the body of while loop. So this makes the outside part of while loop reachable! And hence compiler will throw an error 'no return statement found'.
The compiler is not intelligent enough (or rather lazy ;) ) to figure out that whether the value of x will be modified or not. Hope this clears everything.
"Why doesn't the compiler even warn about returning something? Or why would a language allow us to have a non-void method having an infinite loop and not returning anything?".
This code is valid in all other languages too (probably except Haskell!). Because the first assumption is we are "intentionally" writing some code.
And there are situations that this code can be totally valid like if you are going to use it as a thread; or if it was returning a Task<int>, you could do some error checking based on the returned int value - which should not be returned.
I may be wrong but some debuggers allow modification of variables. Here while x is not modified by code and it will be optimized out by JIT one might modify x to false and method should return something (if such thing is allowed by C# debugger).
The specifics of the Java case for this (which are probably very similar to the C# case) are to do with how the Java compiler determines if a method is able to return.
Specifically, the rules are that a method with a return type must not be able to complete normally and must instead always complete abruptly (abruptly here indicating via a return statement or an exception) per JLS 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.
In other words, a method with a return type must return only by using
a return statement that provides a value return; it is not allowed to
"drop off the end of its body".
The compiler looks to see whether normal termination is possible based on the rules defined in JLS 14.21 Unreachable Statements as it also defines the rules for normal completion.
Notably, the rules for unreachable statements make a special case just for loops that have a defined true constant expression:
A while statement can complete normally iff at least one of the
following is true:
The while statement is reachable and the condition expression is not a
constant expression (§15.28) with value true.
There is a reachable break statement that exits the while statement.
So if the while statement can complete normally, then a return statement below it is necessary since the code is deemed reachable, and any while loop without a reachable break statement or constant true expression is considered able to complete normally.
These rules mean that your while statement with a constant true expression and without a break is never considered to complete normally, and so any code below it is never considered to be reachable. The end of the method is below the loop, and since everything below the loop is unreachable, so is the end of the method, and thus the method cannot possibly complete normally (which is what the complier looks for).
if statements, on the other hand, do not have the special exemption regarding constant expressions that are afforded to loops.
Compare:
// I have a compiler error!
public boolean testReturn()
{
final boolean condition = true;
if (condition) return true;
}
With:
// I compile just fine!
public boolean testReturn()
{
final boolean condition = true;
while (condition)
{
return true;
}
}
The reason for the distinction is quite interesting, and is due to the desire to allow for conditional compilation flags that do not cause compiler errors (from the JLS):
One might expect the if statement to be handled in the following
manner:
An if-then statement can complete normally iff at least one of the
following is true:
The if-then statement is reachable and the condition expression is not
a constant expression whose value is true.
The then-statement can complete normally.
The then-statement is reachable iff the if-then statement is reachable
and the condition expression is not a constant expression whose value
is false.
An if-then-else statement can complete normally iff the then-statement
can complete normally or the else-statement can complete normally.
The then-statement is reachable iff the if-then-else statement is
reachable and the condition expression is not a constant expression
whose value is false.
The else-statement is reachable iff the if-then-else statement is
reachable and the condition expression is not a constant expression
whose value is true.
This approach would be consistent with the treatment of other control
structures. However, in order to allow the if statement to be used
conveniently for "conditional compilation" purposes, the actual rules
differ.
As an example, the following statement results in a compile-time
error:
while (false) { x=3; } because the statement x=3; is not reachable;
but the superficially similar case:
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.
Why does the conditional break statement result in a compiler error?
As quoted in the loop reachability rules, a while loop can also complete normally if it contains a reachable break statement. Since the rules for the reachability of an if statement's then clause do not take the condition of the if into consideration at all, such a conditional if statement's then clause is always considered reachable.
If the break is reachable, then the code after the loop is once again also considered reachable. Since there is no reachable code that results in abrupt termination after the loop, the method is then considered able to complete normally, and so the compiler flags it as an error.
Given the following code sample:
public class WeirdStuff {
public static int doSomething() {
while(true);
}
public static void main(String[] args) {
doSomething();
}
}
This is a valid Java program, although the method doSomething() should return an int but never does. If you run it, it will end in an infinite loop. If you put the argument of the while loop in a separate variable (e.g. boolean bool = true) the compiler will tell you to return an int in this method.
So my question is: is this somewhere in the Java specification and are there situation where this behavior might be useful?
I'll just quote the Java Language Specification, as it's rather clear on this:
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. Except for the special treatment of while, do, and for statements whose condition expression has the constant value true, the values of expressions are not taken into account in the flow analysis.
...
A while statement can complete normally iff at least one of the following is true:
The while statement is reachable and the condition expression is not a constant expression with value true.
There is a reachable break statement that exits the while statement.
...
Every other statement S in a nonempty block that is not a switch block is reachable iff the statement preceding S can complete normally.
And then apply the above definitions to this:
If a method is declared to have a return type, then every return statement (§14.17) in its body must have an Expression. A compile-time error occurs if the body of the method can complete normally (§14.1).
In other words, a method with a return type must return only by using a return statement that provides a value return; it is not allowed to "drop off the end of its body."
Note that it is possible for a method to have a declared return type and yet contain no return statements. Here is one example:
class DizzyDean {
int pitch() { throw new RuntimeException("90 mph?!"); }
}
Java specification defines a concept called Unreachable statements. You are not allowed to have an unreachable statement in your code (it's a compile time error). A while(true); statement makes the following statements unreachable by definition. You are not even allowed to have a return statement after the while(true); statement in Java. Note that while Halting problem is undecidable in generic case, the definition of Unreachable Statement is more strict than just halting. It's deciding very specific cases where a program definitely does not halt. The compiler is theoretically not able to detect all infinite loops and unreachable statements but it has to detect specific cases defined in the spec.
If you are asking if infinite loops can be useful, the answer is yes. There are plenty of situations where you want something running forever, though the loop will usually be terminated at some point.
As to your question: "Can java recognized when a loop will be infinite?" The answer is that it is impossible for a computer to have an algorithm to determine if a program will run forever or not. Read about: Halting Problem
Reading a bit more, your question is also asking why the doSomething() function does not complain that it is not returning an int.
Interestingly the following source does NOT compile.
public class test {
public static int doSomething() {
//while(true);
boolean test=true;
while(test){
}
}
public static void main(String[] args) {
doSomething();
}
}
This indicates to me that, as the wiki page on the halting problem suggests, it is impossible for there to be an algorithm to determine if every problem will terminate, but this does not mean someone hasn't added the simple case:
while(true);
to the java spec. My example above is a little more complicated, so Java can't have it remembered as an infinite loop. Truely, this is a weird edge case, but it's there just to make things compile. Maybe someone will try other combinations.
EDIT: not an issue with unreachable code.
import java.util.*;
public class test {
public static int doSomething() {
//while(true);
while(true){
System.out.println("Hello");
}
}
public static void main(String[] args) {
doSomething();
}
}
The above works, so the while(true); isn't being ignored by the compiler as unreachable, otherwise it would throw a compile time error!
Yes, you can see these 'infinite' loops in some threads, for example server threads that listen on a certain port for incoming messages.
So my question is: is this somewhere in the Java specification
The program is legal Java according to the specification. The JLS (and Java compiler) recognize that the method cannot return, and therefore no return statement is required. Indeed, if you added a return statement after the loop, the Java compiler would give you a compilation error because the return statement would be unreachable code.
and are there situation where this behavior might be useful?
I don't think so, except possibly in obscure unit tests.
I occasionally write methods that will never return (normally), but putting the current thread into an uninterruptible infinite busy-loop rarely makes any sense.
After rereading the question....
Java understands while(true); can never actually complete, it does not trace the following code completely.
boolean moo = true;
while (moo);
Is this useful? Doubtful.
You might be implementing a general interface such that, even though the method may exit with a meaningful return value, your particular implementation is a useful infinite loop (for example, a network server) which never has a situation where it should exit, i.e. trigger whatever action returning a value means.
Also, regarding code like boolean x = true; while (x);, this will compile given a final modifier on x. I don't know offhand but I would imagine this is Java's choice of reasonable straightforward constant expression analysis (which needs to be defined straightforwardly since, due to this rejection of programs dependent on it, it is part of the language definition).
Some notes about unreachable statements:
In java2 specs the description of 'unreachable statement' could be found. Especially interesting the following sentence:
Except for the special treatment of while, do, and for statements whose condition expression has the constant value true, the values of expressions are not taken into account in the flow analysis.
So, it is not obviously possible to exit from while (true); infinite loop. However, there were two more options: change cached values or hack directly into class file or JVM operating memory space.
The following code block gives me a compile time error.
while(false)
{
System.out.println("HI");
}
The error says that there is an unreachable statement.
BUT the following code compiles
boolean b=false;
while(b)
{
System.out.println("Hi");
}
All i could think of was this -> In case-1 as false is a literal so the compiler finds that its unreachable and in case 2 variable b in while condition block is checked at runtime so there is no compilation error?
The compiler writers do not get to decide which conditions to flag as errors - the rules are in the Java Language Specification, for this issue in 14.21 Unreachable Statements.
The relevant sentence is: "The contained statement is reachable iff the while statement is reachable and the condition expression is not a constant expression whose value is false."
In each case, you have a reachable while statement. In the first version, false is a constant expression whose value is false, so the contained statement is not considered reachable. In the second version, b is not a constant expression at all, so the contained statement is treated as being reachable.
Adding final to the declaration of b changes the while condition to a constant expression whose value is false, making the contained statement unreachable again.
Specifying the rules for what is and is not a compile time error in the JLS has the benefit that all Java compilers should accept the same set of programs. The rules generally do not require the compiler to do data flow analysis, presumably to limit the cost and difficulty of writing compilers.
The compiler sees while(false), which will never be true, so you cannot reach the println. This throws an error.
Meanwhile, although while(b) will never be true either, the compiler doesn't automatically know this, because b isn't automatically false, it is a boolean that happens to have the value false, it could change at some point. (It doesn't here, but it could have).
To make this point more general, the compiler will look at the type of a variable, but not what the variable actually is. Many beginning programming classes have sections which deal with how polymorphism and type casting leads to run-time errors in some cases, and compiler errors in others. If you happen to have taken a course such as these, you can think of your question here as having a similar explanation.
This makes sense to me. While (false) will never evaluate successfully, whereas in the case of while(b) - the value of b could be updated to true at some point in the program's lifetime.
The compiler isn't extremely smart about things. It'll find a block where you have a provably false condition, and it'll cry. Give it something like this, and javac isn;t wired to look even that far. Tings obvious to humans are not necessarily obvious to computers, and vice versa.
Compiler sees while(false) and can determine that block inside while won't execute ever.
while in case of boolean, value of b evaluated at runtime so it won't stop you
I often find when debugging a program it is convenient, (although arguably bad practice) to insert a return statement inside a block of code. I might try something like this in Java ....
class Test {
public static void main(String args[]) {
System.out.println("hello world");
return;
System.out.println("i think this line might cause a problem");
}
}
of course, this would yield the compiler error.
Test.java:7: unreachable statement
I could understand why a warning might be justified as having unused code is bad practice. But I don't understand why this needs to generate an error.
Is this just Java trying to be a Nanny, or is there a good reason to make this a compiler error?
Because unreachable code is meaningless to the compiler. Whilst making code meaningful to people is both paramount and harder than making it meaningful to a compiler, the compiler is the essential consumer of code. The designers of Java take the viewpoint that code that is not meaningful to the compiler is an error. Their stance is that if you have some unreachable code, you have made a mistake that needs to be fixed.
There is a similar question here: Unreachable code: error or warning?, in which the author says "Personally I strongly feel it should be an error: if the programmer writes a piece of code, it should always be with the intention of actually running it in some scenario." Obviously the language designers of Java agree.
Whether unreachable code should prevent compilation is a question on which there will never be consensus. But this is why the Java designers did it.
A number of people in comments point out that there are many classes of unreachable code Java doesn't prevent compiling. If I understand the consequences of Gödel correctly, no compiler can possibly catch all classes of unreachable code.
Unit tests cannot catch every single bug. We don't use this as an argument against their value. Likewise a compiler can't catch all problematic code, but it is still valuable for it to prevent compilation of bad code when it can.
The Java language designers consider unreachable code an error. So preventing it compiling when possible is reasonable.
(Before you downvote: the question is not whether or not Java should have an unreachable statement compiler error. The question is why Java has an unreachable statement compiler error. Don't downvote me just because you think Java made the wrong design decision.)
There is no definitive reason why unreachable statements must be not be allowed; other languages allow them without problems. For your specific need, this is the usual trick:
if (true) return;
It looks nonsensical, anyone who reads the code will guess that it must have been done deliberately, not a careless mistake of leaving the rest of statements unreachable.
Java has a little bit support for "conditional compilation"
http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21
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.
It is Nanny.
I feel .Net got this one right - it raises a warning for unreachable code, but not an error. It is good to be warned about it, but I see no reason to prevent compilation (especially during debugging sessions where it is nice to throw a return in to bypass some code).
I only just noticed this question, and wanted to add my $.02 to this.
In case of Java, this is not actually an option. The "unreachable code" error doesn't come from the fact that JVM developers thought to protect developers from anything, or be extra vigilant, but from the requirements of the JVM specification.
Both Java compiler, and JVM, use what is called "stack maps" - a definite information about all of the items on the stack, as allocated for the current method. The type of each and every slot of the stack must be known, so that a JVM instruction doesn't mistreat item of one type for another type. This is mostly important for preventing having a numeric value ever being used as a pointer. It's possible, using Java assembly, to try to push/store a number, but then pop/load an object reference. However, JVM will reject this code during class validation,- that is when stack maps are being created and tested for consistency.
To verify the stack maps, the VM has to walk through all the code paths that exist in a method, and make sure that no matter which code path will ever be executed, the stack data for every instruction agrees with what any previous code has pushed/stored in the stack. So, in simple case of:
Object a;
if (something) { a = new Object(); } else { a = new String(); }
System.out.println(a);
at line 3, JVM will check that both branches of 'if' have only stored into a (which is just local var#0) something that is compatible with Object (since that's how code from line 3 and on will treat local var#0).
When compiler gets to an unreachable code, it doesn't quite know what state the stack might be at that point, so it can't verify its state. It can't quite compile the code anymore at that point, as it can't keep track of local variables either, so instead of leaving this ambiguity in the class file, it produces a fatal error.
Of course a simple condition like if (1<2) will fool it, but it's not really fooling - it's giving it a potential branch that can lead to the code, and at least both the compiler and the VM can determine, how the stack items can be used from there on.
P.S. I don't know what .NET does in this case, but I believe it will fail compilation as well. This normally will not be a problem for any machine code compilers (C, C++, Obj-C, etc.)
One of the goals of compilers is to rule out classes of errors. Some unreachable code is there by accident, it's nice that javac rules out that class of error at compile time.
For every rule that catches erroneous code, someone will want the compiler to accept it because they know what they're doing. That's the penalty of compiler checking, and getting the balance right is one of the tricker points of language design. Even with the strictest checking there's still an infinite number of programs that can be written, so things can't be that bad.
While I think this compiler error is a good thing, there is a way you can work around it.
Use a condition you know will be true:
public void myMethod(){
someCodeHere();
if(1 < 2) return; // compiler isn't smart enough to complain about this
moreCodeHere();
}
The compiler is not smart enough to complain about that.
It is certainly a good thing to complain the more stringent the compiler is the better, as far as it allows you to do what you need.
Usually the small price to pay is to comment the code out, the gain is that when you compile your code works. A general example is Haskell about which people screams until they realize that their test/debugging is main test only and short one. I personally in Java do almost no debugging while being ( in fact on purpose) not attentive.
If the reason for allowing if (aBooleanVariable) return; someMoreCode; is to allow flags, then the fact that if (true) return; someMoreCode; does not generate a compile time error seems like inconsistency in the policy of generating CodeNotReachable exception, since the compiler 'knows' that true is not a flag (not a variable).
Two other ways which might be interesting, but don't apply to switching off part of a method's code as well as if (true) return:
Now, instead of saying if (true) return; you might want to say assert false and add -ea OR -ea package OR -ea className to the jvm arguments. The good point is that this allows for some granularity and requires adding an extra parameter to the jvm invocation so there is no need of setting a DEBUG flag in the code, but by added argument at runtime, which is useful when the target is not the developer machine and recompiling & transferring bytecode takes time.
There is also the System.exit(0) way, but this might be an overkill, if you put it in Java in a JSP then it will terminate the server.
Apart from that Java is by-design a 'nanny' language, I would rather use something native like C/C++ for more control.