]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Make VERIFY a variadic macro
authorJonathan Wakely <jwakely@redhat.com>
Tue, 1 Jul 2025 11:44:04 +0000 (12:44 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Mon, 7 Jul 2025 20:50:50 +0000 (21:50 +0100)
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 <tkaminsk@redhat.com>
libstdc++-v3/testsuite/ext/verify_neg.cc [new file with mode: 0644]
libstdc++-v3/testsuite/util/testsuite_hooks.h

diff --git a/libstdc++-v3/testsuite/ext/verify_neg.cc b/libstdc++-v3/testsuite/ext/verify_neg.cc
new file mode 100644 (file)
index 0000000..ce03374
--- /dev/null
@@ -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 }
+}
index faa01ba6abd83addea5b98e21dbbd18ac8427f6d..bf34fd121c1b23e0557d1aee706056ac78947302 100644 (file)
 # 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>