Clarify status of non-returning functions with respect to function attributes

Jens Gustedt, INRIA and ICube, France

2025-02-16

target

integration into IS ISO/IEC 9899:202y

document history

document number date comment
n3424 202412 Original proposal
n3456 202501 - 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
n3494 202502 - unspecified occurence of effects
- add a footnote to show the unspecified occurence

license

CC BY, see https://creativecommons.org/licenses/by/4.0

1 Motivation

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

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.

2 Approach

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:

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.

3 Suggested changes to the wording.

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 notwithstanding result in exceptional control flow and are therefore not considered for the effects they produce, here. If a call to a function with the [[reproducible]] or [[unsequenced]] attribute would, in the absence of the attribute, result in one of the events above, then it is unspecified whether and when the event occurs.FNT3)
FNT1) In particular, each of the functions exit, quick_exit and thrd_exit have visible side effects because they close streams and modify the corresponding state that holds the active atexit handlers, at_quick_exit handlers or tss_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.
FNT3) In particular, if calls to attributed functions, in compliance with the spefications in this clause, are moved or even completely omitted by the translator, the produced side effects may be different or may appear in a different order than in the absence of the attribute. For example an assertion that triggers from the function without the attribute may produce output in a different order than for the function with the attribute, or the output and final call to abort may not happen at all.

Add <assert.h>, <setjmp.h> and <stdlib.h> to the Forward references.

Aknowledgements

Thanks to Paul Eggert, Bruno Haible, Jan Hubicka and Joseph Myers for discussion and feedback.