C++ Logo

std-discussion

Advanced search

Re: Is forward progress guarantee still useful?

From: David Brown <david.brown_at_[hidden]>
Date: Sat, 20 Sep 2025 16:52:47 +0200
On 20/09/2025 02:45, Thiago Macieira via Std-Discussion wrote:
> On Friday, 19 September 2025 00:02:00 Pacific Daylight Time David Brown via
> Std-Discussion wrote:
>> I expect the compiler did exactly as you suggest -
>> replace the loop with its internal representation for UB, exactly as it
>> would if you wrote "__builtin_unreachable();". I am simply suggesting
>> that in a situation like this, a clear halt is a bit more developer
>> friendly.
>
> The problem is reading the developer's mind. That's why we have a Standard: so
> the developer can tell the compiler what they intended.
>

Sure. And again - there is nothing wrong with what the compiler did,
based on the standard.

But equally, there is nothing wrong with a compiler providing
implementation-specific (documented or undocumented) semantics to
particular cases of UB. A good compiler does not just try to give the
most efficient object code for source code with defined behaviour, but
also tries to help developers find their mistakes - a compiler is a
development tool, not just a translator.

I do realise there are often situations where it is difficult for the
compiler to do this. Identifying UB in the code and warning the user at
compile time or providing helpful semantics (like a halt with an error
message) at runtime can range from trivial, through costly in terms of
runtime speed and size, to downright impossible.


> Suppose you have:
> void will_exit();
> bool f(int i)
> {
> if (i)
> return false;
> will_exit();
> }
>
> That will_exit() function comes from some third-party library and isn't marked
> [[noreturn]]. We know it won't return, so we don't want the compiler to
> generate unnecessary code. So the author of this code needs to insert a
> __builtin_unreachabble() to suppress the "control reaches end of non-void
> function" warning. But note how there's no difference in code generation: the
> __builtin simply suppresses the warning.

There can be a difference in code generation - since the compiler knows
that a "__builtin_unreachable();" after the "will_exit();" call can't be
reached, it can simplify the generation of "f" - there is no need for a
function epilogue, and that may also mean a simplified prologue.

>
> That's no different from any other UB: the compiler can't tell the the UB could
> happen and it should insert a halt. If you want that, you should compile with
> UBSan.
>
>

Certainly if you want to be sure that you get a halt for given bit of UB
code, then you need to tell the compiler you want that, and are willing
to pay for it in code size and speed if necessary. That can be with
compiler flags (like sanitisers) or with "__builtin_trap();" (I see it
in gcc - I expect clang has it too), or other implementation-specific
features. (There will also be std::breakpoint() in C++26, which might
be appropriate. But I don't know what that is supposed to do if you are
not running with a debugger.)

So I am fully in agreement that if you want a particular kind of defined
behaviour, don't write UB and expect the compiler to read your mind.
But I still think that sometimes it is possible for a compiler to make a
guess at the developer's mind!

Received on 2025-09-20 14:52:56