From c504bec035e90efceb212c538efc986ef3db7f7a Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 13 Sep 2022 17:29:35 -0500 Subject: [PATCH 1/4] 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 , 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 | 11 ++++++ doc/gnulib.texi | 27 ++++++++++++- doc/posix-headers/assert.texi | 37 +++++++++++------- m4/assert_h.m4 | 47 +++++++++++++++++------ modules/assert-h | 2 +- modules/assert-h-tests | 3 ++ tests/test-assert.c | 71 +++++++++++++++++++++++++++++++++++ 7 files changed, 171 insertions(+), 27 deletions(-) create mode 100644 tests/test-assert.c diff --git a/ChangeLog b/ChangeLog index 31fb6837f4..bba1a1a4e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,17 @@ 2022-09-13 Paul Eggert + 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 , 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. + stdalign-tests: port to C23 * tests/test-stdalign.c: Do not test __alignas_is_defined, _Alignof, or _Alignas as they are obsolescent. 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{} 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{} 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..f3d50533f9 100644 --- a/doc/posix-headers/assert.texi +++ b/doc/posix-headers/assert.texi @@ -10,28 +10,37 @@ 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{} 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 +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{}. -The Gnulib substitutes are macros that require including @code{}. -@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..36b3c78c91 100644 --- a/m4/assert_h.m4 +++ b/m4/assert_h.m4 @@ -8,22 +8,47 @@ dnl From Paul Eggert. AC_DEFUN([gl_ASSERT_H], [ - GL_GENERATE_ASSERT_H=false AC_CACHE_CHECK([for static_assert], [gl_cv_static_assert], - [AC_COMPILE_IFELSE( + [gl_save_CFLAGS=$CFLAGS + for gl_working in "yes, a keyword" "yes, an macro"; do + AS_CASE([$gl_working], + [*assert.h*], [CFLAGS="$gl_save_CFLAGS -DINCLUDE_ASSERT_H"]) + + AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( - [[#include - static_assert (2 + 2 == 4, "arithmetic doesn't work"); + [[#ifdef INCLUDE_ASSERT_H + #include + #endif + 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=$gl_working], + [gl_cv_static_assert=no]) + CFLAGS=$gl_save_CFLAGS + test "$gl_cv_static_assert" != no && break + done]) + + GL_GENERATE_ASSERT_H=false + 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 + #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 that conforms to C11. +An 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 . */ + +/* Written by Paul Eggert. */ + +#include + +#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 + +STATIC_ASSERT_TESTS; + +static int +g (void) +{ + assert (f ("this should work")); + return 0; +} + +#define NDEBUG 1 +#include + +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