* m4/assert_h.m4 (gl_ASSERT_H): Also test for static_assert keyword a la C23, and define HAVE_C_STATIC_ASSERT if so. If not, arrange for config.h to #define static_assert by including <assert.h>, and then do "#undef assert" so that the assert macro still needs an explicit include. This should be safe even on very old hosts, as assert.h has been re-includable for decades. * tests/tests-assert.c: New test. * modules/assert-h-tests (Files, Makefile.am): Add it. --- ChangeLog | 13 +++++++ doc/gnulib.texi | 27 ++++++++++++- doc/posix-headers/assert.texi | 38 ++++++++++++------- m4/assert_h.m4 | 43 ++++++++++++++++----- modules/assert-h | 2 +- modules/assert-h-tests | 3 ++ tests/test-assert.c | 71 +++++++++++++++++++++++++++++++++++ 7 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 tests/test-assert.c
diff --git a/ChangeLog b/ChangeLog index d812e73f55..9e714143b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2022-09-12 Paul Eggert <egg...@cs.ucla.edu> + + assert-h: static_assert is a keyword in C23 + * m4/assert_h.m4 (gl_ASSERT_H): Also test for static_assert + keyword a la C23, and define HAVE_C_STATIC_ASSERT if so. + If not, arrange for config.h to #define static_assert + by including <assert.h>, and then do "#undef assert" + so that the assert macro still needs an explicit include. + This should be safe even on very old hosts, as assert.h + has been re-includable for decades. + * tests/tests-assert.c: New test. + * modules/assert-h-tests (Files, Makefile.am): Add it. + 2022-09-12 Bruno Haible <br...@clisp.org> supersede: Avoid a failure when writing to /dev/null in Solaris zones. diff --git a/doc/gnulib.texi b/doc/gnulib.texi index e9c2fd8fa6..1bef2a0975 100644 --- a/doc/gnulib.texi +++ b/doc/gnulib.texi @@ -865,7 +865,8 @@ This chapter describes which keywords specified by ISO C are substituted by Gnulib. @menu -* bool:: @code{bool}, @code{false}, and @code{true} +* bool:: @code{bool}, @code{false}, and @code{true} +* static_assert:: @code{static_assert} @end menu @node bool @@ -889,6 +890,30 @@ On pre-C23 platforms, the keyword substitutes are macros. On pre-C23 platforms, the keyword substitutes assume C99 or later. @end itemize +@node static_assert +@section @code{static_assert} + +Gnulib module: assert-h + +The @code{assert-h} module arranges for both @code{static_assert} and +@code{<assert.h>} to be like standard C@. @xref{assert.h}. + +Portability problems fixed by Gnulib: +@itemize +@item +Pre-C11 platforms lack @code{static_assert}. + +@item +On pre-C23 platforms, @code{<assert.h>} must be included before +using @code{static_assert}. +@end itemize + +Portability problems not fixed by Gnulib: +@itemize +@item +On pre-C23 platforms, @code{static_assert} is a macro. +@end itemize + @node Header File Substitutes @chapter ISO C and POSIX Header File Substitutes diff --git a/doc/posix-headers/assert.texi b/doc/posix-headers/assert.texi index 3392a1691a..5af0e8cb8f 100644 --- a/doc/posix-headers/assert.texi +++ b/doc/posix-headers/assert.texi @@ -10,28 +10,38 @@ See also the Gnulib modules @code{assert} and @code{verify}. Portability problems fixed by Gnulib: @itemize @item -On older platforms @code{static_assert} and @code{_Static_assert} do -not allow the second string-literal argument to be omitted. For -example, GCC versions before 9.1 do not support the single-argument -@code{static_assert} that was standardized by C2x and C++17. +On older C platforms @code{<assert.h>} must be included before using +@code{static_assert}. For example, GCC versions before 13 do not +support the @code{static_assert} keyword that was standardized by C23. @item -Even-older platforms do not support @code{static_assert} or -@code{_Static_assert} at all. For example, GCC versions before 4.6 do -not support @code{_Static_assert}, and G++ versions before 4.3 do not -support @code{static_assert}, which was standardized by C11 and C++11. +On older platforms @code{static_assert} does not allow the second +string-literal argument to be omitted. For example, GCC versions +before 9.1 do not support the single-argument @code{static_assert} +that was standardized by C23 and C++17. +@item +Even-older platforms do not support @code{static_assert} at all. +For example, GCC versions before 4.6 and G++ versions before 4.3 +do not support the two-argument form, which was standardized +by C11 and C++11. +@item +Older C platforms might not support the obsolescent +@code{_Static_assert} keyword or macro. +This portability problem should not matter with code using this or the +@code{static_assert} module, as such code should use +@code{static_assert} instead. @end itemize Portability problems not fixed by Gnulib: @itemize @item -C @code{_Static_assert} and C++ @code{static_assert} -are keywords that can be used without including @code{<assert.h>}. -The Gnulib substitutes are macros that require including @code{<assert.h>}. -@item -The C @code{static_assert} and @code{_Static_assert} can also +A @code{static_assert} can also be used within a @code{struct} or @code{union} specifier, in place of an ordinary declaration of a member of the struct or union. The -Gnulib substitute can be used only as an ordinary declaration. +Gnulib substitute can be used only as an ordinary declaration +in code intended to be portable to C99 or earlier. +@item +In C23 and C++11 and later, @code{static_assert} is a keyword. +In C11 and C17 it is a macro. Any Gnulib substitute is also a macro. @item In C99 and later, @code{assert} can be applied to any scalar expression. In C89, the argument to @code{assert} is of type @code{int}. diff --git a/m4/assert_h.m4 b/m4/assert_h.m4 index 7af15a2287..6300c859c7 100644 --- a/m4/assert_h.m4 +++ b/m4/assert_h.m4 @@ -12,18 +12,43 @@ AC_DEFUN([gl_ASSERT_H], AC_CACHE_CHECK([for static_assert], [gl_cv_static_assert], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( - [[#include <assert.h> - static_assert (2 + 2 == 4, "arithmetic doesn't work"); + [[static_assert (2 + 2 == 4, "arithmetic does not work"); static_assert (2 + 2 == 4); ]], [[ - static_assert (sizeof (char) == 1, "sizeof doesn't work"); + static_assert (sizeof (char) == 1, "sizeof does not work"); static_assert (sizeof (char) == 1); ]])], - [gl_cv_static_assert=yes], - [gl_cv_static_assert=no])]) - if test $gl_cv_static_assert = no; then - GL_GENERATE_ASSERT_H=true - gl_NEXT_HEADERS([assert.h]) - fi + [gl_cv_static_assert="yes, a keyword"], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include <assert.h> + static_assert (2 + 2 == 4, "arithmetic does not work"); + static_assert (2 + 2 == 4); + ]], + [[ + static_assert (sizeof (char) == 1, "sizeof does not work"); + static_assert (sizeof (char) == 1); + ]])], + [gl_cv_static_assert="yes, an <assert.h> macro"], + [gl_cv_static_assert=no])])]) + + AS_CASE([$gl_cv_static_assert], + [yes*keyword*], + [AC_DEFINE([HAVE_C_STATIC_ASSERT], [1], + [Define to 1 if the static_assert keyword works.])], + [no], + [GL_GENERATE_ASSERT_H=true + gl_NEXT_HEADERS([assert.h])]) + + dnl The "zz" puts this toward config.h's end, to avoid potential + dnl collisions with other definitions. #undef assert so that + dnl programs are not tempted to use it without specifically + dnl including assert.h. Break the #undef apart with a comment + dnl so that 'configure' does not comment it out. + AH_VERBATIM([zzstatic_assert], +[#if !defined HAVE_C_STATIC_ASSERT && __cpp_static_assert < 201411 + #include <assert.h> + #undef/**/assert +#endif]) ]) diff --git a/modules/assert-h b/modules/assert-h index bf7565dc21..b67d44fe4a 100644 --- a/modules/assert-h +++ b/modules/assert-h @@ -1,5 +1,5 @@ Description: -An <assert.h> that conforms to C11. +An <assert.h> and static_assert that are like C23. Files: lib/assert.in.h diff --git a/modules/assert-h-tests b/modules/assert-h-tests index 19375f61c3..670b06947c 100644 --- a/modules/assert-h-tests +++ b/modules/assert-h-tests @@ -1,7 +1,10 @@ Files: +tests/test-assert.c Depends-on: configure.ac: Makefile.am: +TESTS += test-assert +check_PROGRAMS += test-assert diff --git a/tests/test-assert.c b/tests/test-assert.c new file mode 100644 index 0000000000..45b0c0f457 --- /dev/null +++ b/tests/test-assert.c @@ -0,0 +1,71 @@ +/* Test assert.h and static_assert. + Copyright 2022 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert. */ + +#include <config.h> + +#define STATIC_ASSERT_TESTS \ + static_assert (2 + 2 == 4, "arithmetic does not work"); \ + static_assert (2 + 2 == 4); \ + static_assert (sizeof (char) == 1, "sizeof does not work"); \ + static_assert (sizeof (char) == 1) + +STATIC_ASSERT_TESTS; + +static char const * +assert (char const *p, int i) +{ + return p + i; +} + +static char const * +f (char const *p) +{ + return assert (p, 0); +} + +#include <assert.h> + +STATIC_ASSERT_TESTS; + +static int +g (void) +{ + assert (f ("this should work")); + return 0; +} + +#define NDEBUG 1 +#include <assert.h> + +STATIC_ASSERT_TESTS; + +static int +h (void) +{ + assert (f ("this should work")); + return 0; +} + +int +main (void) +{ + STATIC_ASSERT_TESTS; + g (); + h (); + return 0; +} -- 2.37.2