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.