C++26 Makes Trivial Infinite Loops Well-Defined, Ending Decades of Undefined Behavior

C++26: Trivial infinite loops are no longer undefined behaviour

Before C++26, a trivial infinite loop like `while (true);` was undefined behavior, allowing compilers to optimize it away. C++26's P2809R3 fixes this by defining such loops as well-defined and replacing their body with `std::this_thread::yield()`. This change, also accepted as a defect report, addresses a long-standing divergence from C and prevents dangerous optimizations in embedded and security-critical code.

In Clang, this prints “Hello world!”. The compiler removes the infinite loop, main falls through, and the linker-placed unreachable() function executes.
  1. JoshTriplett

    > When both conditions are met, the loop body is replaced with a call to std::this_thread::yield().

    Insert screaming here.

    An infinite loop, with no library calls whatsoever, gets a system call inserted. That's a horrible surprise waiting to happen.

    The entire concept of the "forward progress guarantee" is broken. An infinite loop should compile to an infinite loop. Nothing more, nothing less.

  2. wahern

    > When both conditions are met, the loop body is replaced with a call to std::this_thread::yield(). This gives execution of the loop the forward-progress semantics it previously lacked.

    That's the epitome of the hidden code downside that Linus and many others dislike about C++. For constructors and destructors it's somewhat unavoidable and not so random, though Rust does better at limiting the blast radius of non-local code, at least in the drop case.

    If they didn't want to adopt the C11 rule, the C++ committee should've explored a rule that required the compiler to emit a diagnostic or error for trivial loops (whether as defined by C11 or otherwise), requiring the programmer to explicitly insert ::yield or similar. No hidden code, and less opportunity for the compiler to do surprising things.

    The C committee has been rigorously enumerating UB cases in the standard and addressing each case in turn, often by requiring a diagnostic, error, or by turning it into implemention defined behavior. But inserting code like that would be unthinkable.

  3. omoikane

    > The loop must be a trivially empty iteration statement -- meaning its body is literally empty

    This seems to say that the loop body can not be "continue". Indeed, I just tried -std=c++26 with ";" and got an infinite loop as promised, but "continue" restores the undefined behavior:

    - "while(true);" -> https://godbolt.org/z/T65o51crx

    - "while(true) continue;" -> https://godbolt.org/z/Pj9raEcnP

    This is unfortunate since I know of one style guide that prefers "continue" over single semicolons. I guess all those code will be doing "while(true) {}" from now on.

    https://google.github.io/styleguide/cppguide.html#Formatting...

  4. Aurornis

    I never would have guessed that the unreachable() function would get executed in that example. Probably not something you’d encounter in practice, though I have seen some weird things happen with layers of #ifdef

  5. ameliaquining

    The article, most unfortunately, doesn't explain why anyone would want infinite loops to be UB in the first place. I found this explanation: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm

More from this day

2026-09-18