The noreturn keyword is useful for a few library functions such as abort and exit which cannot return. The user can also define their own functions that never return using this keyword.
_Noreturn void fatal (void);
void fatal() {
/* ... */
exit(1);
}
The noreturn keyword tells the compiler to assume that fatal() cannot return. It can then optimize without regard to what would happen if fatal ever did return. This makes slightly better code. More importantly, it helps avoid spurious warnings of uninitialized variables. You cannot assume that registers saved by the calling function are restored before calling the noreturn function. It does not make sense for a noreturn function to have a return type other than void.
This is a good keyword for standardization because it gives additional information that can be used by the optimizer, but does not alter the semantics of the program if removed. It is supported by both MSVC and GCC.
quick_exit()
.
void f (void) { FILE *f; f = fopen( file, ...); if (f == NULL) { handle_error( ... ); } /* work with f */ }If an SA tool wishes to infer that the /* work with f */ code only executes if f points to an open file, it currently has a difficult task...it has to do sophisticated execution analysis on the handle_error() function to see if it returns. It may not have access to the handle_error() source code, further complicating the process. A developer can virtually eliminate this complexity by indicating that handle_error() should be a noreturn function. Consequently, the SA tool can easily assume that f is a valid file when necessary, and consequently make stronger guarantees about the correctness of the code.
This is a saccrifice because the type system could help to identify locations where a noreturn function was useful. For instance, a function that takes a function pointer as an argument could insist that its argument be a noreturn function.
However, there is a simple workaround. It is easy to encapsulate the 'noreturn-ness' of a function pointer:
If a function wishes to treat a function pointer as a pointer to a noreturn function, it can simply call exit() after the function pointer. Consequently, if the pointed-to function actually does return, the system exits immediately.
void (*handle_error(void)); /* this should be a noreturn function */
...
void fn() {
if (!operation_successful()) {
handle_error();
// If we cannot guarantee the pointer does not return,
// we can stop this flow of control thus:
exit(1);
}
// at this point, the operation was successful
}
Therefore, we consider the sacrifice of not including 'noreturn' in the type system to be a worthwhile trade-off.
The same argument could be extended to claim that _Noreturn itself is not necessary, because a developer could always append an exit()
call after any non-returning function. To apply to the fopen()
example above, this would yield:
void f (void) { FILE *f; f = fopen( file, ...); if (f == NULL) { handle_error( ... ); exit(1); } /* work with f */ }Assuming a static analyzer knows that
exit()
never returns, it can easily infer that f
is a valid function pointer once the if-clause is complete.
This approach has one major disadvantage, however. It forces responsibility for handling 'noreturn' onto any function that calls a noreturn function. This increases the complexity of using non-returning functions, and also increases the likelihood the developer will forget to call exit()
, which leaves them in the same state as we are in today.
The noreturn keyword allows developers to invoke noreturn functions without concern for their return status, simplifying their code and reducing the chance for error. Explicitly calling exit()
after a noreturn function should only be necessary when the noreturn function is accessed through a function pointer.
void fn(void) __attribute__ ((noreturn));
__attribute__ ((noreturn)) void fn(void);
__attribute__ ((noreturn)) void fn(void) {exit(1);}
Consequently, the following macro definition is sufficient to achieve compatibility with GCC:
#define _Noreturn __attribute__ ((noreturn))
Consequently, the following are accepted as valid by GCC:
_Noreturn void fn(void);
_Noreturn void fn(void) {exit(1);}
__declspec
keyword, which is also documented in [N1403]. MS provides a 'noreturn' attribute with the same semantics proposed in this document. The following code samples are accepted by MSVC 2008 Express (on Windows XP SP3) as well-formed.
__declspec( noreturn) void fn(void);
__declspec( noreturn) void fn(void) {exit(1);}
Consequently, the following macro definition is sufficient to achieve compatibility with MSVC:
#define _Noreturn __declspec( noreturn)
Consequently, the following are accepted as valid by MSVC:
_Noreturn void fn(void);
_Noreturn void fn(void) {exit(1);}
void fn [[noreturn]] (void);
void fn [[noreturn]] (void) {exit(1);}
The draft standard for C++0x, [N3000], section 8.3, paragraph 5, states:
In a declaration attribute-specifieropt T attribute-specifieropt D where D is an unadorned identifier the type of this identifier is âTâ. The first optional attribute-specifier appertains to the entity being declared. The second optional attribute-specifier appertains to the type T, but not to the class or enumeration declared in the decl-specifier-seq, if any.
This implies that if the [[noreturn]] attribute precedes the declaration, it still appertains to the function being declared. Therefore, the following declarations have the same meaning:
[[noreturn]] void fn(void);
[[noreturn]] void fn(void) {exit(1);}
Consequently, the following macro definition is sufficient to achieve compatibility with C++0x:
#define _Noreturn [[noreturn]]
Consequently, the following would be accepted as valid for C++0x and C:
_Noreturn void fn(void);
_Noreturn void fn(void) {exit(1);}
#if _MSC_VER >= 1310
/* MS Visual Studio 2003/.NET Framework 1.1 or newer */
#define NORETURN _declspec( noreturn)
#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ >= 5)
/* GCC 2.5 or newer */
#define NORETURN __attribute__ ((noreturn))
#elif __cplusplus >= ???
// ??? = the value [tbd] specified in C++0x 16.8
#define NORETURN [[noreturn]]
#elif __STDC_VERSION__ >= 201ymmL
// the value specified in C1x 6.10.8)
#define NORETURN _Noreturn
#else
#define NORETURN ???
// (my project's choice: #error, or nothing)
#endif
Hence you can use the NORETURN macro to specify the noreturn property in portable code:
NORETURN void fn(void);
NORETURN void fn(void) {exit(1);}
Syntax function-specifier: inline _NoreturnModify paragraph 4 to read:
In a hosted environment, no function specifier shall appear in a declaration of main.Add a new paragraph after paragraph 4 before paragraph 5:
A function specifier may appear more than once; the behavior is the same as if it appeared only once.Modify paragraph 6 (formerly paragraph 5) to read:
A function declared with an inline function specifier is an inline function. Making a function an inline function suggests that calls to the function be as fast as possible.120) The extent to which such suggestions are effective is implementation-defined.121)This essentially takes the statement about how many times function specifiers may appear out of the paragraph about inline, and makes it into a paragraph by itself, because it can apply to the new keyword.
Add a new paragraph:
The keyword _Noreturn specifies that a function does not return. The implementation shall produce a diagnostic message if a function f is declared with the noreturn keyword, but the implementation cannot be certain that the function does not indeed return. If a function f is called where f was previously declared with the noreturn attribute, and f eventually returns, the behavior is undefined. [ Example: _Noreturn void f () { abort(); /* ok */ } _Noreturn void g (int i) { /* ill-formed if called with i <= 0 */ if (i > 0) abort(); } ]
longjmp()
raise()
abort()
exit()
_Exit()
quick_exit()