<p>In Java, the following is a compile-time error, due to the unreachable statement:</p>
<pre class="lang-java prettyprint-override"><code>while(true) {
    break;
    System.out.println(&quot;unreachable&quot;);
}
</code></pre>
<p>In Rust, the equivalent code is not:</p>
<pre class="lang-rust prettyprint-override"><code>loop {
    break;
    println!(&quot;unreachable&quot;);
}
</code></pre>
<p>In general, determining reachability is undecidable; however, that doesn't stop compilers from applying rules which can statically detect some but not all unreachable code. The Rust compiler already has logic for this, in order to emit a warning (and also to discard the unreachable code), but the same logic is not used to emit an error message. So in Rust and likely many other cases, this is a design decision, not a feasibility issue.</p>
<p>What reasons have the designers of languages given for or against emitting errors for unreachable code? I'm particularly interested in the rationales for mainstream languages which either do make this an error, or which don't but have plenty of other static checks; authoritative answers about other languages are also welcome.</p>

<history><design-choice-comparison><compiler-checks>
Why do (or don't) languages forbid unreachable code?
CC BY-SA 4.0
2024-03-21T22:17:35.150Z
Languages that expect to be used as a target for code generators generally don't want to flag errors about things that are merely inefficient or odd and not definitely wrong.
Chris Dodd
CC BY-SA 4.0
2024-03-22T20:51:52.120Z
Some languages (such as ABB's RAPID) are designed to run in the hardware space as well as the software space. In such cases, unreachable code CAN be executed by manually shifting the point of execution. Using ABB as the example, the industrial robots may have a set of motions locked behind and IF(FALSE) to prevent automatic running, but allow a programmer to manually execute that path. As a programmer who occupies both programming worlds, I can see the merits of having unreachable code, even in PROD-ENV in a pure software system. This is becoming increasingly prevalent due to MC's and Python.
GOATNine
CC BY-SA 4.0
2024-03-23T07:25:12.190Z
@GOATNine *"manually shifting the point of execution"* is equivalent to a GOTO, or at least a loader or debugger executing at an arbitrary location.
smci
CC BY-SA 4.0
2024-03-23T08:21:31.360Z
There's *code that's unreachable at compile-time* vs *code that's unreachable at run-time*. Instead of `while(true)` it could be something like `if (LOGGING_ENABLED)`. Often used to turn on/off debug statements, logging/ logging levels, general verbosity, assertions, etc. That way we can have extra functionality or debug in the object-code but choose to turn it off at runtime for performance/common-case.
smci
CC BY-SA 4.0
2024-03-23T08:25:22.007Z
Also, IIRC one of the side-effects of removing code that is only reachable in the debug case is it can cause associated memory allocation, strings etc. to move around a lot (unwantedly), which in turn can make bugs (buffer overflows, off-by-one errors, etc.) only get tickled in runtime but not debug-mode build, since debug build adds a lot of pessimism and padding. That might not be desirable.
smci
CC BY-SA 4.0
2024-03-23T20:29:33.417Z
If memory serves me right, Java was made to fix C/C++ perceived shortcomings (lots of spit was shed in savage IRC battles on that topic...), while Rust tries to retain compatibility with that half a century worth of backward compatibility disguised as a language (seriously, its 2024 and we still cant reliably tell the size of integer because 7-bit bytes was a norm for few decades).
And now Java is stuck with a questionable feature for backward compatibility just because someone had strong opinion on some topic few decades ago :)
PTwr
CC BY-SA 4.0
2024-03-24T20:24:11.753Z
There are lots of answers now on why language X doesn't error on unreachable code.  Will nobody write an answer that explains why Jave **does**?
James K
CC BY-SA 4.0
2024-03-24T20:35:13.737Z
@PTwr Java would not break backwards compatibility if they decided to stop making this an error.
kaya3
CC BY-SA 4.0
2024-03-26T18:55:54.863Z
@kaya3 yea. I was kinda rambling without thinking about this specific feature by that point :)
Tho it would wreck havock with all the IDE's, stylecheckers, backporting. Just think how such change in basic assumption would wreck cool toys that work with bytecode  !
PTwr
CC BY-SA 4.0
2024-03-26T19:05:13.583Z
@JamesK I managed to avoid Java so I am a bit rusty, but from what I remember the thought process was about making safe, modern, clean, and easy (in that order) language for banking because COBOL programers started dying out and C/C++ features easy memory corruption.
In this case its easy to tell the thought process, "If everyone should obey the Warning to delete that code then why not make it compulsory?". Sounds logical, and its comon for release builds to error on such warnnings for good reasons. But in develop its annoying :D
PTwr
<p>In languages that have a preprocessor, templates or macros, unreachable code often occurs without being an issue:</p>
<ul>
<li>Configuration options checked with <code>#ifdef</code> can affect what code is run. It's not always reasonable to <code>#ifdef</code> out the unreachable code, as the condition could get much more complex than the individual checks.</li>
<li>Target platform affects the size of types like <code>int</code>, which causes unreachable branches in checks based on <code>sizeof(int)</code>. This is usually indicative of the opposite of a bug: the code deliberately handles platform differences by checking the size.</li>
<li>C++ templates can result in unreachable code by checking the properties of types, or by numeric range limitations.</li>
<li>Compiler optimizations and inlining result in unreachable code when functions or macros are used in specific cases.</li>
</ul>
<p>That said, many C and C++ compilers do support a warning about unreachable code, and usually also allow configuring warnings as errors.</p>

CC BY-SA 4.0
2024-03-22T07:25:30.357Z
2024-03-22T07:25:30.357Z
jpa
CC BY-SA 4.0
2024-03-22T15:58:06.447Z
I would argue that #if FALSE code isn't so much unreachable as non-existant from the compiler perspective.
SoronelHaetir
CC BY-SA 4.0
2024-03-22T18:38:28.050Z
@SoronelHaetir I agree, but if you have `#if CONFIG_OPTION return do_other_thing(); #endif  ... normal stuff ..` the normal stuff can become unreachable. With a single config option you can always `#else` it, but with multiple options it gets complex quick. Though many would say that a lot of `#ifdef`s is a code smell to start with :)
jpa
CC BY-SA 4.0
2024-03-23T14:08:32.290Z
+1 Yes, this is _the_ reason. The same reasoning applies to languages that heavily rely on `inline`s: it's easier to tolerate a few unreachable branches in the inlined / generated code here and there, rather than forcing the user to make their `inline`d method / macros super-complex in order to avoid the generation of unreachable branches.
Andrey Tyukin
<p>Great question. I can tell you a bit about C# and why in C#, unreachable code is a warning, not an error. I'll start by briefly describing how reachability detection works, then what it is used for, and then why it's a warning, not an error.</p>
<p>Unreachable code is detected by a few <em>relatively</em> simple rules. The simplest are ones like the case you describe; we can very easily partition statements into &quot;blocks&quot; where control enters at the top of the block and does not leave normally (that is, without an exception) until the end of the block. If no control flow enters the top of a basic block, the whole block is unreachable.</p>
<p>Those statements that always &quot;go somewhere else&quot; (goto, return, break, continue, throw) are said to have an &quot;unreachable endpoint&quot;. As does any statement that is itself unreachable.</p>
<p>Next, C# does an analysis of compile-time constant expressions; these are expressions involving only literals or named constants. The <code>else</code> block of an <code>if(0==0)</code> condition is unreachable, but C# would consider the <code>else</code> block of <code>if(x * 0 == 0)</code> for integer variable <code>x</code> to be reachable, since the expression is not a constant expression. (Fun fact, C# 1 and 2 had a bug where it ran the arithmetic optimizer too early and treated some expressions of this form as constants; I fixed it for C# 3.)  <code>while(true) {}</code> has an unreachable endpoint.</p>
<p>C# also analyzes the logical or/and expressions to determine reachability, which is where the rules get a little complicated; see the spec for details.</p>
<p>And let me finish this brief overview with the puzzle I always pose when discussing reachability. There is a C# program where there is a reachable goto, but its target label is unreachable. Can you give an example of such a program?</p>
<hr />
<p>Why does C# care about reachability at all?</p>
<ul>
<li><p>The end point of the last statement in a method with a non-void return type must be unreachable, otherwise we have a bug; the method doesn't necessarily return a value.</p>
</li>
<li><p>If the end point of the last statement in a method is reachable then every <code>out</code> parameter must be definitely assigned before that, otherwise we have a bug, we are possibly returning without assigning the out parameter.</p>
</li>
<li><p>In C and C++, the switch statement is bug prone. In a switch in C, each label starts a section, that section can be empty, and so in order to have two labels with the same consequence, control must fall through from the bottom of one section to the next. C# fixes this logical problem by instead saying that a section is never empty; it always has at least one statement, and that a section may have multiple labels. A section must have an unreachable endpoint.</p>
</li>
<li><p>C# disallows reading from a <em>local</em> variable before it is <em>known</em> to have been assigned, again because this is a common source of bugs. The rules that determine when a variable is known to be assigned and known to be read are based on reachability analysis. If there is a possibly-reachable read where the corresponding write is unreachable or possibly unreachable, that's an error. But <strong>a read of a possibly-unassigned local is not an error if the read itself is unreachable</strong>.</p>
</li>
</ul>
<p>What I'm getting at here is: we care about reachability analysis because it enables automatically finding common bugs and preventing them at compile time.  But why then is unreachable code not itself a bug that makes a compile-time error? And why is reading from an unassigned local legal in unreachable code?</p>
<hr />
<p>To facilitate debugging!  I'm sure you've encountered this situation of:</p>
<pre><code>if(some complicated and hard to reproduce condition)
{
  there's a bug in this code
} else {
  some perfectly normal code
}
</code></pre>
<p>If the repro is a pain, the natural thing to do is to just write</p>
<pre><code>if(true /* painful repro condition */)
{
  // put your breakpoint here
  there's a bug in this code
} else {
  some perfectly normal code
}
</code></pre>
<p>If that's a compile-time error because the else case is now unreachable, then you're slowing down the debugging process. If it is not a warning, then you're risking the developer accidentally checking in debug code. Therefore the sensible thing to do is to make it a warning.</p>
<p>Same with unassigned locals.</p>
<pre><code>int x;
if (Validate(y, out x))
{
  do something with x
} else {
  uh oh the error case has a bug in it
  but it does not read x
}
</code></pre>
<p>We quickly debug it by</p>
<pre><code>int x;
if (false /*Validate(y, out x)*/)
{
  do something with x
} else {
  // breakpoint here
  uh oh the error case has a bug in it
  but it does not read x   
}
</code></pre>
<p>In this debugging scenario &quot;do something with x&quot; reads x before it is assigned. Doesn't matter! Reading a variable before it is assigned in <em>unreachable code</em> is not an error.  Rather, the unreachable code is a warning so that you don't check it in by mistake.</p>
<p>There are other scenarios as well; <a href="https://ericlippert.com/2012/03/05/why-are-local-variables-definitely-assigned-in-unreachable-statements/" rel="noreferrer">here's one I described in 2012.</a></p>
<hr />
<p>The lesson for language designers here is to think about <em>everything</em> that a developer is going to do. Code is not just written and checked in; it's modified in temporary ways during debugging, often under time pressure, and anything we can do to make that process easier makes developers more productive.</p>
<hr />
<p>If this subject interests you, I've written about it a fair amount. A good start is this now-deleted post on the Coverity blog:</p>
<p><a href="https://web.archive.org/web/20140331160029/http://blog.coverity.com/2013/11/06/c-reachability#.UzmRJ4HP1qY" rel="noreferrer">https://web.archive.org/web/20140331160029/http://blog.coverity.com/2013/11/06/c-reachability#.UzmRJ4HP1qY</a></p>

CC BY-SA 4.0
2024-03-21T19:55:10.693Z
2024-03-22T17:56:06.090Z
2024-03-22T17:56:06.090Z
Eric Lippert
CC BY-SA 4.0
2024-03-22T07:56:38.643Z
Also of note is that C is still widely used today, and is used prominently (maybe exclusively or almost exclusively) in bare-metal embedded development. In that case, where you read a variable, it might not even be a variable at all, but a hardware element, a sensor value, etc. therefore the compiler should not disallow you from reading an unassigned variable, because it might be assigned by hardware. Similarly, code can be executed by hardware interrupts, which the compiler might now nothing about.
vsz
CC BY-SA 4.0
2024-03-22T08:24:07.330Z
As a longtime C# developer, when I read the title, I was thinking "Man, I introduce unreachable code when debugging and rapid prototyping all the time!", it was really interesting to read your answer. I'm grateful to you and the team for thinking of the many man hours I have saved :-)
razethestray
CC BY-SA 4.0
2024-03-22T09:15:46.650Z
@vsz: This restriction on C# only applies to locals and "out" formals; fields, array elements, pointer dereferences, are assumed to be initially assigned.
Eric Lippert
CC BY-SA 4.0
2024-03-22T09:53:18.343Z
@vsz when we access hardware from embedded code we do so by dereferencing a volatile pointer variable eg `volatile unsigned int *`. The pointer variable itself must be initialized to the correct address (alternatively we dereference a pointer literal). When we talk about uninitialized variables in C we are talking a non-static variable that is local to a block and has not been assigned yet. Such a variable will usually reside on the stack or it may be optimized into a register. Either way its contents are considered unknown and the standard says reading it is undefined behaviour.
Rodney
CC BY-SA 4.0
2024-03-22T10:03:53.900Z
For those stumped by the "unreachable `goto` target" riddle: https://stackoverflow.com/a/15217509/1662268
Brondahl
CC BY-SA 4.0
2024-03-22T14:42:19.550Z
Off-topic(?): why would/did you "worsen" reachability analysis in the case `if(x * 0 == 0)` mentioned? Sure not only to "stick to the letter of the law". C# tends to favour usefulness over mere formality.
Pablo H
CC BY-SA 4.0
2024-03-22T17:36:10.303Z
@PabloH: The C# team takes breaking changes very seriously and we debated whether it was worth it to potentially break someone by making this change. The thing is: if some C# 1 programmer wrote this code, which is almost certainly wrong, then ignored the warning they got and checked it in anyway, do we really care that we are "breaking" them by *not* producing a warning? No, no we do not. And running the optimization passes in the wrong order was causing other knock on effects that were making the compiler uncompliant with the spec, harder to test, and harder to extend.
Eric Lippert
CC BY-SA 4.0
2024-03-22T17:46:25.583Z
@PabloH: There were also bugs in the optimization pass itself; for example, C# at the time made a distinction between "literal zero" and "constant zero", but the arithmetic optimization pass had a bug where `x * 0` was treated as a literal but `0 * x` was treated as a constant. And then I fixed that bug incorrectly and that had knock on effects and other breaking changes, and the whole thing was a mess. It is better to just get the thing compliant with the specification and know that you can test it.
Eric Lippert
CC BY-SA 4.0
2024-03-22T17:51:59.273Z
@PabloH: Also it is worth pointing out that the reachability pass was made to run twice as a result of doing it correctly. The first time we run it on unoptimized code and produce unreachable code warnings, make sure switch sections are correct, make sure methods return values, and so on. Then the optimizer runs, then the reachability checker runs again and removes unreachable code so that the IL generator doesn't see it. If there is some *benefit* in removing intentionally unreachable code only discovered by the optimizer, you still get that benefit.
Eric Lippert
CC BY-SA 4.0
2024-03-22T17:58:32.353Z
@Rodney: Indeed. I note that in C# the reason for giving an error for reading a local before initializing it is NOT because the value of the local will be garbage. The CLR generally ensures that locals are initialized to zero by default. (Though there are some rare exceptions to that.) Rather it is because reading a local before writing it is probably a bug; if you intended it to be default-initialized then it is easy enough to do so explicitly.
Eric Lippert
CC BY-SA 4.0
2024-03-22T18:05:51.553Z
@PabloH: I wrote contemporaneously about these bugs as I was fixing them back in 2006, so if this subject particularly interests you, see https://web.archive.org/web/20140414043617/http://blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx.  I haven't copied those articles to my new blog site yet, so wayback machine it is for now. :)
Eric Lippert
CC BY-SA 4.0
2024-03-22T20:30:54.103Z
Python and Lua have a similar problem with syntactically not allowing code after a `return` or `break`. You have to write `if True: return` (Python) or `do break end` (Lua; the equivalent of `{break;}`) when debugging like this
Criticize SE actions means ban
CC BY-SA 4.0
2024-03-23T05:25:03.573Z
@user253751 That is incorrect. Of the two, only Lua does so, and only after `return` ([testing code](https://gist.github.com/InSyncWithFoo/2a1d18de96588cff5442c2a343c43c55)).
InSync
CC BY-SA 4.0
2024-03-23T19:21:47.443Z
If you have a variable declaration in unreachable code in C# it's an error and it's very annoying. Usually this happens because I need to temporarily lop of large amounts of code for debugging, which is easiest done by an early return statement.
Joshua
<p>Because it's not uncommon to create unreachable code deliberately. Sometimes it's just temporary during incremental development, sometimes it's permanent.</p>
<p>For instance, you might have code like</p>
<pre><code>if (DEBUG) {
    // do stuff
}
</code></pre>
<p>When the <code>DEBUG</code> macro expands to <code>false</code> all this code is unreachable.</p>
<p>Sometimes you have code like</p>
<pre><code>if (condition) {
    // do stuff
}
</code></pre>
<p>and you want to temporarily disable it. So you change the first line to</p>
<pre><code>if (false &amp;&amp; (condition)) {
</code></pre>
<p>There are other ways to accomplish this, such as testing the macro in the preprocessor, or commenting out the whole block of code. But doing it like this is often preferable because the block structure of the code is preserved.</p>

CC BY-SA 4.0
2024-03-21T23:04:26.967Z
2024-03-21T23:04:26.967Z
Barmar
CC BY-SA 4.0
2024-03-22T15:10:15.163Z
Note that Java has a special case for this due to debugging: `if(false){...}` is considered okay from the compiler, but `while(false){...}` is considered an error as unreachable code.
Nayuki
CC BY-SA 4.0
2024-03-22T15:12:39.790Z
@Nayuki And warnings from compilers, linters, and IDEs can also often be silenced with special comments. So my answer is not a completely compelling argument.
Barmar
CC BY-SA 4.0
2024-03-29T17:50:48.920Z
A decade after the "goto fail" bug and this really shouldn't be the correct answer.  Production code should error on unreachable code, while deliberate debug-build-only code for things like assertions that are condition on special flags like DEBUG should be identified by the compiler as an exception.  Any remaining edge cases should be up to the developer to deliberately mark as exceptions with some kind of compiler pragma.  Of course probably not with C, but modern languages should be built this way from the start and the DEBUG build flag should be special.
lamont
<p>I'll come at this from a primarily C perspective.</p>
<blockquote>
<p>In general, determining reachability is undecidable; however,
that doesn't stop compilers from applying rules which can
statically detect some but not all unreachable code.</p>
</blockquote>
<p>Many programming languages predate the existence of compilers that could do that sort of detection.  Early C compilers didn't perform any real optimization at all, they just translated what's on the page directly to assembly.  Forbidding unreachable code would be a rule that was impossible to enforce, thus pointless.  Even in the modern era, trying to enforce this rule can result in code that compiles on one compiler and not on another.  That makes things unnecessarily difficult for the programmer.  This could even happen on the same compiler between two different sets of compiler options.</p>
<p>This becomes even more of a problem when you start trying to write code that's reusable or cross-platform.  The C preprocessor allows you to <code>#ifdef</code> chunks of code and hide them, but that preprocessor is <em>extremely</em> primitive.  It's not uncommon to need to gate a chunk of code using logic that can be resolved at compile time, but is much more sophisticated than what the preprocessor is capable of.  Oversimplified example:</p>

<pre class="lang-c prettyprint-override"><code>void transmit_token(token *tok) /* platform-specific typedef */
{
    queue_token(tok);
    // Receiving device expects a 32-bit transmission,
    // insert padding bytes if necessary.
    for (uint i = 0; i &lt; (4 - sizeof(*tok)); ++i)
        queue_byte(PAD_BYTE);
    transmit_waiting_bytes();
}
</code></pre>
<p>When data type <code>token</code> is 32 bits long, the compiler can easily tell that the <code>for</code> loop is dead code.  The preprocessor has no knowledge of types and can't evaluate a statement that complex, so if dead code was forbidden you'd have to write something like this instead:</p>
<pre class="lang-c prettyprint-override"><code>void transmit_token(token *tok) /* platform-specific typedef */
{
    queue_token(tok);
    // Receiving device expects a 32-bit transmission,
    // insert padding bytes if necessary.
#if defined TOKEN_IS_ONE_BYTE
    queue_byte(PAD_BYTE);
    queue_byte(PAD_BYTE);
    queue_byte(PAD_BYTE);
#elif defined TOKEN_IS_TWO_BYTES
    queue_byte(PAD_BYTE);
    queue_byte(PAD_BYTE);
#elif defined TOKEN_IS_THREE_BYTES
    queue_byte(PAD_BYTE);
#endif
    transmit_waiting_bytes();
}
</code></pre>
<p>Gross.  Not only is it far more complex and copy/paste-y, but now I have to manually define a boat load of platform-specific preprocessor constants.  That introduces a <em>lot</em> of opportunity for human error (don't ask how I know).  When porting this code to a new platform, I now have to read through the entire codebase and determine which lines need platform-specific modifications vs. having a generic version of the code that <em>just works</em>.</p>
<p>On projects that I've worked on personally, a hard rule against dead code would have prevented me from having re-usable code libraries.  I would have needed to maintain separate x86, PPC, and MIPS versions of my library code, and then somehow manually keep them all in sync at all times.  That quickly turns into a bug farm.  Instead, I had a single library that could be built for all platforms.  Some of the code was unreachable for some platforms, but that's perfectly OK.  The compiler detected it and cut it out automatically.</p>
<p>Forbidding dead code could even have legal consequences.  Let's say that you're using a third-party library and the compiler detects dead code in a library header (including a header in C essentially pastes its contents into your source file, so the compiler processes it just like the rest of your code) for your particular combination of platform, compiler, and compile options.  What do you do?  Modify the header to remove the dead code?  A library with a proprietary license likely doesn't give you the rights to make modifications at all.  If the library is using something like the LGPL, modifying it would be considered creating a derivative work and would then require you to publish your source code and do other things that may not be desirable.  All just to get around a compiler error that's complaining about something that isn't actually a problem.  Even if I <em>did</em> have the rights to modify the header, I may not have the source for the library itself. How do I know my changes aren't breaking something?</p>
<p>At the end of the day, dead code is really only a problem if it's <em>unintentionally</em> dead.  The drawbacks of throwing a compiler error here <em>far</em> outweigh the benefits.  That's not to say that C programmers don't care about dead code, we just use a static analyzer for that and not the compiler.</p>

CC BY-SA 4.0
2024-03-22T21:12:55.713Z
2024-03-22T21:12:55.713Z
bta
<p>In Python, only syntax errors and warnings can happen at compile time, and unreachable code wouldn't qualify as a syntactic issue. So reachability analysis simply isn't done. It's designed like this for a few reasons:</p>
<ul>
<li><p>It simplifies understanding of error messages, and lets one get to the unit tests faster.</p>
</li>
<li><p>Everything is so dynamic that it would be hard to prove (non)reachability in pathological cases, anyway. For example, a name for a global variable could be read from a file and used to update the <code>globals()</code> dict, subsequently impacting an <code>if</code> condition later in the same function.</p>
</li>
<li><p>Since things are so dynamic, there is at least some pressure to make compilation fast, since it happens frequently and as part of the execution of the program (rather than all ahead of time).</p>
</li>
<li><p>There is no pressure from correctness requirements, largely due to dynamic typing. For example, there's no problem of &quot;if a non-void function reaches the end without reaching <code>return</code> then it would be a bug for the compiler to accept that&quot; - because every function is non-void, and every function may return a value of any type, and there is a specific value dedicated for the purpose of being the return value of functions that reach the end without encountering <code>return</code> (i.e., <code>None</code>).</p>
</li>
<li><p>Having bytecode that closely reflects the structure of the code is considered an advantage for debugging. Very little optimization is done at the level of peephole optimization of bytecode; the optimization work is focused on the design of the virtual instruction set itself, and on the code that interprets those bytecodes. Even the &quot;optimization&quot; flags only strip out some metadata from the resulting <code>.pyc</code> files. There are some very specific hard-coded cases: e.g. code inside <code>if False:</code> will be optimized out, but code inside even something as trivial as <code>if 1 == 1:</code> will not.</p>
</li>
</ul>

CC BY-SA 4.0
2024-03-23T07:53:12.480Z
2024-03-23T07:53:12.480Z
Karl Knechtel
CC BY-SA 4.0
2024-03-23T08:48:57.780Z
Probably worth pointing out that there are many *extra* tools that do reachability analysis. It’s just something one opts in as desired.
MisterMiyagi
CC BY-SA 4.0
2024-03-23T17:49:48.690Z
These are good points, but is there any authoritative source to show that they are the rationale of the Python designers?
kaya3
CC BY-SA 4.0
2024-03-23T19:30:46.217Z
This is what I've understood from my own expertise. I'm sure there is some documentation for at least some of these points, but I'd have to do fresh research.
Karl Knechtel
<p>Since unreachable code detection is undecidable, as a compiler evolves the detection algorithm can become better and/or worse. It would be a bad idea to have code that compiles successfully on one version and fails on the next simply because the compiler got smarter at detecting unreachable code.</p>

CC BY-SA 4.0
2024-03-22T23:37:43.617Z
2024-03-22T23:37:43.617Z
Rufflewind
CC BY-SA 4.0
2024-03-23T17:53:46.430Z
Java's approach is to exactly specify the rules for deciding which code to error on, and not change those rules to make them error on more code. Meanwhile Typescript also errors on unreachable code, and is happy to push minor updates which report errors in programs where there previously were none, since finding errors is the main reason people use Typescript. This could be a rationale for some languages, but do you know of any where the designers have said it is?
kaya3
<p>The only problem that would be solved by prohibiting unreachable code would be to prevent bloated executables.</p>
<p>That's a performance issue. Compilers aren't supposed to stop the developer from being inefficient.</p>

CC BY-SA 4.0
2024-03-23T22:40:40.247Z
2024-03-23T22:40:40.247Z
EvilSnack
CC BY-SA 4.0
2024-03-23T23:40:14.747Z
Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer).
Community Compiler
CC BY-SA 4.0
2024-03-24T18:02:29.707Z
The problem with unreachable code is not that it takes up space in the executable; a good compiler will skip emitting unreachable code. The problem with unreachable code is **the developer probably believes it is reachable** and the developer is wrong! Prohibiting unreachable code isn't an *optimization*, it's a *bug detector*.
Eric Lippert
CC BY-SA 4.0
2024-03-24T18:24:49.067Z
Compilers should not throw errors for things that are *probably* wrong, but for things that are *certainly* wrong.
EvilSnack
CC BY-SA 4.0
2024-03-24T20:07:11.570Z
@EvilSnack And yet Java and Typescript are two examples of mainstream languages which give compile-time errors for unreachable code. I'm sure there are languages where your rationale is the one given for making this decision, but you should identify those languages and show that this is why they designed it that way. Otherwise this is just your opinion.
kaya3
# Why do (or don't) languages forbid unreachable code?
Posted by: username_0 on 2024-03-21
In Java, the following is a compile-time error, due to the unreachable statement:
while(true) {
    break;
    System.out.println("unreachable");
}

In Rust, the equivalent code is not:
loop {
    break;
    println!("unreachable");
}

In general, determining reachability is undecidable; however, that doesn't stop compilers from applying rules which can statically detect some but not all unreachable code. The Rust compiler already has logic for this, in order to emit a warning (and also to discard the unreachable code), but the same logic is not used to emit an error message. So in Rust and likely many other cases, this is a design decision, not a feasibility issue.
What reasons have the designers of languages given for or against emitting errors for unreachable code? I'm particularly interested in the rationales for mainstream languages which either do make this an error, or which don't but have plenty of other static checks; authoritative answers about other languages are also welcome.
**Tags:** history design-choice-comparison compiler-checks
> **Comments:**
> **username_1**: Languages that expect to be used as a target for code generators generally don't want to flag errors about things that are merely inefficient or odd and not definitely wrong.  
> **username_2**: Some languages (such as ABB's RAPID) are designed to run in the hardware space as well as the software space. In such cases, unreachable code CAN be executed by manually shifting the point of execution. Using ABB as the example, the industrial robots may have a set of motions locked behind and IF(FALSE) to prevent automatic running, but allow a programmer to manually execute that path. As a programmer who occupies both programming worlds, I can see the merits of having unreachable code, even in PROD-ENV in a pure software system. This is becoming increasingly prevalent due to MC's and Python.  
> **username_3**: @GOATNine *"manually shifting the point of execution"* is equivalent to a GOTO, or at least a loader or debugger executing at an arbitrary location.  
> **username_3**: There's *code that's unreachable at compile-time* vs *code that's unreachable at run-time*. Instead of `while(true)` it could be something like `if (LOGGING_ENABLED)`. Often used to turn on/off debug statements, logging/ logging levels, general verbosity, assertions, etc. That way we can have extra functionality or debug in the object-code but choose to turn it off at runtime for performance/common-case.  
> **username_3**: Also, IIRC one of the side-effects of removing code that is only reachable in the debug case is it can cause associated memory allocation, strings etc. to move around a lot (unwantedly), which in turn can make bugs (buffer overflows, off-by-one errors, etc.) only get tickled in runtime but not debug-mode build, since debug build adds a lot of pessimism and padding. That might not be desirable.  
> **username_4**: If memory serves me right, Java was made to fix C/C++ perceived shortcomings (lots of spit was shed in savage IRC battles on that topic...), while Rust tries to retain compatibility with that half a century worth of backward compatibility disguised as a language (seriously, its 2024 and we still cant reliably tell the size of integer because 7-bit bytes was a norm for few decades).
And now Java is stuck with a questionable feature for backward compatibility just because someone had strong opinion on some topic few decades ago :)  
> **username_5**: There are lots of answers now on why language X doesn't error on unreachable code.  Will nobody write an answer that explains why Jave **does**?  
> **username_0**: @PTwr Java would not break backwards compatibility if they decided to stop making this an error.  
> **username_4**: @kaya3 yea. I was kinda rambling without thinking about this specific feature by that point :)
Tho it would wreck havock with all the IDE's, stylecheckers, backporting. Just think how such change in basic assumption would wreck cool toys that work with bytecode  !  
> **username_4**: @JamesK I managed to avoid Java so I am a bit rusty, but from what I remember the thought process was about making safe, modern, clean, and easy (in that order) language for banking because COBOL programers started dying out and C/C++ features easy memory corruption.
In this case its easy to tell the thought process, "If everyone should obey the Warning to delete that code then why not make it compulsory?". Sounds logical, and its comon for release builds to error on such warnnings for good reasons. But in develop its annoying :D  
---
## 7 answers
### Answer by username_9 on 2024-03-21. Score: 61
Great question. I can tell you a bit about C# and why in C#, unreachable code is a warning, not an error. I'll start by briefly describing how reachability detection works, then what it is used for, and then why it's a warning, not an error.
Unreachable code is detected by a few relatively simple rules. The simplest are ones like the case you describe; we can very easily partition statements into "blocks" where control enters at the top of the block and does not leave normally (that is, without an exception) until the end of the block. If no control flow enters the top of a basic block, the whole block is unreachable.
Those statements that always "go somewhere else" (goto, return, break, continue, throw) are said to have an "unreachable endpoint". As does any statement that is itself unreachable.
Next, C# does an analysis of compile-time constant expressions; these are expressions involving only literals or named constants. The else block of an if(0==0) condition is unreachable, but C# would consider the else block of if(x * 0 == 0) for integer variable x to be reachable, since the expression is not a constant expression. (Fun fact, C# 1 and 2 had a bug where it ran the arithmetic optimizer too early and treated some expressions of this form as constants; I fixed it for C# 3.)  while(true) {} has an unreachable endpoint.
C# also analyzes the logical or/and expressions to determine reachability, which is where the rules get a little complicated; see the spec for details.
And let me finish this brief overview with the puzzle I always pose when discussing reachability. There is a C# program where there is a reachable goto, but its target label is unreachable. Can you give an example of such a program?

Why does C# care about reachability at all?

The end point of the last statement in a method with a non-void return type must be unreachable, otherwise we have a bug; the method doesn't necessarily return a value.

If the end point of the last statement in a method is reachable then every out parameter must be definitely assigned before that, otherwise we have a bug, we are possibly returning without assigning the out parameter.

In C and C++, the switch statement is bug prone. In a switch in C, each label starts a section, that section can be empty, and so in order to have two labels with the same consequence, control must fall through from the bottom of one section to the next. C# fixes this logical problem by instead saying that a section is never empty; it always has at least one statement, and that a section may have multiple labels. A section must have an unreachable endpoint.

C# disallows reading from a local variable before it is known to have been assigned, again because this is a common source of bugs. The rules that determine when a variable is known to be assigned and known to be read are based on reachability analysis. If there is a possibly-reachable read where the corresponding write is unreachable or possibly unreachable, that's an error. But a read of a possibly-unassigned local is not an error if the read itself is unreachable.


What I'm getting at here is: we care about reachability analysis because it enables automatically finding common bugs and preventing them at compile time.  But why then is unreachable code not itself a bug that makes a compile-time error? And why is reading from an unassigned local legal in unreachable code?

To facilitate debugging!  I'm sure you've encountered this situation of:
if(some complicated and hard to reproduce condition)
{
  there's a bug in this code
} else {
  some perfectly normal code
}

If the repro is a pain, the natural thing to do is to just write
if(true /* painful repro condition */)
{
  // put your breakpoint here
  there's a bug in this code
} else {
  some perfectly normal code
}

If that's a compile-time error because the else case is now unreachable, then you're slowing down the debugging process. If it is not a warning, then you're risking the developer accidentally checking in debug code. Therefore the sensible thing to do is to make it a warning.
Same with unassigned locals.
int x;
if (Validate(y, out x))
{
  do something with x
} else {
  uh oh the error case has a bug in it
  but it does not read x
}

We quickly debug it by
int x;
if (false /*Validate(y, out x)*/)
{
  do something with x
} else {
  // breakpoint here
  uh oh the error case has a bug in it
  but it does not read x   
}

In this debugging scenario "do something with x" reads x before it is assigned. Doesn't matter! Reading a variable before it is assigned in unreachable code is not an error.  Rather, the unreachable code is a warning so that you don't check it in by mistake.
There are other scenarios as well; here's one I described in 2012.

The lesson for language designers here is to think about everything that a developer is going to do. Code is not just written and checked in; it's modified in temporary ways during debugging, often under time pressure, and anything we can do to make that process easier makes developers more productive.

If this subject interests you, I've written about it a fair amount. A good start is this now-deleted post on the Coverity blog:
https://web.archive.org/web/20140331160029/http://blog.coverity.com/2013/11/06/c-reachability#.UzmRJ4HP1qY
> **Comments:**
> **username_10** (2024-03-22): Also of note is that C is still widely used today, and is used prominently (maybe exclusively or almost exclusively) in bare-metal embedded development. In that case, where you read a variable, it might not even be a variable at all, but a hardware element, a sensor value, etc. therefore the compiler should not disallow you from reading an unassigned variable, because it might be assigned by hardware. Similarly, code can be executed by hardware interrupts, which the compiler might now nothing about.  
> **username_11** (2024-03-22): As a longtime C# developer, when I read the title, I was thinking "Man, I introduce unreachable code when debugging and rapid prototyping all the time!", it was really interesting to read your answer. I'm grateful to you and the team for thinking of the many man hours I have saved :-)  
> **username_9** (2024-03-22): @vsz: This restriction on C# only applies to locals and "out" formals; fields, array elements, pointer dereferences, are assumed to be initially assigned.  
> **username_12** (2024-03-22): @vsz when we access hardware from embedded code we do so by dereferencing a volatile pointer variable eg `volatile unsigned int *`. The pointer variable itself must be initialized to the correct address (alternatively we dereference a pointer literal). When we talk about uninitialized variables in C we are talking a non-static variable that is local to a block and has not been assigned yet. Such a variable will usually reside on the stack or it may be optimized into a register. Either way its contents are considered unknown and the standard says reading it is undefined behaviour.  
> **username_13** (2024-03-22): For those stumped by the "unreachable `goto` target" riddle: https://stackoverflow.com/a/15217509/1662268  
> **username_14** (2024-03-22): Off-topic(?): why would/did you "worsen" reachability analysis in the case `if(x * 0 == 0)` mentioned? Sure not only to "stick to the letter of the law". C# tends to favour usefulness over mere formality.  
> **username_9** (2024-03-22): @PabloH: The C# team takes breaking changes very seriously and we debated whether it was worth it to potentially break someone by making this change. The thing is: if some C# 1 programmer wrote this code, which is almost certainly wrong, then ignored the warning they got and checked it in anyway, do we really care that we are "breaking" them by *not* producing a warning? No, no we do not. And running the optimization passes in the wrong order was causing other knock on effects that were making the compiler uncompliant with the spec, harder to test, and harder to extend.  
> **username_9** (2024-03-22): @PabloH: There were also bugs in the optimization pass itself; for example, C# at the time made a distinction between "literal zero" and "constant zero", but the arithmetic optimization pass had a bug where `x * 0` was treated as a literal but `0 * x` was treated as a constant. And then I fixed that bug incorrectly and that had knock on effects and other breaking changes, and the whole thing was a mess. It is better to just get the thing compliant with the specification and know that you can test it.  
> **username_9** (2024-03-22): @PabloH: Also it is worth pointing out that the reachability pass was made to run twice as a result of doing it correctly. The first time we run it on unoptimized code and produce unreachable code warnings, make sure switch sections are correct, make sure methods return values, and so on. Then the optimizer runs, then the reachability checker runs again and removes unreachable code so that the IL generator doesn't see it. If there is some *benefit* in removing intentionally unreachable code only discovered by the optimizer, you still get that benefit.  
> **username_9** (2024-03-22): @Rodney: Indeed. I note that in C# the reason for giving an error for reading a local before initializing it is NOT because the value of the local will be garbage. The CLR generally ensures that locals are initialized to zero by default. (Though there are some rare exceptions to that.) Rather it is because reading a local before writing it is probably a bug; if you intended it to be default-initialized then it is easy enough to do so explicitly.  
> **username_9** (2024-03-22): @PabloH: I wrote contemporaneously about these bugs as I was fixing them back in 2006, so if this subject particularly interests you, see https://web.archive.org/web/20140414043617/http://blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx.  I haven't copied those articles to my new blog site yet, so wayback machine it is for now. :)  
> **username_15** (2024-03-22): Python and Lua have a similar problem with syntactically not allowing code after a `return` or `break`. You have to write `if True: return` (Python) or `do break end` (Lua; the equivalent of `{break;}`) when debugging like this  
> **username_16** (2024-03-23): @user253751 That is incorrect. Of the two, only Lua does so, and only after `return` ([testing code](https://gist.github.com/InSyncWithFoo/2a1d18de96588cff5442c2a343c43c55)).  
> **username_17** (2024-03-23): If you have a variable declaration in unreachable code in C# it's an error and it's very annoying. Usually this happens because I need to temporarily lop of large amounts of code for debugging, which is easiest done by an early return statement.  
---
### Answer by username_18 on 2024-03-21. Score: 29
Because it's not uncommon to create unreachable code deliberately. Sometimes it's just temporary during incremental development, sometimes it's permanent.
For instance, you might have code like
if (DEBUG) {
    // do stuff
}

When the DEBUG macro expands to false all this code is unreachable.
Sometimes you have code like
if (condition) {
    // do stuff
}

and you want to temporarily disable it. So you change the first line to
if (false && (condition)) {

There are other ways to accomplish this, such as testing the macro in the preprocessor, or commenting out the whole block of code. But doing it like this is often preferable because the block structure of the code is preserved.
> **Comments:**
> **username_19** (2024-03-22): Note that Java has a special case for this due to debugging: `if(false){...}` is considered okay from the compiler, but `while(false){...}` is considered an error as unreachable code.  
> **username_18** (2024-03-22): @Nayuki And warnings from compilers, linters, and IDEs can also often be silenced with special comments. So my answer is not a completely compelling argument.  
> **username_20** (2024-03-29): A decade after the "goto fail" bug and this really shouldn't be the correct answer.  Production code should error on unreachable code, while deliberate debug-build-only code for things like assertions that are condition on special flags like DEBUG should be identified by the compiler as an exception.  Any remaining edge cases should be up to the developer to deliberately mark as exceptions with some kind of compiler pragma.  Of course probably not with C, but modern languages should be built this way from the start and the DEBUG build flag should be special.  
---
### Answer by username_6 on 2024-03-22. Score: 14
In languages that have a preprocessor, templates or macros, unreachable code often occurs without being an issue:

* Configuration options checked with `#ifdef` can affect what code is run. It's not always reasonable to `#ifdef` out the unreachable code, as the condition could get much more complex than the individual checks.
* Target platform affects the size of types like `int`, which causes unreachable branches in checks based on `sizeof(int)`. This is usually indicative of the opposite of a bug: the code deliberately handles platform differences by checking the size.
* C++ templates can result in unreachable code by checking the properties of types, or by numeric range limitations.
* Compiler optimizations and inlining result in unreachable code when functions or macros are used in specific cases.

That said, many C and C++ compilers do support a warning about unreachable code, and usually also allow configuring warnings as errors.
> **Comments:**
> **username_7** (2024-03-22): I would argue that #if FALSE code isn't so much unreachable as non-existant from the compiler perspective.  
> **username_6** (2024-03-22): @SoronelHaetir I agree, but if you have `#if CONFIG_OPTION return do_other_thing(); #endif  ... normal stuff ..` the normal stuff can become unreachable. With a single config option you can always `#else` it, but with multiple options it gets complex quick. Though many would say that a lot of `#ifdef`s is a code smell to start with :)  
> **username_8** (2024-03-23): +1 Yes, this is _the_ reason. The same reasoning applies to languages that heavily rely on `inline`s: it's easier to tolerate a few unreachable branches in the inlined / generated code here and there, rather than forcing the user to make their `inline`d method / macros super-complex in order to avoid the generation of unreachable branches.  
---
### Answer by username_21 on 2024-03-22. Score: 4
I'll come at this from a primarily C perspective.

In general, determining reachability is undecidable; however,
that doesn't stop compilers from applying rules which can
statically detect some but not all unreachable code.

Many programming languages predate the existence of compilers that could do that sort of detection.  Early C compilers didn't perform any real optimization at all, they just translated what's on the page directly to assembly.  Forbidding unreachable code would be a rule that was impossible to enforce, thus pointless.  Even in the modern era, trying to enforce this rule can result in code that compiles on one compiler and not on another.  That makes things unnecessarily difficult for the programmer.  This could even happen on the same compiler between two different sets of compiler options.
This becomes even more of a problem when you start trying to write code that's reusable or cross-platform.  The C preprocessor allows you to #ifdef chunks of code and hide them, but that preprocessor is extremely primitive.  It's not uncommon to need to gate a chunk of code using logic that can be resolved at compile time, but is much more sophisticated than what the preprocessor is capable of.  Oversimplified example:
void transmit_token(token *tok) /* platform-specific typedef */
{
    queue_token(tok);
    // Receiving device expects a 32-bit transmission,
    // insert padding bytes if necessary.
    for (uint i = 0; i < (4 - sizeof(*tok)); ++i)
        queue_byte(PAD_BYTE);
    transmit_waiting_bytes();
}

When data type token is 32 bits long, the compiler can easily tell that the for loop is dead code.  The preprocessor has no knowledge of types and can't evaluate a statement that complex, so if dead code was forbidden you'd have to write something like this instead:
void transmit_token(token *tok) /* platform-specific typedef */
{
    queue_token(tok);
    // Receiving device expects a 32-bit transmission,
    // insert padding bytes if necessary.
#if defined TOKEN_IS_ONE_BYTE
    queue_byte(PAD_BYTE);
    queue_byte(PAD_BYTE);
    queue_byte(PAD_BYTE);
#elif defined TOKEN_IS_TWO_BYTES
    queue_byte(PAD_BYTE);
    queue_byte(PAD_BYTE);
#elif defined TOKEN_IS_THREE_BYTES
    queue_byte(PAD_BYTE);
#endif
    transmit_waiting_bytes();
}

Gross.  Not only is it far more complex and copy/paste-y, but now I have to manually define a boat load of platform-specific preprocessor constants.  That introduces a lot of opportunity for human error (don't ask how I know).  When porting this code to a new platform, I now have to read through the entire codebase and determine which lines need platform-specific modifications vs. having a generic version of the code that just works.
On projects that I've worked on personally, a hard rule against dead code would have prevented me from having re-usable code libraries.  I would have needed to maintain separate x86, PPC, and MIPS versions of my library code, and then somehow manually keep them all in sync at all times.  That quickly turns into a bug farm.  Instead, I had a single library that could be built for all platforms.  Some of the code was unreachable for some platforms, but that's perfectly OK.  The compiler detected it and cut it out automatically.
Forbidding dead code could even have legal consequences.  Let's say that you're using a third-party library and the compiler detects dead code in a library header (including a header in C essentially pastes its contents into your source file, so the compiler processes it just like the rest of your code) for your particular combination of platform, compiler, and compile options.  What do you do?  Modify the header to remove the dead code?  A library with a proprietary license likely doesn't give you the rights to make modifications at all.  If the library is using something like the LGPL, modifying it would be considered creating a derivative work and would then require you to publish your source code and do other things that may not be desirable.  All just to get around a compiler error that's complaining about something that isn't actually a problem.  Even if I did have the rights to modify the header, I may not have the source for the library itself. How do I know my changes aren't breaking something?
At the end of the day, dead code is really only a problem if it's unintentionally dead.  The drawbacks of throwing a compiler error here far outweigh the benefits.  That's not to say that C programmers don't care about dead code, we just use a static analyzer for that and not the compiler.
---
### Answer by username_22 on 2024-03-23. Score: 1
In Python, only syntax errors and warnings can happen at compile time, and unreachable code wouldn't qualify as a syntactic issue. So reachability analysis simply isn't done. It's designed like this for a few reasons:

* It simplifies understanding of error messages, and lets one get to the unit tests faster.
* Everything is so dynamic that it would be hard to prove (non)reachability in pathological cases, anyway. For example, a name for a global variable could be read from a file and used to update the `globals()` dict, subsequently impacting an `if` condition later in the same function.
* Since things are so dynamic, there is at least some pressure to make compilation fast, since it happens frequently and as part of the execution of the program (rather than all ahead of time).
* There is no pressure from correctness requirements, largely due to dynamic typing. For example, there's no problem of "if a non-void function reaches the end without reaching `return` then it would be a bug for the compiler to accept that" - because every function is non-void, and every function may return a value of any type, and there is a specific value dedicated for the purpose of being the return value of functions that reach the end without encountering `return` (i.e., `None`).
* Having bytecode that closely reflects the structure of the code is considered an advantage for debugging. Very little optimization is done at the level of peephole optimization of bytecode; the optimization work is focused on the design of the virtual instruction set itself, and on the code that interprets those bytecodes. Even the "optimization" flags only strip out some metadata from the resulting `.pyc` files. There are some very specific hard-coded cases: e.g. code inside `if False:` will be optimized out, but code inside even something as trivial as `if 1 == 1:` will not.
> **Comments:**
> **username_23** (2024-03-23): Probably worth pointing out that there are many *extra* tools that do reachability analysis. It’s just something one opts in as desired.  
> **username_0** (2024-03-23): These are good points, but is there any authoritative source to show that they are the rationale of the Python designers?  
> **username_22** (2024-03-23): This is what I've understood from my own expertise. I'm sure there is some documentation for at least some of these points, but I'd have to do fresh research.  
---
### Answer by username_24 on 2024-03-22. Score: -1
Since unreachable code detection is undecidable, as a compiler evolves the detection algorithm can become better and/or worse. It would be a bad idea to have code that compiles successfully on one version and fails on the next simply because the compiler got smarter at detecting unreachable code.
> **Comments:**
> **username_0** (2024-03-23): Java's approach is to exactly specify the rules for deciding which code to error on, and not change those rules to make them error on more code. Meanwhile Typescript also errors on unreachable code, and is happy to push minor updates which report errors in programs where there previously were none, since finding errors is the main reason people use Typescript. This could be a rationale for some languages, but do you know of any where the designers have said it is?  
---
### Answer by username_25 on 2024-03-23. Score: -3
The only problem that would be solved by prohibiting unreachable code would be to prevent bloated executables.

That's a performance issue. Compilers aren't supposed to stop the developer from being inefficient.
> **Comments:**
> **username_26** (2024-03-23): Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer).  
> **username_9** (2024-03-24): The problem with unreachable code is not that it takes up space in the executable; a good compiler will skip emitting unreachable code. The problem with unreachable code is **the developer probably believes it is reachable** and the developer is wrong! Prohibiting unreachable code isn't an *optimization*, it's a *bug detector*.  
> **username_25** (2024-03-24): Compilers should not throw errors for things that are *probably* wrong, but for things that are *certainly* wrong.  
> **username_0** (2024-03-24): @EvilSnack And yet Java and Typescript are two examples of mainstream languages which give compile-time errors for unreachable code. I'm sure there are languages where your rationale is the one given for making this decision, but you should identify those languages and show that this is why they designed it that way. Otherwise this is just your opinion.  
---
