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