On 2025-12-26 11:44, Jₑₙₛ Gustedt wrote:
Hello Paul,

On Wed, 24 Dec 2025 01:11:01 -0800, Paul Eggert wrote:

However, suppose f is defined this way:

    void
    f (int *b)
    {
      if (*b) // error condition ...
        exit (2); // ... that leads to termination
      *b = 1;
    }

If [[reproducible]] allows this sort of thing, then it's not valid to
do the optimization that I mentioned, because it will cause the
program to exit with status 1 rather than status 2.

Yes, but is this a problem? `f` is clearly not idempotent if `*b`
initially is `0`, so it is not unsequenced.

Why is f not idempotent if there's no requirement that f must return? Is it because f didn't assign to *b the second time through? If so, let's look at a slightly different function:

     void
     g (int *b)
     {
       int b0 = *b;
       *b = 1;
       if (b0) // error condition ...
         exit (2); // ... that leads to termination
     }

As far as I can see, g satisfies C23's definition of "idempotent" (and "effectless", so also "reproducible"), yet obviously a compiler should not optimize away a second call to g.

To fix this, we could clarify what C23 means by the term "the observable state of the execution" (C23 § 6.7.13.8.1 ¶ 8), a term that is nowhere defined in the standard. And all the clarifications I can think of will state clearly that a reproducible function must reproduce not only observable store operations, but also observable forms of not-returning, because exiting or otherwise failing to return also changes "the observable state of the execution".

GNU C finesses this by requiring that these functions must return exactly once. Although poorly documented, it's a simple rule. C23 could follow GNU C's lead, or it could come up with something more complicated that separately covers all the forms of not-returning. I hope C23 keeps things simple, though, as hardly any C programmer understands this stuff.


Allowing termination to happen under certain inputs would not mean
that the other part of the definition of the term can be ignored, no?
Such a function that terminates could not be idempotent if it
changes the pointed to argument to a state that is invalid for a
second call.

Yes, for the ordinary English-language meaning of "idempotent". However, I don't see how that meaning is captured by C23's own definition of "idempotent".

Reply via email to