On Wed, Jul 2, 2025 at 12:31 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> This defines the testsuite assertion macro VERIFY so that it allows
> un-parenthesized expressions containing commas. This matches how assert
> is defined in C++26, following the approval of P2264R7.
>
> The primary motivation is to allow expressions that the preprocessor
> splits into multiple arguments, e.g.
> VERIFY( vec == std::vector<int>{1,2,3,4} );
>
> To achieve this, VERIFY is redefined as a variadic macro and then the
> arguments are grouped together again through the use of __VA_ARGS__.
>
> The implementation is complex due to the following points:
>
> - The arguments __VA_ARGS__ are contextually-converted to bool, so that
>   scoped enums and types that are not contextually convertible to bool
>   cannot be used with VERIFY.
> - bool(__VA_ARGS__) is used so that multiple arguments (i.e. those which
>   are separated by top-level commas) are ill-formed. Nested commas are
>   allowed, but likely mistakes such as VERIFY( cond, "some string" ) are
>   ill-formed.
> - The bool(__VA_ARGS__) expression needs to be unevaluated, so that we
>   don't evaluate __VA_ARGS__ more than once. The simplest way to do that
>   would be just sizeof bool(__VA_ARGS__), without parentheses to avoid a
>   vexing parse for VERIFY(bool(i)). However that wouldn't work for e.g.
>   VERIFY( []{ return true; }() ), because lambda expressions are not
>   allowed in unevaluated contexts until C++20. So we use another
>   conditional expression with bool(__VA_ARGS__) as the unevaluated
>   operand.
>
> libstdc++-v3/ChangeLog:
>
>         * testsuite/util/testsuite_hooks.h (VERIFY): Define as variadic
>         macro.
>         * testsuite/ext/verify_neg.cc: New test.
> ---
>
> Tested powerpc64le-linux.
>
LGTM. I have followed the discussion on the LWG reflector on it.

>
>  libstdc++-v3/testsuite/ext/verify_neg.cc      | 28 +++++++++++++++++++
>  libstdc++-v3/testsuite/util/testsuite_hooks.h | 17 +++++------
>  2 files changed, 35 insertions(+), 10 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/ext/verify_neg.cc
>
> diff --git a/libstdc++-v3/testsuite/ext/verify_neg.cc
> b/libstdc++-v3/testsuite/ext/verify_neg.cc
> new file mode 100644
> index 000000000000..ce033741beeb
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/ext/verify_neg.cc
> @@ -0,0 +1,28 @@
> +// { dg-do compile { target c++11 } }
> +
> +#include <testsuite_hooks.h>
> +
> +struct X { explicit operator void*() const { return nullptr; } };
> +
> +void
> +test_VERIFY(int i)
> +{
> +  // This should not be parsed as a function type bool(bool(i)):
> +  VERIFY( bool(i) );
> +
> +  // This should not produce warnings about lambda in unevaluated context:
> +  VERIFY( []{ return 1; }() );
> +
> +  // Only one expression allowed:
> +  VERIFY(1, 2); // { dg-error "in expansion of macro" }
> +  // { dg-error "compound expression in functional cast" "" { target
> *-*-* } 0 }
> +
> +  // A scoped enum is not contextually convertible to bool:
> +  enum class E { E0 };
> +  VERIFY( E::E0 ); // { dg-error "could not convert" }
> +
> +  // explicit conversion to void* is not contextually convertible to bool:
> +  X x;
> +  VERIFY( x ); // { dg-error "in expansion of macro" }
> +  // { dg-error "invalid cast .* to type 'bool'" "" { target *-*-* } 0 }
> +}
> diff --git a/libstdc++-v3/testsuite/util/testsuite_hooks.h
> b/libstdc++-v3/testsuite/util/testsuite_hooks.h
> index faa01ba6abd8..bf34fd121c1b 100644
> --- a/libstdc++-v3/testsuite/util/testsuite_hooks.h
> +++ b/libstdc++-v3/testsuite/util/testsuite_hooks.h
> @@ -58,16 +58,13 @@
>  # define _VERIFY_PRINT(S, F, L, P, C) __builtin_printf(S, F, L, P, C)
>  #endif
>
> -#define VERIFY(fn)                                                      \
> -  do                                                                    \
> -  {                                                                     \
> -    if (! (fn))
>       \
> -      {
>       \
> -       _VERIFY_PRINT("%s:%d: %s: Assertion '%s' failed.\n",            \
> -                     __FILE__, __LINE__, __PRETTY_FUNCTION__, #fn);    \
> -       __builtin_abort();                                              \
> -      }
>       \
> -  } while (false)
> +#define VERIFY(...)                                                    \
> +   ((void)((__VA_ARGS__)                                               \
> +            ? (void)(true ? true : bool(__VA_ARGS__))                  \
> +            : (_VERIFY_PRINT("%s:%d: %s: Assertion '%s' failed.\n",    \
> +                             __FILE__, __LINE__, __PRETTY_FUNCTION__,  \
> +                             #__VA_ARGS__),                            \
> +               __builtin_abort())))
>
>  #ifdef _GLIBCXX_HAVE_UNISTD_H
>  # include <unistd.h>
> --
> 2.50.0
>
>

Reply via email to