From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS22989 209.51.188.0/24 X-Spam-Status: No, score=-3.3 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id 83EB71F45F for ; Thu, 9 May 2019 16:13:31 +0000 (UTC) Received: from localhost ([127.0.0.1]:57399 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hOlfm-0000kl-PZ for normalperson@yhbt.net; Thu, 09 May 2019 12:13:30 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60388) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hOldg-0007hM-7V for bug-gnulib@gnu.org; Thu, 09 May 2019 12:11:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hOldd-0003cI-Dt for bug-gnulib@gnu.org; Thu, 09 May 2019 12:11:20 -0400 Received: from zimbra.cs.ucla.edu ([131.179.128.68]:46440) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hOldc-0003On-Hd for bug-gnulib@gnu.org; Thu, 09 May 2019 12:11:17 -0400 Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 1B1C71619B2 for ; Thu, 9 May 2019 09:10:44 -0700 (PDT) Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id n371xGuUCHy7; Thu, 9 May 2019 09:10:42 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 5D4BC1619A6; Thu, 9 May 2019 09:10:42 -0700 (PDT) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id OFxQxXs_wVy2; Thu, 9 May 2019 09:10:42 -0700 (PDT) Received: from Penguin.CS.UCLA.EDU (Penguin.CS.UCLA.EDU [131.179.64.200]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id 357DD1618EE; Thu, 9 May 2019 09:10:42 -0700 (PDT) From: Paul Eggert To: bug-gnulib@gnu.org Subject: [PATCH 2/3] Support C2X and C++17 static_assert Date: Thu, 9 May 2019 09:10:33 -0700 Message-Id: <20190509161034.14248-2-eggert@cs.ucla.edu> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190509161034.14248-1-eggert@cs.ucla.edu> References: <20190509161034.14248-1-eggert@cs.ucla.edu> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 131.179.128.68 X-BeenThere: bug-gnulib@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Gnulib discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paul Eggert Errors-To: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org Sender: "bug-gnulib" C2X and C++17 finally added support for a simple, single-argument =E2=80=98static_assert=E2=80=99 that implements what the Gnulib =E2=80=98= verify=E2=80=99 macro was doing back in 2005. Implement static_assert on older platforms. The only remaining advantage of =E2=80=98verify=E2=80=99 is a shorter nam= e. * 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=E2=80=99t bother trying to copy the expression into the diagnostic, since 1-argument static_assert doesn=E2=80=99t. (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 + + Support C2X and C++17 static_assert + C2X and C++17 finally added support for a simple, single-argument + =E2=80=98static_assert=E2=80=99 that implements what the Gnulib =E2=80=98= verify=E2=80=99 macro was + doing back in 2005. Implement static_assert on older platforms. + The only remaining advantage of =E2=80=98verify=E2=80=99 is a shorter n= ame. + * 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=E2=80=99t bother trying to copy the expressi= on + into the diagnostic, since 1-argument static_assert doesn=E2=80=99t. + (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 =20 Fix _GL_HAVE__STATIC_ASSERT typo diff --git a/doc/posix-headers/assert.texi b/doc/posix-headers/assert.tex= i 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/on= linepubs/9699919799/based =20 Gnulib module: assert-h =20 -See also the Gnulib module @code{assert}. +See also the Gnulib modules @code{assert} and @code{verify}. =20 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_asse= rt}, -and G++ versions through at least 4.6.0 do not support @code{static_asse= rt}. +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 =20 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{}. The Gnulib substitutes are macros that require including @code{}. @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 re= ject a usage like @samp{verify (@var{V});} even when @var{V} is nonzero. =20 -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. =20 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 =20 =20 -/* Define _GL_HAVE__STATIC_ASSERT to 1 if _Static_assert works as per C1= 1. - 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++1= 1. - 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 <=3D __GNUC_MINOR__) \ - && (201112L <=3D __STDC_VERSION__ || !defined __STRICT_ANSI__) \ - && !defined __cplusplus) -# define _GL_HAVE__STATIC_ASSERT 1 -#endif -#if 6 <=3D __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 <=3D __STDC_VERSION__ \ + || (!defined __STRICT_ANSI__ && 4 < __GNUC__ + (6 <=3D __GNUC_MINO= R__))) +# define _GL_HAVE__STATIC_ASSERT 1 +# endif +# if (202000L <=3D __STDC_VERSION__ \ + || (!defined __STRICT_ANSI__ && 9 <=3D __GNUC__)) +# define _GL_HAVE__STATIC_ASSERT1 1 +# endif +#else +# if 201703L <=3D __cplusplus || 9 <=3D __GNUC__ +# define _GL_HAVE_STATIC_ASSERT1 1 +# endif #endif =20 /* FreeBSD 9.1 , included by and lots of other @@ -167,11 +175,9 @@ #define _GL_GENSYM(prefix) _GL_CONCAT (prefix, _GL_COUNTER) =20 /* Verify requirement R at compile-time, as an integer constant expressi= on - 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. */ =20 -#define _GL_VERIFY_TRUE(R, DIAGNOSTIC) \ - (!!sizeof (_GL_VERIFY_TYPE (R, DIAGNOSTIC))) +#define _GL_VERIFY_TRUE(R) (!!sizeof (_GL_VERIFY_TYPE (R))) =20 #ifdef __cplusplus # if !GNULIB_defined_struct__gl_verify_type @@ -181,40 +187,43 @@ template }; # 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 =20 /* 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 firs= t + two, so that the _Static_assert macro optionally defined below suppor= ts + both the C11 two-argument syntax and the C2X one-argument syntax. =20 Unfortunately, unlike C11, this implementation must appear as an ordinary declaration, and cannot appear inside struct { ... }. */ =20 -#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 =20 /* _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 =20 verify_true is obsolescent; please use verify_expr instead. */ =20 -#define verify_true(R) _GL_VERIFY_TRUE (R, "verify_true (" #R ")") +#define verify_true(R) _GL_VERIFY_TRUE (R) =20 /* Verify requirement R at compile-time. Return the value of the expression E. */ =20 -#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)) =20 /* 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. */ =20 -#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 =20 #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 static_assert (2 + 2 =3D=3D 4, "arithmetic doesn't work"); + static_assert (2 + 2 =3D=3D 4); ]], [[ static_assert (sizeof (char) =3D=3D 1, "sizeof doesn't work"= ); + static_assert (sizeof (char) =3D=3D 1); ]])], [gl_cv_static_assert=3Dyes], [gl_cv_static_assert=3Dno])]) --=20 2.21.0