https://gcc.gnu.org/g:5ca5e1d54f237770ef5f32ff20c69711f720551c

commit r16-2077-g5ca5e1d54f237770ef5f32ff20c69711f720551c
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Tue Jul 1 12:44:04 2025 +0100

    libstdc++: Make VERIFY a variadic macro
    
    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.
    
    Reviewed-by: Tomasz KamiƄski <tkami...@redhat.com>

Diff:
---
 libstdc++-v3/testsuite/ext/verify_neg.cc      | 28 +++++++++++++++++++++++++++
 libstdc++-v3/testsuite/util/testsuite_hooks.h | 17 +++++++---------
 2 files changed, 35 insertions(+), 10 deletions(-)

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>

Reply via email to