This “unreachable” concept is rather low-level, in LLVM, and I’ve not thought much of it, until seeming you can define it in C++ as a function? Should Julia do the same. And see here C++ vs C difference related to it and loops:
Is your question about behavior of infinite loops or explicit unreachable / UB?
Unreachable can be obtained via llvmcall. You can use this to insert assumptions, a la x > 0 || unreachable() to give the compiler license to assume that x is positive. Related is the llvm builtin assume.
There is an argument to put it into Base – I think that would be nice, but not essential (easy enough to type as llvmcall).
Now you computer will need to check whether x < 0, and if so will need to throw an exception.
But you know that x > 0! So try
x > 0 || error()
sqrt(x)
Ok, the sqrt(x) doesn’t need to check anymore: It can only be reached if x > 0. But the branch cannot be optimized away: In the !(x > 0) case, there will come up an unreachable. But only because it isn’t reached – this function has perfectly well-defined semantics and will longjmp to an exception handler.
On the other hand, consider
x > 0 || unreachable()
sqrt(x)
In this case, there will be no check whether x>0. None at all, the x > 0 || unreachable() has the effect of removing the subsequent check without introducing a new one.
unreachable is literally naked UB – undefined behavior. There are common ways of reasoning about UB:
It is undefined behavior. The compiler can do whatever is convenient.
It cannot happen. That is, the optimizing compiler reasons about your code; it does optimizations that it can prove safe. “prove” is always relative to both logic / rules of deduction, and axioms. Some axioms are stuff like x + (y + z) == (x + y) + z for i64. Another important axiom is that unreachable cannot be reached. Hence, it logically follows that x > 0 (otherwise unreachable would be reached before side-effects), and sqrt doesn’t need to check nonnegativity anymore.
“ex falso quodlibet”, i.e. anything can be derived from a contradiction. If the compiler can also prove that !(x>0) then it follows that your function cannot be called (before side-effects) and any other function that calls your function cannot be called, etc.
I see that this “forward progress guarantee” (or not, for trivial infinite loops) is the much more interesting question, and it’s quite the rabbit hole (I hadn’t really looked at this “unreachable” business beyond a single threaded program):
Since C++ diverged from C practical for infinite loops, and then C11 made the same change, and now C++26 is (partially) changing back (and I suppose C too), an interesting question is, does Julia align more with C++, or C when the semantics differ?
It is not uncommon to hear about C/C++ programming as a shorthand for “C and C++” programming. This implies that C and C++ are similar, but distinct, programming languages with the obvious interpretation being that C++ is a proper superset of C. However, this does not accurately describe the situation.
I think I posted this in Offtopic, since I though people might be interested (in obscure C++26 issues), and casually asked, how Julia compares. I at least think I did, and thus assume someone moved to internals. If nothing needs to be discussed about more changes in Julia, at least consider moving back to Offtopic, or where you think more read than Internals.
The business with infinite loops being undefined is so nuts to me. Yeah, because no one would ever want to write a program that runs until it is externally terminated. Right, servers aren’t a thing! Julia’s support for this is limited only by LLVM’s.
What I could see is that if a loop has no side effects then it might be ok to just skip it, but even that seems a little dicey to me.
It was also about being a trivial loop, loops with side effects were never undefined behaviour. The idea is to allow the language to assume forward progress but it’s a bit of a mess and thankfully they walked it back.