Niall Douglas

Jens Gustedt

Function failure annotation

We have been seeing an evolution in proposals for the best syntax for describing how to mark how a function fails, such that compilers and linkers can much better optimise function calls than at present. These papers were spurred by [WG21 P0709] Zero-overhead deterministic exceptions, which proposes deterministic exception handling for C++, whose implementation could be very compatible with C code.

P0709 resulted in [WG21 P1095] Zero overhead deterministic failure -A unified mechanism for C and C++ which proposed a C syntax and exception-like mechanism for supporting C++ deterministic exceptions.

That was responded to by [WG14 N2361] Out-of-band bit for exceptional return and errno replacement which proposed an even wider reform, whereby the most popular idioms for function failure in C code would each gain direct calling convention support in the compiler.

This paper synthesises all of the proposals above into common proposal, and does not duplicate some of the detail in the above papers for brevity and clarity. It hews closely to what [WG14 N2361] proposes, but with different syntax, and in addition it shows how the corresponding C++ features can be constructed on top of it, without loosing call compatibility with C.

-mech_ub Invalid input leads to undefined behavior. E.g many character classification macros such as isalpha are undefined if the input is not either a valid character or EOF.

-mech_sentinel

These return a failure exclusively by returning a sentinel value (e.g. -1, NaN, EOF or a null pointer), or a range of sentinel values (e.g. < 0). Many of the C standard library functions work like this, the simplest example being getc which returns a value EOF unequal to any character value in case of failure.

-mech_sentinel+errno

These indicate a failure via returning a sentinel value as above, and additionally store the cause of the failure in a thread-local state such as errno, or fegetexceptflag(). In a variant of this, mech_errno+sentinel, say, the sentinel value (such as INT_MAX) only indicates a potential failure, and errno has to be checked to learn the failure state. -mech_errno

These return a failure exclusively by writing to a thread-local state e.g. errno not being modified means function was successful (i.e. to check for failure, the caller must set errno to zero before the call). Any function return value may be a valid value, or garbage.

In this context the picture is complicated by POSIX (ISO 9945) because in many cases it changes the category from the one foreseen by C (ISO-9899). E.g C has the malloc function in category mech_sentinel, whereas POSIX sets error codes into errno, and thus changes the category to mech_sentinel+errno.

Failure modes mech_ub, mech_sentinel+errno and mech_errno are difficult to handle and to optimize, and would nowadays probably have been designed differently. E.g modifying thread local data, which appears as-if modifying a global variable to the C abstract machine in terms of side effects, prevents function declarations from being marked as pure by the developer and generally complicates code generation. This prevents much optimisation which would otherwise be possible, particularly pessimising small functions like the C math functions which in standard C configuration must spend more time in function ceremony than implementing the mathematical operation itself.

N2361 emphasises a lot on improving this situation of C legacy interfaces to provide them with three alternative calling mechanisms on one side, and by providing three alternative return specifications for failure on the other. Each alternative return specification for each function would be an alternative implementation edition of that function. Each implementation edition is proposed to use a fixed offset from a linker symbol, but other implementations are possible, and we will not focus on this aspect here.

-N2361's oob_direct

Caller expects legacy behaviour i.e. set thread-local state or similar or have even undefined behavior in case of failure. This is the default calling mechanism that is chosen when the function is called directly. -N2361's oob_ignore Caller ignores failure, so any failure-related code in the callee is wasted CPU cycles, and that part of that code could be optimized out. receiving such a failure bit, the return value of the function can be interpreted differently and e.g be used to transport additional failure information. Other path of failure communication (errno, fesetexcept(), ...) are cut off and are guaranteed not to be changed by this call mechanism.

For legacy interfaces that use failure mechanisms mech_sentinel+errno or mech_errno, oob_capture means that a caller has to be able to handle the fact that the thread-local state is not being modified, and that the error value is returned directly by hijacking the function return value, instead.

As previously mentioned, N2361 also provides alternative return mechanisms for the functions such that they may specify failure. The most important of these for the following will be oob_return which simply indicates 'failure' of the execution and that provides the possibility to return additional failure information through the specified return value. Two other failure modes are provided, oob_return_never and oob_return_error. These are mainly thought to provide means to amend legacy interfaces that follow one of the failure conventions above.

Under N2361, each function declaration would be annotated with attributes to indicate that it can be called with any of the additional calling mechanisms -if it is not annotated at all it is a legacy function and calling it with the new modes would not be defined. These annotations enable compile-time rather than link-time diagnostics. Also they improve optimisation of calls, because now the caller may know if a function could set errno eventually or not.

The caller must always explicitly opt-in to calling a non-legacy edition by wrapping the function call with markup to indicate that an optimised calling mechanism is requested. If code tries to call a function compiled by a legacy compiler other than directly (oob_direct), where the function declaration is incorrectly attributed, a link error would always result.

The additional calling mechanisms are designed such they can be used with identifiers of such annotated functions or by using conventional function pointers to these functions.

Differences between WG14 N2361 and WG21 P1095

[WG21 P1095] Zero overhead deterministic failure -A unified mechanism for C and C++ spent much of its time describing how existing errno-based C code could be mechanistically transformed by the compiler into code which no longer used errno under the bonnet, but which otherwise did not need to be rewritten apart from opting-in a function definition. This was intended to aid existing code to be recompiled and automatically 'just work' in the majority of cases, with diagnostics where code would need to be modified. WG14 did not much care for this approach, as it is felt that C compilers ought to not rewrite the programmer's code to such an extent, even if mechanistically.

N2361 simply requires that the programmer must rewrite their code, particularly in the implementation of a function, but also at every function call site if one does not want legacy behaviour.

The broader sweep of N2361 of tackling the entire standard C library at once is a useful innovation. One can get onboard the proposal that function implementations must be reimplemented to support more efficient calling conventions, as that would be a once-off investment of effort by the library implementer.

What is unhelpful in N2361 is the requirement for code calling standard library functions to have to wrap every call site with markup to specifically opt into the more efficient calling convention.

-Firstly, that is tedious, but it is also visually fussy, ruining the clarity and succinctness of C prose.

N2429:4

Niall Douglas and Jens Gustedt -Secondly, one specifically want existing code to automatically be more efficient, where the new behaviour has identical visible side effects to the current behaviour, when recompiled with new compilers and new standard libraries. -Thirdly, how N2361 would interoperate with C++ deterministic exceptions is not obvious, and a slightly different formulation of N2361 would solve that problem.

PROPOSED DESIGN

Returning to the sample C standard library function from WG21 P1095R0 which we shall permute to demonstrate the proposal at work:

int myabs ( int x ) { if ( x == INT_MIN ) { errno = ERANGE ; return INT_MIN ; } return ( x < 0) ? -x : x ; }
In the following we will show some 'editions' of this function that will highlight several aspects of our proposal. Whether or not these 'editions' may be specified directly (as the following example suggests) or if they will only be deduced automatically from the supplementary information that is provided to the compiler, will only be decided in the future depending to the feedback from the committees and the wider public. They are mainly used here to communicate and clarify the optimisation potential of the approach.

These functions are annotated with a particular 'qualifier' such as :ignore_errno, that is voluntarily chosen to be syntactically different from any C or C++ syntax. Also, for illustration a gcc attribute [[gnu::const]] is used to indicate that a given function is considered to be referentially transparent, but other optimization attributes could equally be used.

For the header file, if we wish to declare to the public that myabs() supports more efficient failure handling, we would declare our myabs() implementations to external code as follows: // errno setting edition . // Linkage is " myabs " as well as " myabs : raise_errno " , as : raise_errno is // default edition ( see below ) extern int myabs : raise_errno ( int x ) ;

# define oob_errno_combine_decl ( T ) struct result_with_errno_ ## T \ { \ T return_value ; \ int errcode ; \ } # define oob_errno_combine ( T ) struct result_with_errno_ ## T //
The following implementations then follow: It should be stressed that this will ever only be an opt-in facility for the library implementer. Here, it gives us the opportunity to talk about the different editions of the function. Nobody is suggesting that any of the above is required for any given function implementation.

# include < errno .h > # include < limits .h > // Input
Note also that whilst a library implementer could implement all of the above manually by hand, later in this paper we propose convenience markup to avoid writing so much tedious boilerplate.

errno-related function failure editions

The ignore_*, struct_*, union_*, signal_* and raise_* edition qualifiers have a pattern to them, though the exact semantics are hardcoded to each edition qualifier. For *_errno:

-ignore_errno
No setting nor checking of errno will be performed by the caller. Therefore, any code related to the use of errno to return failure ought to be elided in this edition.

-struct_errno This edition will return a sentinel value and what errno would be set to in a product type (i.e. a structure), or the successful value with the error code set to zero. errno's value on input is never read.

-union_errno This edition will return either the successful value, or what errno would be set to in a sum type (i.e. a union), along with an efficient discriminant to indicate which is in force. In order to preserve the type system perfectly in function pointers, rather than returning N2429:6 Niall Douglas and Jens Gustedt a union, such functions return a symbolic type oob_union so the compiler knows that an efficient discriminant such as the CPU's carry flag, or spare CPU register, was used.

As none of the C standard library functions ever return either a value or errno, none would implement a union_errno edition. This would not prevent other functions in user libraries doing so, however.

-signal_errno Invalid inputs, or invalid input state, will cause the function to never return e.g. a signal may be raised instead, or the program may crash. This means that if the function returns, it was successful. This differs from ignore_errno because implementations will probably still detect failure, but is similar in that callers will never check for failure.

If the reader is familiar with C++'s noexcept, this is the exact same facility: no C++ exception throws ever emit from a noexcept function, because std::terminate() would be called instead.

-raise_errno [default] This edition sets errno if failure occurs. For the family of *_errno editions, raise_errno is defined as the default. That means that the raise_errno edition of the function is emitted into linkage as the unqualified edition i.e. this is the edition of the function which unrecompiled code links to, and if you take the address of the unadorned function, this is the function pointer type you get.

Note that by defining any *_errno edition of a function, you guarantee that that function never changes its behaviour based on reads of errno, and that the other editions have the same caller-visible side effects as the default edition, except for side effects upon errno.

There are many more proposed hardcoded ignore_*, struct_*, union_*, signal_* and raise_* edition qualifiers for other than errno, which we will cover later.

When to choose which edition

Before we get onto those however, the next part is how compilers ought to interpret the availability of multiple editions of functions. Such automatic optimisation opportunities had not been developed previously in N2361. We show such opportunities for errno-setting functions with :struct_errno editions available. Consider a sequence of code like the following where a loop of an errno-setting function accumulates failure: Not setting a thread local errno state every loop iteration is much more efficient, plus myabs:struct_errno() is const pure, and thus calls of it can be more aggressively elided and/or parallelised via SIMD. The optimisation is safe, because the mere existence of * _errno editions for a function guarantee that replacing the default edition with a non-default edition does not have unknown side effects.

Similarly, consider a sequence of code like the following: Here, an optimiser can spot that setting errno has no visible side effects upon the caller, thus yielding as-if code: One can easily see the optimisation opportunities if the compiler can hard-assume that an unchecked errno-setting function call will never set errno for a given translation unit:1 it can optimise out failure handling, remove branches, improve CPU pipelining and vectorize.

int out [ length ] ,

Function pointers

If one were to choose an implementation where each function implementation edition has separate linkage, then each edition of a function would be an ordinary C function. It would be callable directly by specifying its edition, the address can be taken, and so on: As one can see, the function pointer type system would be unmodified from the existing system.

What happens if somebody supplies an ignore edition to a function pointer whose caller expects an errno setting edition? This would be possible without casting as the pointer types are compatible. The answer is misoperation -don't do that.

Math function edition qualifiers

Of particular concern to this proposal is to improve the performance of the C math functions in standards conforming C code. WG21 is especially acutely keen to fix this, as standard C++ is impossible on GPUs due to the use of thread-local data, which GPUs do not have.

The following math function editions are such they add to the previous *_errno by also considering the floating point exception flags and environment, as they are defined in the <fenv.h> header. The following qualifiers are proposed:

-ignore_feexcept Assume that the caller immediately called feholdexcept() and saved errno just prior to calling this function, and will call fesetenv() and restore errno immediately after this function returns. The edition can therefore hard-assume that any floating-point failure will be ignored, including that errno will not be checked by the caller.

-struct_feexcept A structure compatible with the following layout would be returned: { T return_value ; // The return value type e . g . float , double , long double , etc int feexceptcode ; } This edition will return either the successful value in return_value and 0 in feeexceptcode, or return a sentinel value in return_value and what feraiseexcept () would have been called with to raise the floating-point exception. errno's value on input is never read, and errno's value is never written.

-union_feexcept

Unavailable as is unneeded in the C standard library. -signal_feexcept

Floating-point failure is handled by raising SIGFPE, and thus the current thread of execution must be aborted. The caller can assume that failure will never occur.

-raise_feexcept [default] This edition calls feraiseexcept() and/or sets errno if failure occurs as per the current rules for math_errhandling.

CONVENIENCE SYNTAX

Generating and maintaining different 'editions' of the same function is tedious and error prone; providing only the opportunity for handwritten optimisations as presented above would severely restrict the acceptability of this technique. Therefore, we propose convenience Function failure annotation N2429:9 syntax that will probably the more commonly used interfaces, if not the only ones that survive the scrutiny of the standardisation process.

Function declaration attributes

The different versions of myabs can basically be seen as mechanical rewriting of the same code. This may be due to the simplicity of the base function in question, or it could be a general property of the error convention using errno and the floating exception flags. In fact, a first inspection of a concrete implementation of the C library (musl libc) has shown that the error handling is concentrated in some easily identifiable error paths, and that an annotation of these can be done mostly automatically.

So, in order to save on spelling out function editions by hand, it is proposed that function attributes will trigger the automatic generation of the editions by the compiler, similar to N2361's proposed syntax. The earlier myabs() function could have the compiler autogenerate the *_errno editions simply by placing an [[oob_return*]] attribute before its declaration, and using a family of oob_*() features to save on writing much tedious boilerplate:

[[ oob_return_errno ( nondefault : gnu :: const ) ]] int myabs ( int x ) { if ( x == INT_MIN ) { oob_return_errno ( ERANGE , INT_MIN ) ; } return ( x < 0) ? -x : x ; }
Here, we assume a new feature oob_return_errno(...) (extending N2361, see below) which applies the attributes specified by ... to a subset of the editions, depending on the rule specified. This allows one to announce that:

-the function is referentially transparent, provided no error condition occurs, -in case of such an error condition only the default version would use errno to transmit the failure state, -the function implementation solely uses the oob_return_errno feature to indicate failure, and, -that the non-default editions receive the attribute [[gnu::const]].

In the header file, the declaration also becomes similarly succinct:

[[ oob_return_errno ( nondefault : gnu :: const ) ]] int myabs ( int x ) ;
With such simple annotations, different editions as proposed in the previous section can be automatically generated and automatically used. In the terminology of N2361 the automatically rewritten optimisation of the first loop example of the previous section can be as simple as the following: Such a rewrite then would behave as-if it were the original code. On the other hand, this rewritten code can be optimized much more efficiently because the loop does not contain any conditional and because the access to the thread local state errno can completely be avoided.

In view of the different editions that we have introduced above, we propose to differentiate the different error return features from N2361. In particular, following the above terminology the oob_return_error convention would better be spelled oob_return_feexcept, and we add a convention oob_return_errno that guarantees that default failure paths exclusively use errno.

WG21 P0709 Deterministic C++ Exceptions

As this is a WG14 focused paper, this section will be extremely brief, but it is worth demonstrating how C code could safely and usefully call many more C++ functions in the future than at present.

Assuming that [WG21 P1028] SG14 status_code and standard error object for P0709 Zero-overhead deterministic exceptions becomes the chosen std::error for lightweight exceptions, implementing C support for calling C++ functions which throw lightweight exceptions is straightforward.

Under the [WG21 P1095] formulation of P0709, deterministic exception throwing functions can locally throw a to-std::error convertible custom type more appropriate to a local use case. Such custom local type throws must be some custom std::status_code<DomainType>, which can implicitly decay to std::error upon demand. Under P1095, status codes know how to throw themselves as a non-deterministic exception upon request.

As std::error is also a std::status_code<erased<T>>, deterministic exception throwing functions would always throw some status_code. Hence we can hard code C++ function edition semantics around those of status_code:

-ignore_status(erased<E>/DomainType)

The status code will never be checked by calling code, and thus any code related to checking whether to return a status code can be skipped in the edition.

-struct_status(erased<E>/DomainType) This edition will return a sentinel value and what the status code would be in a product type (i.e. a structure), or the successful value with the status code set to empty.

There is currently no proposed use case for this edition in the core C++ language. However, there are a number of places in the C++ standard library where the ability to return a value with informational status would be extremely useful.

-union_status(erased<E>/DomainType) This function implementation edition is selected by the C++ language when a C++ throws function calls another C++ throws function i.e. a deterministic exceptions enabled function is calling another deterministic exception enabled function.

This edition will return either the successful value, or what the status would be in a sum type (i.e. a union), along with an efficient discriminant to indicate which is in force.

This edition is always generated by C++ throws(E) or throws functions i.e. deterministic exception throwing C++ functions. As mentioned above, pointers to functions with this edition would return a oob_union type, which indicates an ABI-efficient means of returning a discriminant (but see subsection below on function pointers).

-signal_status(erased<E>/DomainType) This function implementation edition is selected when a noexcept function calls a C++ throws function, and does not trap nor check the deterministic exception thrown. This edition never returns failure i.e. is a noexcept C++ function. If it experiences failure, it calls std::terminate(), as all noexcept C++ functions do.

Function failure annotation N2429:11

Note that this is an optimisation: the compiler is effectively hoisting the potential call to std::terminate() out of the noexcept function, and into the source of the uncaught exception throw. This would mean that destructors within the noexcept function, or any destructors between the leafmost throwing function and the noexcept function, would not fire before std::terminate() is called. However, it also means that codegen for noexcept functions dramatically improves, and it also solves a big problem with value-based exceptions in that the cause of why std::terminate() was invoked gets lost much easier than with type-based exceptions. By invoking std::terminate() at the very leafmost function, instead of at the noexcept base of the call tree, it localises the cause.

-raise_status(DomainType)

This function implementation edition is selected when an exception type throwing function calls a throws function i.e. when a legacy exception throwing function calls a deterministic exception throwing function.

Upon failure, this edition raises a non-deterministic type-based exception throw by calling the status code's .throw_exception() facility to cause the status code to be converted into a legacy exception throw.

Depending on the C implementation, throwing type-based exceptions through C code may be unwise. Portable C code should not call this function implementation edition.

C++ function pointers

Something important to note is that in this proposal, deterministic exception value throwing functions do not have a compatible function pointer type with type-based exception throwing functions i.e. But it also solves another desire: the C++ style function pointer is auto-propagating i.e. when calling that function pointer, C++ will automatically insert a check for failure after the function call returns, and will propagate any failure up the call tree. In contrast, the C style function pointer does not propagate -like in C, calling it gets you a returned value of oob_union type, and it is on the caller to inspect that value and do something with it.

This proposed facility raises some very interesting possibilities for header files which contains function declarations which auto-propagate failure when used from C++, and return oob_union when used from C code. This facility is not explored further in this paper.

  Differences between WG14 N2361 and WG21 P1095 . . . . . . . . . . . . . . . 3 2 Proposed Design 4 2.1 errno-related function failure editions . . . . . . . . . . . . . . . . . . . . . . . 5 2.2 When to choose which edition . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.3 Function pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4 Math function edition qualifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 3 Convenience syntax 8 3.1 Function declaration attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 3.2 WG21 P0709 Deterministic C++ Exceptions . . . . . . . . . . . . . . . . . . . . 10 3.2.1 C compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.2.2 C++ function pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1. INTRODUCTION [WG14 N2361] Out-of-band bit for exceptional return and errno replacement describes different function failure mechanisms in the standard C library:

  Input is not checked edition . [[ gnu :: const ]] extern int myabs : ignore_errno ( int x ) ; // Product type return edition . [[ gnu :: const ]] extern oob_errno_combine ( int ) myabs : struct_errno ( int x ) ; // Invalid input is UB edition . [[ gnu :: const ]] extern int myabs : signal_error ( int x ) ;

  int out [ length ] , in [ length ]; errno = 0; // reset errno to zero for ( size_t n = 0; n < length ; n ++) { out [ n ] = myabs ( in [ n ]) ; } // If any one of the above myabs () functions failed ... if (0 != errno ) { abort () ; } An optimiser can spot that the setting of real errno can be hoisted outside of the loop, thus yielding as-if code: int out [ length ] , in [ length ]; int errcode = 0; // NOT errno for ( size_t n = 0; n < length ; n ++) { oob_errno_combine ( int ) ret = myabs : struct_errno ( in [ n ]) ; if ( ret . errcode != 0) { out [ n ] = ret . return_value ; } errcode |= ret . errcode ; } if (0 != errcode ) { Function failure annotation N2429:7 abort () ; // never returns } errno = 0; // must be zero henceforth

  int out [ length ] , in [ length ]; for ( size_t n = 0; n < length ; n ++) { out [ n ] = myabs : ignore_errno ( in [ n ]) ; } If however the programmer did not save and restore errno and simply does not check it at all: int out [ length ] , in [ length ]; for ( size_t n = 0; n < length ; n ++) { out [ n ] = myabs ( in [ n ]) ; } // errno is not checked and the compiler has been configured with some option like -funchecked-errno-functions -always-succeed, then the optimiser might generate: int out [ length ] , in [ length ]; for ( size_t n = 0; n < length ; n ++) { out [ n ] = myabs : signal_errno ( in [ n ]) ; // failure not possible }

  int (* ignore_myabs ) ( int ) = & myabs : ignore_errno ; oob_errno_combine ( int ) (* struct_myabs ) ( int ) = & myabs : struct_errno ; N2429:8 Niall Douglas and Jens Gustedt int (* signal_myabs ) ( int ) = & myabs : signal_error ; int (* raise_myabs ) ( int ) = & myabs : raise_errno ; int (* default_myabs ) ( int ) = & myabs ; assert ( raise_myabs == default_myabs ) ; // true

  int out [ length ] , in [ length ]; int errcode = 0; // NOT errno for ( size_t n = 0; n < length ; n ++) { bool flag ; out [ n ] = oob_capture (& flag , myabs , in [ n ]) ; errcode |= flag ; } if (0 != errcode ) { abort () ; // never returns } // errno has never been touched N2429:10 Niall Douglas and Jens Gustedt

  3.2.1. C compatibilityC++ status codes with a C-compatible payload are guaranteed to be C-compatible, and, from the perspective of C, they have a layout of:struct status_code_ ## DomainPayloadType { void * domain ;// pointer to status_code_domain instance describing T DomainPayloadType payload ; // An intptr_t for std :: error };So, C++ functions look just like C functions with editions returning a status code as their failure code, and for struct_status(erased<E>/DomainType), union_status(erased< E>/DomainType) and signal_status(erased<E>/DomainType), C code can directly call C++ functions, and understand their failures, without intermediate wrappers to prevent nondeterministic type-based exception throws entering C code.

  template < class T > T make () throws ; template < class T > static T (* make_function_ptr ) () = & make <T >; // FAILS , incompatible pointer types Instead one must either spell out the function pointer type, or annotate the function pointer type with throws: // C ++ style template < class T > static T (* make_function_ptr1 ) () throws = & make <T >; // works // C style template < class T > N2429:12 Niall Douglas and Jens Gustedt static oob_union (T , std :: error ) (* make_function_ptr2 ) () = & make <T >; // worksThis solves a concern raised in the Cologne EWG discussion of P0709 about mixing pointer types of deterministic exception throwing functions with other kinds of exception throwing functions.

  is not checked edition

	Function failure annotation	N2429:5
	[[ gnu :: const ]] int myabs : ignore_errno ( int x ) {
	// maybe undefined if x is INT_MIN
	return ( x < 0) ? -x : x ;
	}
	// Return what errno would be set to put into a product type .
	[[ gnu :: const ]] oob_errno_combine ( int ) myabs : struct_errno ( int x ) {
	// expected to be false , could be annotated and optimized
	if ( x == INT_MIN ) {
	return ( oob_errno_combine ( int ) ) { INT_MIN , ERANGE }; // sentinel value + errno
	}
	return ( oob_errno_combine ( int ) ) { myabs : ignore_errno ( x ) , 0};
	}
	// Invalid inputs cause UB due to precondition violation .
	[[ gnu :: const ]] int myabs : signal_error ( int x ) {
	oob_errno_combine ( int ) ret = myabs : struct_errno ( x ) ;
	// expected to be false , could be annotated and optimized
	if ( ret . errcode != 0) {
	abort () ;
	}
	return ret . return_value ;
	}
	// Set errno if failed . Not pure . Default edition .

int myabs : raise_errno ( int x ) { oob_errno_combine ( int ) ret = myabs : struct_errno ( x ) ; // expected to be false , could be annotated and optimized if ( ret . errcode != 0) { errno = ret . errcode ; } return ret . return_value ; }

It would probably be unwise to enable such a compiler setting for an entire program.

ACKNOWLEDGEMENTS

Our thanks to the numerous people who have contributed feedback on lightweight exceptions. Your numbers are so numerous now that listing you all would be distracting from this paper's goal to be a short, combined proposal for consideration by the standards committees. Nevertheless, thanks are due to each and every one of you.


Lars Gullik Bjønnes

Jens Gustedt

Remove support for function definitions with identifier lists proposal for C2x

Function definitions with identifier lists (often referred to as K&R functions) are deprecated for a long time. Because they are now clearly phased out everywhere and because function declarations and definitions that provide a prototypes are clearly superior we propose to remove this feature for good.

INTRODUCTION

The original syntax for definitions with identifier lists and a subsequent declarator list has the disadvantage that it does not provide a prototype of the defined function, and that the function needs a special convention (called default argument promotion) that leaves the verification of type correspondance between caller and callee to the programmer. As an example take the following declaration and definition: // header declaration , not a prototype long maxl () ; // identifier list definition , not a prototype long maxl (a , b ) long a , b ; { return a > b ? a : b ; } Here a caller of maxl would have to ensure that for any call the arguments are of type compatible with long, automatic conversion to long for narrower types such as int is not provided. In addition, this syntax has caveats when used with parameters that are narrower than int, since such arguments undergo default argument promotion. Narrow integers are (usually) converted to int, and float to double.

// header declaration , not a prototype , valid short maxs () ; // prototype declaration , not compatible , invalid short maxs ( short a , short b ) ; // prototype declaration , compatible , valid short maxs ( int a , int b ) ; // identifier list definition , undergoing default argument promotion short maxs (a , b ) short a , b ; { return a > b ? a : b ; } This function is always called with int arguments. But because these int arguments then are immediately converted to short by the function, there is the additional tacit assumption that the values of these arguments fit into short. So calling such a function with value that exceeds SHRT_MAX, e.g, cannot be be caught by the caller. The modern, prototyped form does not have these drawbacks. The following declaration and definition ensure that the function always receives correctly converted parameters: Therefore, the syntax of function definitions with identifier lists has been deprecated for a long time, and compilers such as gcc and clang issue warnings if they encounter such a construct, or even need specific compiler options to accept it. There are other issues within the field of function syntax, namely -declarations (that are not definitions) may omit parameter information completely, and, -in contrast to pure declarations, definitions that provide a prototype, may not omit the parameter name, even if the parameter is not used in the function body.

For the latter, there is already paper N2381, proposing to remove that restriction. Though it seems that the underlying problem has similar roots as the issues at hand, that proposal is orthogonal to ours and we will not need to handle the issue, here.

The first, the property to provide declarations without prototypes, has also been marked obsolescent in the C standard, but still is relatively widely used. In particular, it seems that the possibility to declare function pointers with unknown parameter list is appreciated in parts of the C community for dealing with function call backs. Therefore, we will not attempt to remove this specific feature here, this would need much more preparation and discussion.

As a consequence, when removing identifier lists from function declarations and declaration lists from function definitions, we have to ensure that we still have syntax that allows for an empty parameter list.

REMOVING FUNCTION DEFINITIONS WITH IDENTIFIER LISTS

At many places the removal of the feature is straight forward, entire sentences or even paragraphs can be completely removed. These are found in 6.5.2.2 p6 (function call), 6.7.6.3 p3 (function declarations), and 6.9.1.5 p5 and p13 (function definitions), as referred in the appendix.

Remove support for function definitions with identifier lists N2432:3

Syntax changes

The declaration syntax for functions with identifier lists can simply be removed from direct declarator :

Remove the last line from syntax entry direct-declarator in 6.7.6 p1.

The depending syntax entry, identifier list, cannot be removed because it is also used for the syntax of #define. Therefore we propose to move that syntax entry to section 6.10 (preprocessing):

Move syntax entry identifier-list from 6.7.6 p1 to 6.10 p1.

Since we want to keep the possibility of specifying an empty parameter list, we add that feature to the sole remaining syntax for function declarations.

Make the term parameter-type-list of the term function-declarator in 6.7.6 p1 optional.

The definition syntax for functions with identifier lists can simply be removed:

Remove the declaration-list term from syntax entry function-definition in 6.9.1 p1.

The depending syntax entry, declaration-list, is not needed elsewhere and can therefore be removed, too:

Remove the syntax entry declaration-list from 6.9.1 p1.

For the term function body see below ??.

2.2. Function declarators (6.7.6.3)

Here, the paragraphs p13 changes, not only because of the removal of identifier lists, but also because we want to strengthen the case where an empty parameter list is used in a function definition. For that case, we propose to enforce the empty list as to be as if a list of void had been given. Thus implicitly it is defining a prototype with no parameters. The whole modified paragraph reads:

:::

For : a :::::::: function ::::::::: declarator :::::::: without : a :::::::::: parameter :::: type :::: list: : if :: it :: is :::: part :: of :: a ::::::::: definition ::

of :::: that :::::::: function :::: the :::::::: function ::: has ::: no ::::::::::: parameters :::: and ::: the :::::: effect :: is ::: as :: if :: it ::::: were :::::::

declared ::::: with :: a ::::::::: parameter ::::: type :::: list ::::::::: consisting :: of :::: the :::::::: keyword :::::: void; ::::::::: otherwise : it :::::::: specifies :::: that ::: no ::::::::::: information :::::: about ::: the :::::::: number :: or ::::: types ::: of ::: the ::::::::::: parameters :: is ::::::::::::

supplied. FNT1

::

A :::::::: function :::::::::: declarator :::::::: provides :: a ::::::::: prototype :::: for :::: the :::::::: function :: if :: it ::::::::: includes :: a :::::::::

parameter ::::: type ::::::::::::::::: list. FNT2 Otherwise, :: a ::::::::: function :::::::::: declaration :: is ::::: said ::: to ::::: have ::: no :::::::::

prototype. :

With two attached footnotes:

:::::::

FNT1 See ::::::: "future :::::::: language ::::::::::: directions" ::::::: (6.11.6). Paragraphs p14 also changes, because in the case analysis for the definition of compatible and composite types the cases for declarations with and without prototypes had been in-N2432:4

Lars Gullik Bjønnes and Jens Gustedt tertwined. Therefore the new version is much simpler than the previous, but cannot simply be obtained by text removal.

:::

For :::: two :::::::: function ::::: types ::: to ::: be ::::::::::: compatible, :::: both ::::: shall ::::::: specify :::::::::: compatible ::::::: return ::::: type :: is ::::: taken ::: as :::::: having :::: the :::::::::: unqualified ::::::: version :: of ::: its :::::::: declared ::::: type.

Function definitions

Since we want to be able to keep a syntax where a function can have an empty parameter list, the removal of the syntax entry with identifier list (6.7.6 p1) implies that we have to make the appearance of the parameter type list in function declarator optional. Otherwise, the removal of the corresponding descriptive text then has no surprises.

Question

Question 1. Shall the feature of function definitions with identifier lists be removed from ISO 9899 as proposed in N2432?

EDITORIAL CHANGES

Along with the above changes we also noticed some possible minor improvements that are merely editorial. The current document has an abusively long footnote 173 that is better expressed as a simple note at the end of the clause.

Question 2. Shall footnote 173 be tranformed into a new note (6.9.1 p13) as proposed in N2432?

Properly introduce the syntax entry function-body

The term function body is used in various places of the document, but it is only introduced half-heartedly as

Remove support for function definitions with identifier lists N2432:5 ... the compound statement that constitutes the body of the definition is executed ... We propose to clarify this by introducing the new syntax term function body (that simply resolves to compound statement) and to change the above sentence as ... the compound statement that constitutes the body of the definition :::: body is executed ... Question 3. Shall 6.9.1 introduce the term function body as proposed in N2432?

3.3. Remove the term no-leading-attribute-declaration

During the integration of the attribute feature the the term no leading attribute declaration had been introduced to cope with the specific case of declaration list. This can now be removed to simplify the corresponding syntax derivation for declarations.

Question 4. Shall we remove the term no leading attribute declaration as proposed in N2432?

Appendix: pages with diffmarks of the proposed changes against the September 2019 working draft.

The following page numbers are from the particular snapshot and may vary once the changes are integrated.

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases: one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types; both types are pointers to qualified or unqualified versions of a character type or void.

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

No other conversions are performed implicitly; in particular, the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator.

If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.

There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function. 102) Recursive function calls shall be permitted, both directly and indirectly through any chain of other functions.

EXAMPLE In the function call

( * pf[f1()]) (f2(), f3() + f4())
the functions f1, f2, f3, and f4 can be called in any order. All side effects have to be completed before the function pointed to by pf[f1()] is called.

Forward references: function declarators (including prototypes) (?? ::::: 6.7.6.3), function definitions (6.9.1), the return statement (6.8.6.4), simple assignment (6.5.16.1).

Structure and union members Constraints

The first operand of the . operator shall have an atomic, qualified, or unqualified structure or union type, and the second operand shall name a member of that type.

The first operand of the-> operator shall have type "pointer to atomic, qualified, or unqualified structure" or "pointer to atomic, qualified, or unqualified union", and the second operand shall name a member of the type pointed to.

Semantics

A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member, 103) and is an lvalue if the first expression is an lvalue. If the first expression has qualified type, the result has the so-qualified version of the type working draft -September 25, 2019 ISO/IEC 9899:202x (E)

Cast operators

Syntax cast-expression: unary-expression ( type-name ) cast-expression

Constraints

Unless the type name specifies a void type, the type name shall specify atomic, qualified, or unqualified scalar type, and the operand shall have scalar type.

Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.

A pointer type shall not be converted to any floating type. A floating type shall not be converted to any pointer type.

Semantics

Preceding an expression by a parenthesized type name converts the value of the expression to the unqualified version of the named type. This construction is called a cast. 112) A cast that specifies no conversion has no effect on the type or value of an expression.

If the value of the expression is represented with greater range or precision than required by the type named by the cast (6.3.1.8), then the cast specifies a conversion even if the type of the expression is the same as the named type and removes any extra range and precision.

Forward references: equality operators (6.5.9), function declarators (including prototypes) (?? :::::: 6.7.6.3), simple assignment (6.5.16.1), type names (6.7.7).

Multiplicative operators

Syntax multiplicative-expression: cast-expression multiplicative-expression * cast-expression multiplicative-expression / cast-expression multiplicative-expression % cast-expression

Constraints

Each of the operands shall have arithmetic type. The operands of the % operator shall have integer type.

If either operand has decimal floating type, the other operand shall not have standard floating type, complex type, or imaginary type.

Semantics

The usual arithmetic conversions are performed on the operands.

The result of the binary * operator is the product of the operands.

The result of the / operator is the quotient from the division of the first operand by the second; the result of the % operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined.

When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded. 113) If the quotient a/b is representable, the expression (a/b) * b + a%b shall equal a;

112) A cast does not yield an lvalue. 113) This is often called "truncation toward zero".

Declarations

Syntax no-leading-attribute-declaration: :::::::::: declaration: : declaration-specifiers init-declarator-list opt ; static_assert-declaration declaration: no-leading-attribute-declaration attribute-specifier-sequence declaration-specifiers init-declarator-list ;

::::::::::::::::::::

static_assert-declaration ::::::::::::::::: attribute-declaration declaration-specifiers: declaration-specifier attribute-specifier-sequence opt declaration-specifier declaration-specifiers declaration-specifier:

storage-class-specifier type-specifier-qualifier function-specifier init-declarator-list: init-declarator init-declarator-list , init-declarator init-declarator: declarator declarator = initializer attribute-declaration: attribute-specifier-sequence ;

Constraints

A declaration other than a static_assert or attribute declaration shall declare at least a declarator (other than the parameters of a function or the members of a structure or union), a tag, or the members of an enumeration.

If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except that:

-a typedef name may be redefined to denote the same type as it currently does, provided that type is not a variably modified type;

-tags may be redeclared as specified in 6.7.2.3.

All declarations in the same scope that refer to the same object or function shall specify compatible types.

Semantics

A declaration specifies the interpretation and properties of a set of identifiers. A definition of an identifier is a declaration for that identifier that:

-for an object, causes storage to be reserved for that object;

-for a function, includes the function body; 126)

-for an enumeration constant, is the (only) declaration of the identifier;

-for a typedef name, is the first (or only) declaration of the identifier.

The declaration specifiers consist of a sequence of specifiers, followed by an optional attribute specifier sequence, that indicate the linkage, storage duration, and part of the type of the entities that the declarators denote. The init declarator list is a comma-separated sequence of declarators, each of 126) Function definitions have a different syntax, described in 6.9.1. § 6.7 Language ISO/IEC 9899:202x (E)

working draft -September 25, 2019 C2x..k-r+editorial 2432 which may have additional type information, or an initializer, or both. The declarators contain the identifiers (if any) being declared. The optional attribute specifier sequence appertains to each of the entities declared by the declarators of the init declarator list.

If an identifier for an object is declared with no linkage, the type for the object shall be complete by the end of its declarator, or by the end of its init-declarator if it has an initializer; in the case of function parameters(including in prototypes), it is the adjusted type (see 6.7.6.3 ) that is required to be complete.

The optional attribute specifier sequence terminating a sequence of declaration specifiers appertains to the type determined by the preceding sequence of declaration specifiers. The attribute specifier sequence affects the type only for the declaration it appears in, not other declarations involving the same type.

Except where specified otherwise, the meaning of an attribute declaration is implementation-defined.

EXAMPLE

In the declaration for an entity, attributes appertaining to that entity may appear at the start of the declaration and after the identifier for that declaration.

[

[deprecated]] void f [[deprecated]] (void); // valid
Forward references: declarators (6.7.6), enumeration specifiers (6.7.2.2), initialization (6.7.9), type names (6.7.7), type qualifiers (6.7.3).

Storage-class specifiers

Constraints

At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _ Thread _ local may appear with static or extern. 127) In the declaration of an object with block scope, if the declaration specifiers include _ Thread _ local, they shall also include either static or extern. If _ Thread _ local appears in any declaration of an object, it shall be present in every declaration of that object.

_ Thread _ local shall not appear in the declaration specifiers of a function declaration.

Semantics

The typedef specifier is called a "storage-class specifier" for syntactic convenience only; it is discussed in 6.7.8. The meanings of the various linkages and storage durations were discussed in 6.2.2 and 6.2.4.

A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined. 128)

The declaration of an identifier for a function that has block scope shall have no explicit storage-class specifier other than extern.

ISO/IEC 9899:202x (E)

working draft -September 25, 2019 C2x..k-r+editorial 2432

Semantics

The first form is equivalent to _ Alignas( _ Alignof( type-name)).

The alignment requirement of the declared object or member is taken to be the specified alignment.

An alignment specification of zero has no effect. 149) When multiple alignment specifiers occur in a declaration, the effective alignment requirement is the strictest specified alignment.

If the definition of an object has an alignment specifier, any other declaration of that object shall either specify equivalent alignment or have no alignment specifier. If the definition of an object does not have an alignment specifier, any other declaration of that object shall also have no alignment specifier. If declarations of an object in different translation units have different alignment specifiers, the behavior is undefined. opt ) attribute-specifier-sequence opt or D ( identifier-list opt ) and the type specified for ident in the declaration "T D" is "deriveddeclarator-type-list T", then the type specified for ident is "derived-declarator-type-list function returning the unqualified version of T". The optional attribute specifier sequence appertains to the function type.

Declarators

A parameter type list specifies the types of, and may declare identifiers for, the parameters of the function.

A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

A declaration of a parameter as "function returning type" shall be adjusted to "pointer to function returning type", as in 6.3.2.1.

If the list terminates with an ellipsis (, ...), no information about the number or types of the parameters after the comma is supplied. 152) The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.

If, in a parameter declaration, an identifier can be treated either as a typedef name or as a parameter name, it shall be taken as a typedef name.

If the function declarator is not part of a definition of that function, parameters may have incomplete type and may use the [ * ] notation in their sequences of declarator specifiers to specify variable length array types.

The storage class specifier in the declaration specifiers for a parameter declaration, if present, is ignored unless the declared parameter is one of the members of the parameter type list for a function definition. The optional attribute specifier sequence in a parameter declaration appertains to the parameter.

An identifier list declares only the identifiers of the parameters of the function. An empty list in ::: For a function declarator that ::::::: without :: a ::::::::: parameter ::::: type ::: list: :: if :: it : is part of a definition of that function specifies that the function has no parameters . The empty list in a function declarator that is not part of a definition of that function :::: and ::: the ::::: effect ::: is :: as :: if :: it ::::: were :::::::: declared ::::: with : a :::::::::: parameter ::::: type ::: list ::::::::: consisting :: of :::: the :::::::: keyword ::::: void; :::::::::: otherwise : it : specifies that no information about the number or types of the parameters is supplied. 153) : A :::::::: function ::::::::: declarator ::::::::: provides : a prototype ::: for ::: the :::::::: function :: if : it :::::::: includes :: a ::::::::: parameter ::::: type ::: list. 154) :::::::::

Otherwise, :: a :::::::: function :::::::::: declaration :: is :::: said ::: to :::: have ::: no :::::::::: prototype.

For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that ::: has ::::: none ::: and : is not part of a function definitionand that contains an empty identifier list, the parameter list shall not have an ellipsis terminatorand the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. ( : . : In the determination of type compatibility and of a 152) The macros defined in the <stdarg.h> header (7.16) can be used to access arguments that correspond to the ellipsis. 153) See "future language directions" (6.11.6).

154)

:::

This :::::: implies ::: that : a :::::: function :::::::: definition :::::: without : a :::::::: parameter :: list ::::::: provides : a :::::::: prototype, ::: and :::: that :::::::: subsequent :::: calls :: to ::: that

Constraints

The storage-class specifiers auto and register shall not appear in the declaration specifiers in an external declaration.

There shall be no more than one external definition for each identifier declared with internal linkage in a translation unit. Moreover, if an identifier declared with internal linkage is used in an expression (other than as a part of the operand of a sizeof or _ Alignof operator whose result is an integer constant), there shall be exactly one external definition for the identifier in the translation unit.

Semantics

As discussed in 5.1.1.1, the unit of program text after preprocessing is a translation unit, which consists of a sequence of external declarations. These are described as "external" because they appear outside any function (and hence have file scope). As discussed in 6.7, a declaration that also causes storage to be reserved for an object or a function named by the identifier is a definition.

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _ Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one. 172)

Function definitions

Syntax function-definition: attribute-specifier-sequence opt declaration-specifiers declarator declaration-list opt compound-statement :::::::::::: function-body :

declaration-list: no-leading-attribute-declaration ::::::::::::: function-body: declaration-list no-leading-attribute-declaration ::::::::::::::::: compound-statement :

Constraints

The identifier declared in a function definition (which is the name of the function) shall have a function type, as specified by the declarator portion of the function definition.

The return type of a function shall be void or a complete object type other than array type.

The storage-class specifier, if any, in the declaration specifiers shall be either extern or static.

If the declarator includes a parameter type list, the ::: The : declaration of each parameter shall include an identifier, except for the special case of a parameter list consisting of a single parameter of type void, in which case there shall not be an identifier. No declaration list shall follow.

working draft -September 25, 2019 ISO/IEC 9899:202x (E)

If the declarator includes an identifier list, each declaration in the declaration list shall have at least one declarator, those declarators shall declare only identifiers from the identifier list, and every identifier in the identifier list shall be declared. An identifier declared as a typedef name shall not be redeclared as a parameter. The declarations in the declaration list shall contain no storage-class specifier other than register and no initializations.

Semantics

The optional attribute specifier sequence in a function definition appertains to the function.

The declarator in a function definition specifies the name of the function being defined and the identifiers of its parameters. If the declarator includes a parameter type list, the list also specifies the :::: and types of all the parameters; such a ::: the declarator also serves as a function prototype for later calls to the same function in the same translation unit. If the declarator includes an identifier list, 173) the types of the parameters shall be declared in a following declaration list. In either case, the :::: The type of each parameter is adjusted as described in?? for a parameter type list :::::: 6.7.6.3; the resulting type shall be a complete object type.

If a function that accepts a variable number of arguments is defined without a parameter type list that ends with the ellipsis notation, the behavior is undefined.

Each parameter has automatic storage duration; its identifier is an lvalue. 173) The layout of the storage for parameters is unspecified.

On entry to the function, the size expressions of each variably modified parameter are evaluated and the value of each argument expression is converted to the type of the corresponding parameter as if by assignment. (Array expressions and function designators as arguments were converted to pointers before the call.)

After all parameters have been assigned, the compound statement that constitutes the body of the function definition ::::: body is executed.

Unless otherwise specified, if the } that terminates a function ::: the :::::::: function ::::: body is reached, and the value of the function call is used by the caller, the behavior is undefined.

NOTE :

In :: a :::::: function :::::::: definition, :: the :::: type :: of :: the ::::::: function ::: and :: its ::::::: prototype ::::: cannot :: be ::::::: inherited :::: from : a :::::: typedef: : 172) The intent is that the type category in a function definition cannot be inherited from a typedef: 173) See "future language directions" (??). 173) A parameter identifier cannot be redeclared in the function body except in an enclosed block. # else new-line group opt (6.10) endif-line:

Preprocessing directives

# endif new-line (6.10) control-line:

# include pp-tokens new-line # define identifier replacement-list new-line # define identifier lparen identifier-list opt ) replacement-list new-line # define identifier lparen ... ) replacement-list new-line # define identifier lparen identifier-list , ... )

replacement-list new-line # undef identifier new-line # line pp-tokens new-line # error pp-tokens opt new-line # pragma pp-tokens opt new-line # new-line (6.10) text-line:

pp-tokens opt new-line (6.10) non-directive: pp-tokens new-line (6.10) lparen: a ( character not immediately preceded by white space (6.10) replacement-list: pp-tokens opt (6.10) pp-tokens:

preprocessing-token pp-tokens preprocessing-token (6.10) new-line:

the new-line character (6.10) ::::::::::: identifier-list: : :::::::::::::::::::::::: Common warnings 1 An implementation may generate warnings in many situations, none of which are specified as part of this document. The following are a few of the more common situations.

2

-A new struct or union type appears in a function prototype (6.2.1, 6.7.2.3).

-A block with initialization of an object that has automatic storage duration is jumped into (6.2.4).

-An implicit narrowing conversion is encountered, such as the assignment of a long int or a double to an int, or a pointer to void to a pointer to any type other than a character type (6.3).

-A hexadecimal floating constant cannot be represented exactly in its evaluation format (6.4.4.2).

-An integer character constant includes more than one character or a wide character constant includes more than one multibyte character (6.4.4.4).

-The characters / * are found in a comment (6.4.7).

-An "unordered" binary operator (not comma, &&, or ||) contains a side effect to an lvalue in one operand, and a side effect to, or an access to the value of, the identical lvalue in the other operand (6.5).

-A function is called but no prototype has been supplied (6.5.2.2).

-The arguments in a function call do not agree in number and type with those of the parameters in a function definition that is not a prototype (6.5.2.2). An object is defined but not used (6.7).

-A value is given to an object of an enumerated type other than by assignment of an enumeration constant that is a member of that type, or an enumeration object that has the same type, or the value of a function that returns the same enumerated type (6.7.2.2).

-An aggregate has a partly bracketed initialization (6.7.8).

-A statement cannot be reached (6.8).

-A statement with no apparent effect is encountered (6.8).

-A constant expression is used as the controlling expression of a selection statement (6.8.4).

-An incorrectly formed preprocessing group is encountered while skipping a preprocessing group (6.10.1).

-An unrecognized #pragma directive is encountered (6.10.6). § H. -Conversion of a pointer to an integer type produces a value outside the range that can be represented (6.3.2.3).

-Conversion between two pointer types produces a result that is incorrectly aligned (6.3.2.3).

-A pointer is used to call a function whose type is not compatible with the referenced type (6.3.2.3).

-An unmatched ' or " character is encountered on a logical source line during tokenization (6.4).

-A reserved keyword token is used in translation phase 7 or 8 for some purpose other than as a keyword (6.4.1).

-A universal character name in an identifier does not designate a character whose encoding falls into one of the specified ranges (6.4.2.1).

-The initial character of an identifier is a universal character name designating a digit (6.4.2.1).

-Two identifiers differ only in nonsignificant characters (6.4.2.1).

-The identifier __ func __ is explicitly declared (6.4.2.2).

-The program attempts to modify a string literal (6.4.5).

-The characters' , \, ", //, or / * occur in the sequence between the < and > delimiters, or the characters ' , \, //, or / * occur in the sequence between the " delimiters, in a header name preprocessing token (6.4.7).

-A side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object (6.5).

-An exceptional condition occurs during the evaluation of an expression (6.5).

-An object has its stored value accessed other than by an lvalue of an allowable type (6.5).

-For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters (6.5.2.2).

-For a call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after default argument promotion are not compatible with the types of the parameters (6.5.2.2).

-For a call to a function without a function prototype in scope where the function is not defined with a function prototype, the types of the arguments after default argument promotion are not compatible with those of the parameters after promotion (with certain exceptions) (6.5.2.2). A function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function (6.5.2.2).

-A member of an atomic structure or union is accessed (6.5.2.3).

-The operand of the unary * operator has an invalid value (6.5.3.2).

-A pointer is converted to other than an integer or pointer type (6.5.4).

-The value of the second operand of the / or % operator is zero (6.5.5).

-If the quotient a/b is not representable, the behavior of both a/b and a%b (6.5.5).

-Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that does not point into, or just beyond, the same array object (6.5.6).

ISO/IEC 9899:202x (E)

working draft -September 25, 2019 C2x..k-r+editorial 2432 -An attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type (6.7.3).

-The specification of a function type includes any type qualifiers (6.7.3).

-Two qualified types that are required to be compatible do not have the identically qualified version of a compatible type (6.7.3).

-An object which has been modified is accessed through a restrict-qualified pointer to a constqualified type, or through a restrict-qualified pointer and another pointer that are not both based on the same object (6.7.3.1).

-A restrict-qualified pointer is assigned a value based on another restricted pointer whose associated block neither began execution before the block associated with this pointer, nor ended before the assignment (6.7.3.1).

-A function with external linkage is declared with an inline function specifier, but is not also defined in the same translation unit (6.7.4).

-A function declared with a _ Noreturn function specifier returns to its caller (6.7.4).

-The definition of an object has an alignment specifier and another declaration of that object has a different alignment specifier (6.7.5).

-Declarations of an object in different translation units have different alignment specifiers (6.7.5).

-Two pointer types that are required to be compatible are not identically qualified, or are not pointers to compatible types (6.7.6.1).

-The size expression in an array declaration is not a constant expression and evaluates at program execution time to a nonpositive value (6.7.6.2).

-In a context requiring two array types to be compatible, they do not have compatible element types, or their size specifiers evaluate to unequal values (6.7.6.2).

-A declaration of an array parameter includes the keyword static within the [ and ] and the corresponding argument does not provide access to the first element of an array with at least the specified number of elements (6.7.6.3 ).

-A storage-class specifier or type qualifier modifies the keyword void as a function parameter type list (6.7.6.3 ).

-In a context requiring two function types to be compatible, they do not have compatible return types, or their parameters disagree in use of the ellipsis terminator or the number and type of parameters (after default argument promotion, when there is no parameter type listor when one type is specified by a function definition with an identifier list) (??) ::::::: (6.7.6.3).

-The value of an unnamed member of a structure or union is used (6.7.9).

-The initializer for a scalar is neither a single expression nor a single expression enclosed in braces (6.7.9).

-The initializer for a structure or union object that has automatic storage duration is neither an initializer list nor a single expression that has compatible structure or union type (6.7.9).

-The initializer for an aggregate or union, other than an array initialized by a string literal, is not a brace-enclosed list of initializers for its elements or members (6.7.9).

-An identifier with external linkage is used, but in the program there does not exist exactly one external definition for the identifier, or the identifier is not used and there exist multiple external definitions for the identifier (6.9). A function definition includes an identifier list, but the types of the parameters are not declared in a following declaration list (6.9.1).

/

  / prototype declaration long maxl ( long a , long b ) ; // prototype definition long maxl ( long a , long b ) { return a > b ? a : b ; } And the equivalent code for short instead of long is also valid: // prototype declaration short maxs ( short a , short b ) ; // prototype definition short maxs ( short a , short b ) { return a > b ? a : b ; }

FNT2

  This ::::::: implies :::: that :: a :::::::: function ::::::::: definition ::::::: without :: a :::::::::: parameter ::: list :::::::: provides :: a ::::::::: prototype, :::: and ::::: that :::::::::: subsequent ::::: calls :: to ::::: that :::::::: function :: in :::: the ::::: same ::::::::::: translation :::: unit ::: are ::::::::::: constrained :::: not ::: to ::::::: provide :::: any ::::::::: argument ::: to :::: the :::::::: function :::: call. :::::: Thus : a ::::::::: definition ::: of : a :::::::: function :::::::: without :::::::::: parameter ::: list :::: and :::: one ::::: that :::: has :::: such :: a :::: list ::::::::: consisting :: of ::: the :::::::: keyword ::::: void ::: are ::::: fully :::::::::: equivalent.

  types. :::::::::: Moreover, :::: the ::::::::: parameter ::::: type ::::: lists, :: if ::::: both :::: are :::::::: present, ::::: shall ::::: agree ::: in ::: the ::::::: number ::: of :::::::::: parameters :::: and ::: in ::: use ::: of ::: the ::::::: ellipsis ::::::::::: terminator; ::::::::::::: corresponding :::::::::: parameters :::: shall ::::: have :::::::::: compatible ::::: types. :: If :::: one :::: type ::: has :: a ::::::::: parameter ::::: type ::: list :::: and ::: the ::::: other :::: type :::: has :::: none :::: and :: is ::: not :::: part :: of :: a ::::::: function :::::::::: definition, ::: the ::::::::: parameter :::: list :::: shall :::: not :::: have ::: an :::::: ellipsis ::::::::::: terminator. :: In :::: the ::::::::::::: determination :: of :::: type ::::::::::::: compatibility ::: and ::: of : a :::::::::: composite ::::: type, ::::: each ::::::::: parameter :::::::: declared ::::: with :::::::: function ::: or ::::: array ::::: type : is :::::: taken :: as ::::::: having ::: the :::::::: adjusted :::: type :::: and ::::: each ::::::::: parameter :::::::: declared ::::: with :::::::: qualified ::::

3. 1 .

 1 Transform an abusively long footnote into a note.

:EXAMPLE 4

 4 ::::: function :: in :: the :::: same ::::::::: translation :::unit :: are ::::::::: constrained ::: not :: to :::::: provide ::: any ::::::: argument :: to ::: the :::::: function ::: call. :::: Thus :: a ::::::: definition :: of : a :::::: function :::::: without :::::::: parameter :: list ::: and ::: one :::: that :: has :::: such : a ::: list ::::::: consisting :: of ::: the ::::::: keyword ::: void ::: are :::: fully :::::::: equivalent. each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type. ) EXAMPLE 1 The declaration int f(void), * fip(), ( * pfi)(); declares a function f with no parameters returning an int, a function fip with no parameter specification returning a pointer to an int, and a pointer pfi to a function with no parameter specification returning an int. It is especially useful to compare the last two. The binding of * fip() is * (fip()) , so that the declaration suggests, and the same construction in an expression requires, the calling of a function fip, and then using indirection through the pointer result to yield an int. In the declarator ( * pfi)(), the extra parentheses are necessary to indicate that indirection through a pointer to a function yields a function designator, which is then used to call the function; it returns an int. 16 If the declaration occurs outside of any function, the identifiers have file scope and external linkage. If the declaration occurs inside a function, the identifiers of the functions f and fip have block scope and either internal or external linkage (depending on what file scope declarations for these identifiers are visible), and the identifier of the pointer pfi has block scope and no linkage. EXAMPLE 2 The declaration int ( * apfi[3])(int * x, int * y); declares an array apfi of three pointers to functions returning int. Each of these functions has two parameters that are pointers to int. The identifiers x and y are declared for descriptive purposes only and go out of scope at the end of the declaration of apfi. EXAMPLE 3 The declaration int ( * fpfi(int ( * )(long), int))(int, ...); declares a function fpfi that returns a pointer to a function returning an int. The function fpfi has two parameters: a pointer to a function returning an int (with one parameter of type long int), and an int. The pointer returned by fpfi points to a function that has one int parameter and accepts zero or more additional arguments of any type. The following prototype has a variably modified parameter. void addscalar(int n, int m, double a[n][n * m+300], double x); addscalar(int n, int m, double a[n][n * m+300], double x) { for (int i = 0; i < n; i++) for (int j = 0, k = n * m+300; j < k; j++) // a is a pointer to a VLA with n * m+300 elements a[i][j] += x; } EXAMPLE 5 The following are all compatible function prototype declarators. double maximum(int n, int m, double a[n][m]); double maximum(int n, int m, double a[ * ][ * ]); double maximum(int n, int m, double a[ ][ * ]); double maximum(int n, int m, double a[ ][m]); as are: void f(double ( * restrict a)[5]);

:F

  : ::: ::::::: typedef :::: int : F : ( :::: void : ) : ; : ::::::::::: // type : F is "function with no parameters ::::::::::::::::::::::::::::::::::::: :: f : , :: g : ; : ::::::::::::::::::::::: // : f and : g both have type compatible with : F :: ::: : F :: f :: { ::: / * ::: ... ::: * / :: } : :::::::::::::: // WRONG: syntax/constraint error :: ::: : F :: g :: () :: { ::: / * ::: ... ::: * / :: } : :::::::::::: // WRONG: declares that : g returns a function :: ::: ::: int :: f : ( :::: void : ) : { ::: / * :::: ... ::: * / :: } ::::::: // RIGHT: : f has type compatible with : F :: ::: ::: int :: g :: () :: { :: / * :::: ... ::: * / :: } : :::::::::: // RIGHT: : g has type compatible with : F :: ::: : F :: * : e : ( :::: void : ) : { ::: / * :::: ... ::: * / :: } : ::::::: // : e returns a pointer to a function :: ::: : F :::: * (( : e : ) : ) : ( ::: void : ) :: { ::: / * :::: ... ::: * / :: } :::: // same: parentheses irrelevant :: ::: ::: int ::: ( * :: fp : ) : ( ::: void : ) : ; : ::::::::::::::: // :: fp points to a function that has type : F :: ::: : F :: * :: Fp : ; : :::::::::::::::::::::::: // :: Fp points to a function that has type : F EXAMPLE 1 In the following: extern int max(int a, int b) { return a > b ? a: b; } extern is the storage-class specifier and int is the type specifier; max(int a, int b) is the function declarator; and { return a > b ? a: b; }

:

  :::::::::: identifier-list: : :::::::::::::::::::::::: identifier :::::::::::::::::::::::::::

  identifier ::::::::::::::::::::::::::: identifier-list :: , :::::::: identifier : (6.10.6) standard-pragma:# pragma STDC FP _ CONTRACT on-off-switch # pragma STDC FENV _ ACCESS on-off-switch # pragma STDC FENV _ DEC _ ROUND direction # pragma STDC FENV _ ROUND dec-direction # pragma STDC CX _ LIMITED _ RANGE on-off-switch (6.10.6) on-off-switch: one of

  An attempt is made to use the value of a void expression, or an implicit or explicit conversion (except to void) is applied to a void expression (6.3.2.2).

	3.1.2 Common warnings ISO/IEC 9899:202x (E) working draft -September 25, 2019 -	C2x..k-r+editorial 2432

102) In other words, function executions do not "interleave" with each other.103) If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.

C2x..k-r+editorial working draft -September 25, 2019 ISO/IEC 9899:202x (E)

127) See "future language directions" (6.11.5).128) The implementation can treat any register declaration simply as an auto declaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifier register cannot be computed, either explicitly (by use of the unary & operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in 6.3.2.1). Thus, the only operator that can be applied to an array declared with storage-class specifier register is sizeof.

149) An alignment specification of zero also does not affect other alignment specifications in the same declaration.

172) Thus, if an identifier declared with external linkage is not used in an expression, there need be no external definition for it.

Portability issues § J.2

working draft -September 25, 2019 ISO/IEC 9899:202x (E)

The first declares x to be a pointer to int; the second declares y to be an array of int of unspecified size (an incomplete type), the storage for which is defined elsewhere.

EXAMPLE 3

The following declarations demonstrate the compatibility rules for variably modified types. // invalid: r has linkage and points to VLA static int ( * q)[m] = &B; // valid: q is a static block pointer to VLA } Forward references: function declarators (6.7.6.3 ), function definitions (6.9.1), initialization (6.7.9).

Function declarators Constraints

A function declarator shall not specify a return type that is a function type or an array type.

The only storage-class specifier that shall occur in a parameter declaration is register.

An identifier list in a function declarator that is not part of a definition of that function shall be empty.

After adjustment, the parameters in a parameter type list in a function declarator that is part of a definition of that function shall not have incomplete type. § 6.7.6. 3 Language

working draft -September 25, 2019 2432

is the function body. The following similar definition uses the identifier-list form for the parameter declarations: Here int a, b; is the declaration list for the parameters. The difference between these two definitions is that the first form acts as a prototype declaration that forces conversion of the arguments of subsequent calls to the function, whereas the second form does not.

EXAMPLE

External object definitions

Semantics

If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to { 0 } .

If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type.

working draft -September 25, 2019 ISO/IEC 9899:202x (E) 6.11 Future language directions 6.11.1 Floating types

Future standardization may include additional floating-point types, including those with greater range, precision, or both than long double.

Linkages of identifiers

Declaring an identifier with internal linkage at file scope without the static storage-class specifier is an obsolescent feature.

External names

Restriction of the significance of an external name to fewer than 255 characters (considering each universal character name or extended source character as a single character) is an obsolescent feature that is a concession to existing implementations.

Character escape sequences

Lowercase letters as escape sequences are reserved for future standardization. Other characters may be used in extensions.

Storage-class specifiers

The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature.

Function declarators

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) ::::::: without :::::::::: prototypes : is an obsolescent feature.

The use of function definitions with separate parameter identifier and declaration lists (not prototype-format parameter type and identifier declarators) is an obsolescent feature.

Pragma directives

Pragmas whose first preprocessing token is STDC are reserved for future standardization.

Predefined macro names

Macro names beginning with __ STDC _ are reserved for future standardization. parameter-declaration parameter-list , parameter-declaration (6.7.6) parameter-declaration: attribute-specifier-sequence opt declaration-specifiers declarator attribute-specifier-sequence opt declaration-specifiers abstract-declarator opt identifier-list: identifier identifier-list , identifier (6.7.7) type-name: specifier-qualifier-list abstract-declarator opt (6.7.7) abstract-declarator: pointer pointer opt direct-abstract-declarator (6.7.7) direct-abstract-declarator:

Uses of the

( abstract-declarator ) array-abstract-declarator attribute-specifier-sequence opt function-abstract-declarator attribute-specifier-sequence opt (6.7.7) array-abstract-declarator: direct-abstract-declarator opt [ type-qualifier-list opt assignment-expression opt ] direct-abstract-declarator opt [ static type-qualifier-list opt assignment-expression ] direct-abstract-declarator opt [ type-qualifier-list static assignment-expression ] direct-abstract-declarator opt [ * ] (6.7.7) function-abstract-declarator: direct-abstract-declarator opt ( parameter-type-list opt ) (6.7.8) typedef-name: identifier (6.7.9) initializer: assignment-expression { initializer-list } { initializer-list , } (6.7.9) initializer-list: designation opt initializer initializer-list , designation opt initializer (6.7.9) designation: designator-list = (6.7.9) designator-list: designator designator-list designator (6.7.9) designator:

[ constant-expression ] . identifier (6.7.10) static_assert-declaration:

_ Static _ assert ( constant-expression , string-literal ) ; _ Static _ assert ( constant-expression ) ; (6.7.11.1) A.2.4 External definitions (6.9) translation-unit: external-declaration translation-unit external-declaration (6.9) external-declaration: function-definition declaration (6.9.1) function-definition: attribute-specifier-sequence opt declaration-specifiers declarator declaration-list opt compound-statement :::::::::::: function-body :

(6.9.1) declaration-list: no-leading-attribute-declaration ::::::::::::: function-body: declaration-list no-leading-attribute-declaration ::::::::::::::::: compound-statement :

A.