Let’s start with a question! Is this program well-defined?
1 2 3 4 int main () { while ( true ) ; }
If you said yes, you’d be wrong — at least before C++26. A while (true); loop with no side effects used to be undefined behaviour. Compilers were free to assume it terminates, and some — Clang in particular — would optimize it away entirely, with spectacular consequences:
1 2 3 4 5 6 7 8 9 10 11 // https://godbolt.org/z/WYMxxeW1T #include <iostream> int main () { while ( true ) ; } void unreachable () { std :: cout << "Hello world!" << std :: endl ; }
In Clang, this prints “Hello world!”. The compiler removes the infinite loop, main falls through, and the linker-placed unreachable() function executes. This is not a compiler bug — it’s just UB, still better than nasal demons.
Recently, I wrote about how C++26 reduces undefined behaviour, covering changes like erroneous behaviour for uninitialized reads and making incomplete-type deletes ill-formed. I completely forgot about this one. I only realized while preparing for an upcoming CppCon talk on C++26 features — so here it is now.
C++26 fixes this with P2809R3. Trivial infinite loops are now well-defined. The mentioned proposal was also accepted as a defect report, so implementations may apply the fix to earlier C++ modes as well. That is why you might not be able to reproduce the old behaviour on a recent compiler even in C++20 mode.
How did we get here?
The story starts with the forward progress guarantee, introduced in C++11 alongside threading support. The standard says ([intro.progress]) that the implementation may assume any thread will eventually do one of the following: terminate, call a library I/O function, access a volatile glvalue, or perform a synchronization or atomic operation.
A while (true); loop does none of those things. Under the pre-C++26 forward-progress rules, an execution that remains in such a loop forever has undefined behaviour. The optimizer can therefore assume that execution never gets stuck there, which enables transformations that remove the loop and mark the path as unreachable.
... continue reading