2025-01-25
integration into IS ISO/IEC 9899:202y
document number | date | comment |
---|---|---|
n3424 | 202412 | Original proposal |
n3456 | 202501 | This paper |
- emphasize that the special functions are excluded because their control flow is considered to be exceptional | ||
- briefly discuss other cases where the gcc and C23 features disagree |
CC BY, see https://creativecommons.org/licenses/by/4.0
The current wording in 6.7.13.8.1 has lead to misunderstandings about
the status of function calls with respect to the [[reproducible]]
and [[unsequenced]]
attributes for the case that such an attributed function does not
return. It seems that the corresponding gcc attributes __attribute__((pure))
and __attribute__((const))
assumed that such calls always return, without clearly documenting that
expectation, nor by documenting its reach.
Clearly, such an expectation makes sense, since otherwise a side effect could be inhibited by a call that does not return. Thus several optimizations that are the goal of these attributes would not be valid.
Currently this misunderstanding has the effect that gcc distinguishes the C23 attributes and their gcc predecessors even for the case where no pointer parameters or pointer return values are involved. The goal of this paper is to revert this unfortunate incident and establish the C23 attributes as having identical semantics as the gcc attributes for this case.
During the discussion on this paper it also became clear, that the
gcc and C23 attributes deviate on pointer parameters and return values,
namely on the fact if and when pointer targets are modifiable. Where C23
follows the model for restrict
that is
already present in the standard, the gcc attributes
const
-qualifiedconst
-qualified
target of a returned pointer of a function with __attribute__((const))
is immutable.If WG14 would have been aware of that assumed semantic when specifying the C23 attributes the result might have been different; unfortunately it now seems too late to make any semantic changes that would bring those features closer together with respect to pointers. In any case, it is not the goal of this paper here to deal with these questions.
The most important cases for non returning functions are already
covered by the current wording. In particular, calling functions that
implicitly change program state by closing streams (exit
and quick_exit
), by changing callback state
(exit
, quick_exit
and thread_exit
) are already prohibited. The aim
of this paper is to clarify this situation and to apply this rule more
widely, namely to prohibit termination of the execution whenever a side
effect may reasonably be expected during program termination.
Nevertheless, several cases of non-returning functions are not yet covered:
longjmp
.abort
, _Exit
).assert
invocation triggers.All these cases concern situations that are usually not taken into
account by optimizers. In particular, the resulting status of the
execution is specifically determined by wording for these features. Thus
not allowing the use of these features could change the behavior of a
function call from defined (whatever is defined for abort
, for example) to undefined. We don’t
think that the introduction of undefined behavior in that way is
reasonable.
New text is underlined green, removed text is
stroke-out red.
Add at the end of 6.7.13.8.1, p4, that talks about the effects that are considered for the attributes:
Similarly, a function call expression E that does not return to its caller is considered to have a visible effect on the execution state. This not withstandingresult in exceptional control flow and are therefore not considered for the effects they produce, here. It is unspecified if the observable side effects of a function call that encounters one of the events above depends on the function type having the
- an invocation of
longjmp
that bypasses E,- an invocation of
abort
or_Exit
,FNT1)- an invocation of
assert
,FNT2) and- the generation of a signal, interrupt or trap that terminates the execution,
[[reproducible]]
or[[unsequenced]]
attribute or not.
FNT1) Note that in particular the functionsexit
,quick_exit
andthrd_exit
have visible side effects because they close streams and modify the states ofatexit
handlers,at_quick_exit
handlers andtss_t
destructors, respectively.
FNT2) Here, in case the asserted expression evaluates to
false, even the diagnostic output that the assert
macro produces is not considered as
an effect.
Modify p7:
A store operation to an object X that is sequenced during a function call such that both synchronize is said to be observable if X is not local to the call, if the lifetime of X ends after the call, if the stored value is different from the value observed by the call, if any, and if it is the last value written before the termination of the call. An evaluation of a function callFNT0) is effectless if any
storeoperation with side effect that is sequenced during the call is the modification of an object X that synchronizes with the call; if additionally the operation is observable, there shall be a unique pointer parameter P of the function such that any access to X shall be to an lvalue that is based on P. A function pointer value f is effectless if any evaluation of a function call that calls f is effectless. A function definition is effectless if the derived function pointer value is effectless.
Add <assert.h>
,
<setjmp.h>
and <stdlib.h>
to the Forward references.
Thanks to Paul Eggert, Bruno Haible, Jan Hubicka and Joseph Myers for discussion and feedback.