C2X and C++17 finally added support for a simple, single-argument ‘static_assert’ that implements what the Gnulib ‘verify’ macro was doing back in 2005. Implement static_assert on older platforms. The only remaining advantage of ‘verify’ is a shorter name. * doc/posix-headers/assert.texi (assert.h): * doc/verify.texi (Compile-time Assertions): Modernize for C2X and C++17. * lib/verify.h (_GL_HAVE__STATIC_ASSERT1, _GL_HAVE_STATIC_ASSERT1): New macros. (_GL_HAVE__STATIC_ASSERT): Remove. (_GL_HAVE__STATIC_ASSERT): Rely more heavily on __STDC_VERSION__. (_GL_VERIFY_TRUE, _GL_VERIFY_TYPE): Remove 2nd arg, the diagnostic string. All callers changed. (_GL_VERIFY): Require 3 or more args, of which only the first 2 are used. All callers changed. (_Static_assert): Allow either 1 or 2 args, and define if !_GL_HAVE__STATIC_ASSERT1 instead of defining if !_GL_HAVE__STATIC_ASSERT. (static_assert): Define if !_GL_HAVE_STATIC_ASSERT1 instead of defining if !_GL_HAVE_STATIC_ASSERT. (verify_expr, verify): Don’t bother trying to copy the expression into the diagnostic, since 1-argument static_assert doesn’t. (verify): Prefer 1-argument _Static_assert if it works. * m4/assert_h.m4 (gl_ASSERT_H): Check for 1-argument static_assert. --- ChangeLog | 28 ++++++++++ doc/posix-headers/assert.texi | 21 +++++--- doc/verify.texi | 15 +++--- lib/verify.h | 97 +++++++++++++++++++---------------- m4/assert_h.m4 | 2 + 5 files changed, 105 insertions(+), 58 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 12231d97b..760cde3c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2019-05-09 Paul Eggert <egg...@cs.ucla.edu> + + Support C2X and C++17 static_assert + C2X and C++17 finally added support for a simple, single-argument + ‘static_assert’ that implements what the Gnulib ‘verify’ macro was + doing back in 2005. Implement static_assert on older platforms. + The only remaining advantage of ‘verify’ is a shorter name. + * doc/posix-headers/assert.texi (assert.h): + * doc/verify.texi (Compile-time Assertions): + Modernize for C2X and C++17. + * lib/verify.h (_GL_HAVE__STATIC_ASSERT1, _GL_HAVE_STATIC_ASSERT1): + New macros. + (_GL_HAVE__STATIC_ASSERT): Remove. + (_GL_HAVE__STATIC_ASSERT): Rely more heavily on __STDC_VERSION__. + (_GL_VERIFY_TRUE, _GL_VERIFY_TYPE): Remove 2nd arg, the diagnostic + string. All callers changed. + (_GL_VERIFY): Require 3 or more args, of which only the first 2 + are used. All callers changed. + (_Static_assert): Allow either 1 or 2 args, and define if + !_GL_HAVE__STATIC_ASSERT1 instead of defining if + !_GL_HAVE__STATIC_ASSERT. + (static_assert): Define if !_GL_HAVE_STATIC_ASSERT1 instead + of defining if !_GL_HAVE_STATIC_ASSERT. + (verify_expr, verify): Don’t bother trying to copy the expression + into the diagnostic, since 1-argument static_assert doesn’t. + (verify): Prefer 1-argument _Static_assert if it works. + * m4/assert_h.m4 (gl_ASSERT_H): Check for 1-argument static_assert. + 2019-05-08 Paul Eggert <egg...@cs.ucla.edu> Fix _GL_HAVE__STATIC_ASSERT typo diff --git a/doc/posix-headers/assert.texi b/doc/posix-headers/assert.texi index 785a07aa9..fa99d3b88 100644 --- a/doc/posix-headers/assert.texi +++ b/doc/posix-headers/assert.texi @@ -5,29 +5,34 @@ POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/based Gnulib module: assert-h -See also the Gnulib module @code{assert}. +See also the Gnulib modules @code{assert} and @code{verify}. Portability problems fixed by Gnulib: @itemize @item -The C11 and C++11 @code{static_assert}, and the C11 -@code{_Static_assert}, are not supported by many platforms. -For example, GCC versions before 4.6.0 do not support @code{_Static_assert}, -and G++ versions through at least 4.6.0 do not support @code{static_assert}. +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. +@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. @end itemize Portability problems not fixed by Gnulib: @itemize @item -C11 @code{_Static_assert} and C++11 @code{static_assert} +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 C11 @code{static_assert} and @code{_Static_assert} can also +The C @code{static_assert} and @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. @item -In C99, @code{assert} can be applied to any scalar expression. +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}. @end itemize diff --git a/doc/verify.texi b/doc/verify.texi index 3a92f2e05..65aede210 100644 --- a/doc/verify.texi +++ b/doc/verify.texi @@ -52,15 +52,18 @@ integer constant expression, then a compiler might reject a usage like @samp{verify (@var{V});} even when @var{V} is nonzero. -Although the standard @code{assert} macro is a runtime test, C11 -specifies a builtin @code{_Static_assert (@var{V}, -@var{STRING-LITERAL})}, its @file{assert.h} header has a similar macro -named @code{static_assert}, and C++11 has a similar +Although the standard @code{assert} macro is a runtime test, C2X +specifies a builtin @code{_Static_assert (@var{V})}, +its @file{assert.h} header has a similar macro +named @code{static_assert}, and C++17 has a similar @code{static_assert} builtin. These builtins and macros differ from @code{verify} in two major ways. First, they can also be used within a @code{struct} or @code{union} specifier, in place of an -ordinary member declaration. Second, they require the programmer to -specify a compile-time diagnostic as a string literal. +ordinary member declaration. Second, they allow the programmer to +specify, as an optional second argument, a compile-time diagnostic as +a string literal. If your program is not intended to be portable to +compilers that lack C2X or C++17 @code{static_assert}, the only +advantage of @code{verify} is that its name is a bit shorter. The @file{verify.h} header defines one more macro, @code{assume (@var{E})}, which expands to an expression of type @code{void} diff --git a/lib/verify.h b/lib/verify.h index eccd7e201..d7e15bc8a 100644 --- a/lib/verify.h +++ b/lib/verify.h @@ -21,23 +21,31 @@ #define _GL_VERIFY_H -/* Define _GL_HAVE__STATIC_ASSERT to 1 if _Static_assert works as per C11. - This is supported by GCC 4.6.0 and later, in C mode, and its use - here generates easier-to-read diagnostics when verify (R) fails. - - Define _GL_HAVE_STATIC_ASSERT to 1 if static_assert works as per C++11. - This is supported by GCC 6.1.0 and later, in C++ mode. - - Use this only with GCC. If we were willing to slow 'configure' - down we could also use it with other compilers, but since this - affects only the quality of diagnostics, why bother? */ -#if (4 < __GNUC__ + (6 <= __GNUC_MINOR__) \ - && (201112L <= __STDC_VERSION__ || !defined __STRICT_ANSI__) \ - && !defined __cplusplus) -# define _GL_HAVE__STATIC_ASSERT 1 -#endif -#if 6 <= __GNUC__ && defined __cplusplus -# define _GL_HAVE_STATIC_ASSERT 1 +/* Define _GL_HAVE__STATIC_ASSERT to 1 if _Static_assert (R, DIAGNOSTIC) + works as per C11. This is supported by GCC 4.6.0 and later, in C + mode. + + Define _GL_HAVE__STATIC_ASSERT1 to 1 if _Static_assert (R) works as + per C2X, and define _GL_HAVE_STATIC_ASSERT1 if static_assert (R) + works as per C++17. This is supported by GCC 9.1 and later. + + Support compilers claiming conformance to the relevant standard, + and also support GCC when not pedantic. If we were willing to slow + 'configure' down we could also use it with other compilers, but + since this affects only the quality of diagnostics, why bother? */ +#ifndef __cplusplus +# if (201112L <= __STDC_VERSION__ \ + || (!defined __STRICT_ANSI__ && 4 < __GNUC__ + (6 <= __GNUC_MINOR__))) +# define _GL_HAVE__STATIC_ASSERT 1 +# endif +# if (202000L <= __STDC_VERSION__ \ + || (!defined __STRICT_ANSI__ && 9 <= __GNUC__)) +# define _GL_HAVE__STATIC_ASSERT1 1 +# endif +#else +# if 201703L <= __cplusplus || 9 <= __GNUC__ +# define _GL_HAVE_STATIC_ASSERT1 1 +# endif #endif /* FreeBSD 9.1 <sys/cdefs.h>, included by <stddef.h> and lots of other @@ -167,11 +175,9 @@ #define _GL_GENSYM(prefix) _GL_CONCAT (prefix, _GL_COUNTER) /* Verify requirement R at compile-time, as an integer constant expression - that returns 1. If R is false, fail at compile-time, preferably - with a diagnostic that includes the string-literal DIAGNOSTIC. */ + that returns 1. If R is false, fail at compile-time. */ -#define _GL_VERIFY_TRUE(R, DIAGNOSTIC) \ - (!!sizeof (_GL_VERIFY_TYPE (R, DIAGNOSTIC))) +#define _GL_VERIFY_TRUE(R) (!!sizeof (_GL_VERIFY_TYPE (R))) #ifdef __cplusplus # if !GNULIB_defined_struct__gl_verify_type @@ -181,40 +187,43 @@ template <int w> }; # define GNULIB_defined_struct__gl_verify_type 1 # endif -# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \ - _gl_verify_type<(R) ? 1 : -1> -#elif defined _GL_HAVE__STATIC_ASSERT -# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \ +# define _GL_VERIFY_TYPE(R) _gl_verify_type<(R) ? 1 : -1> +#elif defined _GL_HAVE__STATIC_ASSERT1 +# define _GL_VERIFY_TYPE(R) \ struct { \ - _Static_assert (R, DIAGNOSTIC); \ + _Static_assert (R); \ int _gl_dummy; \ } #else -# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \ +# define _GL_VERIFY_TYPE(R) \ struct { unsigned int _gl_verify_error_if_negative: (R) ? 1 : -1; } #endif /* Verify requirement R at compile-time, as a declaration without a - trailing ';'. If R is false, fail at compile-time, preferably - with a diagnostic that includes the string-literal DIAGNOSTIC. + trailing ';'. If R is false, fail at compile-time. + + This macro requires three or more arguments but uses at most the first + two, so that the _Static_assert macro optionally defined below supports + both the C11 two-argument syntax and the C2X one-argument syntax. Unfortunately, unlike C11, this implementation must appear as an ordinary declaration, and cannot appear inside struct { ... }. */ -#ifdef _GL_HAVE__STATIC_ASSERT -# define _GL_VERIFY _Static_assert +#if defined _GL_HAVE__STATIC_ASSERT +# define _GL_VERIFY(R, DIAGNOSTIC, ...) _Static_assert (R, DIAGNOSTIC) #else -# define _GL_VERIFY(R, DIAGNOSTIC) \ +# define _GL_VERIFY(R, DIAGNOSTIC, ...) \ extern int (*_GL_GENSYM (_gl_verify_function) (void)) \ - [_GL_VERIFY_TRUE (R, DIAGNOSTIC)] + [_GL_VERIFY_TRUE (R)] #endif /* _GL_STATIC_ASSERT_H is defined if this code is copied into assert.h. */ #ifdef _GL_STATIC_ASSERT_H -# if !defined _GL_HAVE__STATIC_ASSERT && !defined _Static_assert -# define _Static_assert(R, DIAGNOSTIC) _GL_VERIFY (R, DIAGNOSTIC) +# if !defined _GL_HAVE__STATIC_ASSERT1 && !defined _Static_assert +# define _Static_assert(...) \ + _GL_VERIFY (__VA_ARGS__, "static assertion failed", -) # endif -# if !defined _GL_HAVE_STATIC_ASSERT && !defined static_assert +# if !defined _GL_HAVE_STATIC_ASSERT1 && !defined static_assert # define static_assert _Static_assert /* C11 requires this #define. */ # endif #endif @@ -235,22 +244,22 @@ template <int w> verify_true is obsolescent; please use verify_expr instead. */ -#define verify_true(R) _GL_VERIFY_TRUE (R, "verify_true (" #R ")") +#define verify_true(R) _GL_VERIFY_TRUE (R) /* Verify requirement R at compile-time. Return the value of the expression E. */ -#define verify_expr(R, E) \ - (_GL_VERIFY_TRUE (R, "verify_expr (" #R ", " #E ")") ? (E) : (E)) +#define verify_expr(R, E) (_GL_VERIFY_TRUE (R) ? (E) : (E)) /* Verify requirement R at compile-time, as a declaration without a - trailing ';'. */ + trailing ';'. verify (R) acts like static_assert (R) except that + it is portable to C11/C++14 and earlier, and its name is shorter + and may be more convenient. */ -#ifdef __GNUC__ -# define verify(R) _GL_VERIFY (R, "verify (" #R ")") +#ifdef _GL_HAVE__STATIC_ASSERT1 +# define verify(R) _Static_assert (R) #else -/* PGI barfs if R is long. Play it safe. */ -# define verify(R) _GL_VERIFY (R, "verify (...)") +# define verify(R) _GL_VERIFY (R, "verify (...)", -) #endif #ifndef __has_builtin diff --git a/m4/assert_h.m4 b/m4/assert_h.m4 index cf11bae6a..ee278b206 100644 --- a/m4/assert_h.m4 +++ b/m4/assert_h.m4 @@ -14,9 +14,11 @@ AC_DEFUN([gl_ASSERT_H], [AC_LANG_PROGRAM( [[#include <assert.h> static_assert (2 + 2 == 4, "arithmetic doesn't work"); + static_assert (2 + 2 == 4); ]], [[ static_assert (sizeof (char) == 1, "sizeof doesn't work"); + static_assert (sizeof (char) == 1); ]])], [gl_cv_static_assert=yes], [gl_cv_static_assert=no])]) -- 2.21.0