Improve ABSL_ASSERT performance by guaranteeing it is optimized away under NDEBUG in C++20
Also fix the old definition by verifying that the condition is contextually convertible to bool.
The new C++20 definition reduces codegen bloat and guarantees fast performance while still retaining the expression (so that unused-variable warnings don't trigger).
As an example where this makes a difference, compare the following snippet under `-DABSL_INTERNAL_CPLUSPLUS_LANG=202002`: https://godbolt.org/z/hjf59n84v
```
#include <stdlib.h>
template<class T> struct S { S() { abort(); } static S const s; };
template<class T> S<T> const S<T>::s = {};
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
#define ABSL_ASSERT(expr) (decltype((expr) ? void() : void())())
#else
#define ABSL_ASSERT(expr) (false ? ((expr) ? void() : void()) : void())
#endif
void foo() { ABSL_ASSERT(((void)&S<int>::s, true)); }
```
We see that, in unoptimized builds, code is still generated with the old definition of `ABSL_ASSERT`. Moreover, even under optimizations (with `-O2`), the call to abort() still lingers with the old definition of `ABSL_ASSERT`.
Therefore the extra generated code can affect both compile- and run-time performance.
PiperOrigin-RevId: 681563573
Change-Id: I7fbfadcc7fd198e8e1daf14615c33687f6b23af7
diff --git a/absl/base/macros.h b/absl/base/macros.h
index ccc86ab..ff89944 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -82,8 +82,9 @@
// ABSL_ASSERT()
//
// In C++11, `assert` can't be used portably within constexpr functions.
+// `assert` also generates spurious unused-symbol warnings.
// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr
-// functions. Example:
+// functions, and maintains references to symbols. Example:
//
// constexpr double Divide(double a, double b) {
// return ABSL_ASSERT(b != 0), a / b;
@@ -92,8 +93,18 @@
// This macro is inspired by
// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
#if defined(NDEBUG)
-#define ABSL_ASSERT(expr) \
- (false ? static_cast<void>(expr) : static_cast<void>(0))
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+// We use `decltype` here to avoid generating unnecessary code that the
+// optimizer then has to optimize away.
+// This not only improves compilation performance by reducing codegen bloat
+// and optimization work, but also guarantees fast run-time performance without
+// having to rely on the optimizer.
+#define ABSL_ASSERT(expr) (decltype((expr) ? void() : void())())
+#else
+// Pre-C++20, lambdas can't be inside unevaluated operands, so we're forced to
+// rely on the optimizer.
+#define ABSL_ASSERT(expr) (false ? ((expr) ? void() : void()) : void())
+#endif
#else
#define ABSL_ASSERT(expr) \
(ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \