Skip to content

Commit 5ca5e1d

Browse files
committed
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 <[email protected]>
1 parent 9450fb7 commit 5ca5e1d

File tree

2 files changed

+35
-10
lines changed

2 files changed

+35
-10
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// { dg-do compile { target c++11 } }
2+
3+
#include <testsuite_hooks.h>
4+
5+
struct X { explicit operator void*() const { return nullptr; } };
6+
7+
void
8+
test_VERIFY(int i)
9+
{
10+
// This should not be parsed as a function type bool(bool(i)):
11+
VERIFY( bool(i) );
12+
13+
// This should not produce warnings about lambda in unevaluated context:
14+
VERIFY( []{ return 1; }() );
15+
16+
// Only one expression allowed:
17+
VERIFY(1, 2); // { dg-error "in expansion of macro" }
18+
// { dg-error "compound expression in functional cast" "" { target *-*-* } 0 }
19+
20+
// A scoped enum is not contextually convertible to bool:
21+
enum class E { E0 };
22+
VERIFY( E::E0 ); // { dg-error "could not convert" }
23+
24+
// explicit conversion to void* is not contextually convertible to bool:
25+
X x;
26+
VERIFY( x ); // { dg-error "in expansion of macro" }
27+
// { dg-error "invalid cast .* to type 'bool'" "" { target *-*-* } 0 }
28+
}

libstdc++-v3/testsuite/util/testsuite_hooks.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,13 @@
5858
# define _VERIFY_PRINT(S, F, L, P, C) __builtin_printf(S, F, L, P, C)
5959
#endif
6060

61-
#define VERIFY(fn) \
62-
do \
63-
{ \
64-
if (! (fn)) \
65-
{ \
66-
_VERIFY_PRINT("%s:%d: %s: Assertion '%s' failed.\n", \
67-
__FILE__, __LINE__, __PRETTY_FUNCTION__, #fn); \
68-
__builtin_abort(); \
69-
} \
70-
} while (false)
61+
#define VERIFY(...) \
62+
((void)((__VA_ARGS__) \
63+
? (void)(true ? true : bool(__VA_ARGS__)) \
64+
: (_VERIFY_PRINT("%s:%d: %s: Assertion '%s' failed.\n", \
65+
__FILE__, __LINE__, __PRETTY_FUNCTION__, \
66+
#__VA_ARGS__), \
67+
__builtin_abort())))
7168

7269
#ifdef _GLIBCXX_HAVE_UNISTD_H
7370
# include <unistd.h>

0 commit comments

Comments
 (0)