2022-09-13 Paul Eggert <eggert@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.
+
stdalign-tests: port to C23
* tests/test-stdalign.c: Do not test __alignas_is_defined,
_Alignof, or _Alignas as they are obsolescent.
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
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
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
+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}.
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 <assert.h> macro"; do
+ AS_CASE([$gl_working],
+ [*assert.h*], [CFLAGS="$gl_save_CFLAGS -DINCLUDE_ASSERT_H"])
+
+ AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
- [[#include <assert.h>
- static_assert (2 + 2 == 4, "arithmetic doesn't work");
+ [[#ifdef INCLUDE_ASSERT_H
+ #include <assert.h>
+ #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 <assert.h>
+ #undef/**/assert
+#endif])
])
Description:
-An <assert.h> that conforms to C11.
+An <assert.h> and static_assert that are like C23.
Files:
lib/assert.in.h
Files:
+tests/test-assert.c
Depends-on:
configure.ac:
Makefile.am:
+TESTS += test-assert
+check_PROGRAMS += test-assert
--- /dev/null
+/* 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;
+}