ISO C 23 § 7.21.1 requires that <stddef.h> implements 'unreachable'.
This patch does it. 2023-03-16 Bruno Haible <br...@clisp.org> stddef: Define 'unreachable', for ISO C 23 compliance. * lib/verify.h (_GL_HAS_BUILTIN_UNREACHABLE): Don't define if already defined. * lib/stddef.in.h (_GL_HAS_BUILTIN_UNREACHABLE, unreachable): New macros. (abort): Declare if needed for unreachable. * m4/stddef_h.m4 (gl_STDDEF_H): Test for unreachable. * tests/test-stddef.c (test_unreachable_optimization, test_unreachable_noreturn): New functions, based on tests/test-verify.c. * doc/posix-headers/stddef.texi: Mention unreachable. diff --git a/doc/posix-headers/stddef.texi b/doc/posix-headers/stddef.texi index e240f93363..33ad48244c 100644 --- a/doc/posix-headers/stddef.texi +++ b/doc/posix-headers/stddef.texi @@ -7,6 +7,10 @@ Portability problems fixed by Gnulib: @itemize +@item +Some platforms fail to provide @code{unreachable}, which was added in C23: +GCC 13, clang 15, AIX with xlc 12.1, Solaris with Sun C 5.15, and others. + @item Some platforms fail to provide @code{max_align_t}, which was added in C11: NetBSD 8.0, Solaris 11.0, and others. diff --git a/lib/stddef.in.h b/lib/stddef.in.h index 6eadcc3d5a..9e9d4b7cc6 100644 --- a/lib/stddef.in.h +++ b/lib/stddef.in.h @@ -18,7 +18,7 @@ /* Written by Eric Blake. */ /* - * POSIX 2008 <stddef.h> for platforms that have issues. + * POSIX 2008 and ISO C 23 <stddef.h> for platforms that have issues. * <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stddef.h.html> */ @@ -142,6 +142,43 @@ typedef union # endif #endif +/* ISO C 23 § 7.21.1 The unreachable macro */ +#ifndef unreachable + +/* Code borrowed from verify.h. */ +# ifndef _GL_HAS_BUILTIN_UNREACHABLE +# if defined __clang_major__ && __clang_major__ < 5 +# define _GL_HAS_BUILTIN_UNREACHABLE 0 +# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) +# define _GL_HAS_BUILTIN_UNREACHABLE 1 +# elif defined __has_builtin +# define _GL_HAS_BUILTIN_UNREACHABLE __has_builtin (__builtin_unreachable) +# else +# define _GL_HAS_BUILTIN_UNREACHABLE 0 +# endif +# endif + +# if _GL_HAS_BUILTIN_UNREACHABLE +# define unreachable() __builtin_unreachable () +# elif 1200 <= _MSC_VER +# define unreachable() __assume (0) +# else +/* Declare abort(), without including <stdlib.h>. */ +extern +# if defined __cplusplus +"C" +# endif +_Noreturn +void abort (void) +# if defined __cplusplus && (__GLIBC__ >= 2) +throw () +# endif +; +# define unreachable() abort () +# endif + +#endif + # endif /* _@GUARD_PREFIX@_STDDEF_H */ # endif /* _@GUARD_PREFIX@_STDDEF_H */ #endif /* __need_XXX */ diff --git a/lib/verify.h b/lib/verify.h index f0b3fc5851..c700243209 100644 --- a/lib/verify.h +++ b/lib/verify.h @@ -285,14 +285,16 @@ template <int w> # define _GL_HAS_BUILTIN_TRAP 0 #endif -#if defined __clang_major__ && __clang_major__ < 5 -# define _GL_HAS_BUILTIN_UNREACHABLE 0 -#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) -# define _GL_HAS_BUILTIN_UNREACHABLE 1 -#elif defined __has_builtin -# define _GL_HAS_BUILTIN_UNREACHABLE __has_builtin (__builtin_unreachable) -#else -# define _GL_HAS_BUILTIN_UNREACHABLE 0 +#ifndef _GL_HAS_BUILTIN_UNREACHABLE +# if defined __clang_major__ && __clang_major__ < 5 +# define _GL_HAS_BUILTIN_UNREACHABLE 0 +# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) +# define _GL_HAS_BUILTIN_UNREACHABLE 1 +# elif defined __has_builtin +# define _GL_HAS_BUILTIN_UNREACHABLE __has_builtin (__builtin_unreachable) +# else +# define _GL_HAS_BUILTIN_UNREACHABLE 0 +# endif #endif /* Each of these macros verifies that its argument R is nonzero. To diff --git a/m4/stddef_h.m4 b/m4/stddef_h.m4 index a2322ebb7e..aa012219fc 100644 --- a/m4/stddef_h.m4 +++ b/m4/stddef_h.m4 @@ -1,4 +1,4 @@ -# stddef_h.m4 serial 13 +# stddef_h.m4 serial 14 dnl Copyright (C) 2009-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -68,6 +68,21 @@ AC_DEFUN_ONCE([gl_STDDEF_H] GL_GENERATE_STDDEF_H=true fi + AC_CACHE_CHECK([for unreachable], + [gl_cv_func_unreachable], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include <stddef.h> + ]], + [[unreachable (); + ]])], + [gl_cv_func_unreachable=yes], + [gl_cv_func_unreachable=no]) + ]) + if test $gl_cv_func_unreachable = no; then + GL_GENERATE_STDDEF_H=true + fi + if $GL_GENERATE_STDDEF_H; then gl_NEXT_HEADERS([stddef.h]) fi diff --git a/tests/test-stddef.c b/tests/test-stddef.c index 1d674b3cea..3ec00a8f25 100644 --- a/tests/test-stddef.c +++ b/tests/test-stddef.c @@ -68,6 +68,30 @@ static_assert (__alignof__ (wchar_t) <= __alignof__ (max_align_t)); static_assert (__alignof__ (struct d) <= __alignof__ (max_align_t)); #endif +int test_unreachable_optimization (int x); +_Noreturn void test_unreachable_noreturn (void); + +int +test_unreachable_optimization (int x) +{ + /* Check that the compiler uses 'unreachable' for optimization. + This function, when compiled with optimization, should have code + equivalent to + return x + 3; + Use 'objdump --disassemble test-stddef.o' to verify this. */ + if (x < 4) + unreachable (); + return (x > 1 ? x + 3 : 2 * x + 10); +} + +_Noreturn void +test_unreachable_noreturn (void) +{ + /* Check that the compiler's data-flow analysis recognizes 'unreachable ()'. + This function should not elicit a warning. */ + unreachable (); +} + int main (void) {