logoalt Hacker News

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

161 pointsby ibobevlast Thursday at 8:52 PM244 commentsview on HN

Comments

wahernyesterday at 3:56 PM

> 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.

show 7 replies
JoshTriplettyesterday at 3:49 PM

> 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.

show 9 replies
omoikaneyesterday at 3:56 PM

> 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...

ameliaquiningyesterday at 3:55 PM

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

show 2 replies
peterusyesterday at 3:58 PM

There are valid use cases for the infinite while(1) loop in microcontroller programming (contrary to popular belief it seems). Autogenerated HAL code for the stm32 uses it for error handlers, and they support C++ so I am surprised this was UB.

I only use it for error handling and of course it is a bad idea to use this to wait/stall in power sensitive applications, in that case use wake from interrupt.

As an aside, I like to include a software breakpoint in my error handlers. It makes debugging easier without wasting a hardware breakpoint (which are physically limited by the microcontroller):

  __BKPT();
  while (1)
    ;
show 2 replies
Aardwolfyesterday at 9:18 PM

> 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

Why is that rule needed? I could make my for loop try to solve the halting problem and it'll never finish either, circumventing that rule

show 2 replies
Panzerschrekyesterday at 6:35 PM

I don't see how it can be useful. It's almost always an error to write such a loop. The only reason for it to exist is in very low-level code to do nothing, but for such cases using something like an external function written in assembly is perfectly fine, no C++ standard changes are necessary. It's even makes things harder by complicating the standard with little to no benefits in exchange.

show 2 replies
Suractoday at 7:33 AM

For me everything more than c with classes is too much cognitive load. Templates my by ok for implementing Generics but most of the other changes this comitee has produces are complete waste of brain i think

Aurornisyesterday at 3:11 PM

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

show 2 replies
adzmyesterday at 3:15 PM

i have never before thought that a function could 'fall through' to another function. why does this behavior even exist?

show 4 replies
ErikCorryyesterday at 5:16 PM

> [The C rule was rejected for C++ because it] could inhibit useful optimizations

If be curious if these are the sorts of optimizations I would find useful to the point where I would be happy to pay the price of this annoying new behaviour.

Or are they just the sorts of optimizations that a compiler writer finds useful who is engaged in a multi year career-defining pissing contest with a competing team?

Don't get me wrong, I have myself engaged in a multi year career-defining pissing contest with a competing team. It's fun. But let's not kid ourselves that it's for the users' sake.

show 1 reply
MiroslavPokornyyesterday at 1:41 AM

Breadcrumbs for "blog", "year", "month" etc are broken and give 404s :(

One can browse other blog entries so it really doesnt matter too much.

0x69420yesterday at 6:31 PM

> 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.

brutal. hope major compiler vendors throw in a flag that can bring some sanity to this

paparulo329yesterday at 6:59 PM

The mere concept of undefined behavior is hilarious to me. "Oh this part? No we can't and won't even try figuring out what doing that does, this page intentionally left blank; yes we are a very serious whole ass standards body thanks for asking"

show 1 reply
aabolfazlyesterday at 3:46 PM

Sometimes while (true){} doesn't mean anything clever. It just means the system is broken stay here.

kmarctoday at 7:00 AM

This language/ecosystem is just crippled...

... thankfully. Gives many of us well-paid jobs, and the inexplicable joy of archeology (why certain decisions were made at some point in the nineties, and what buggy implementation a bits header is fixing).

And I'm not even snarky here. I kinda like to do this.

kazinatoryesterday at 6:58 PM

I'd much rather have the compiler diagnose an infinite loop than silently pretend that it's not reachable, or that it can be rewritten to a yield.

In other words, I am mentally well.

show 1 reply
deepsuntoday at 3:49 AM

> How did we get here? ... introduced in C++11 alongside threading support. The standard says 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.

I'm more surprised it passed through the committee, they should've seen that back in 2011. I can not imagine such a bug in spec would pass through a Java committee, as they discuss every little thing for years (sometimes decades). It's not like embedded code is something new.

bitbasheryesterday at 5:50 PM

If an infinite loop can be both:

1. An infinite busy loop.

2. A thread yield/sleep.

It is by definition undefined behavior. You don't know what you're going to get!

show 2 replies
z3ratul163071yesterday at 5:28 PM

c++ reaching new lows

show 1 reply
ahy1yesterday at 7:53 PM

Optimizations are nice and all. But they should not ever be allowed to change the behaviour of the program, from what is expected by reading the code.

There are good uses for infinite loops.

show 1 reply
shmerlyesterday at 4:01 PM

Why does the loop mean halt in that embedded case example?

show 1 reply
glum64yesterday at 5:17 PM

label: goto label;

adzmyesterday at 3:16 PM

as an aside, i've always preferred the zoidberg for (;;) to while(true)

show 1 reply
pianom4nyesterday at 6:16 PM

The abrupt shift to LLM slop halfway through is jarring and disgusting to read.

ratelimitsteveyesterday at 7:22 PM

as the kind of person who has been reading the jargon file for fun since the 90s, I thought I had at least a passing familiarity with a lot of hackish slang from the old days. today i learned about nasal demons as a phrase for undefined behavior. i supposed there's still fossils in the dirt

oleganzayesterday at 3:18 PM

Why is null-terminated C string considered a "billion dollar mistake", but UB isn't?

show 5 replies
BobbyTables2yesterday at 3:15 PM

TLDR: For almost 1/6 of a century, the C++ standards broke the simplest infinite loop and only just recently fixed it.

Idiots!

Don’t they really that people write real programs to solve real problems? This isn’t a theoretical academic exercise!

show 5 replies
semiinfinitelyyesterday at 7:02 PM

cant wait for ai to re-write all of the software we wrote in this dogshit programming language

account42yesterday at 3:33 PM

Unfortunate. There isn't ever a good reason to have an infinite loop so concerned compilers could have just diagnosed this as a warning.

show 5 replies
Dwedityesterday at 7:00 PM

"This is not simply a common pattern on bare metal — it was also undefined behaviour in C++."

Not just X (em dash) but also Y.