Add additional bounds check functions to cover most ABSL_HARDENING_ASSERTs.

Most current uses of ABSL_HARDENING_ASSERT are one of:
* numerical comparison
* checking that a container is non-empty
* checking that a pointer is non-null.

This change adds additional hardening assert functions which conduct the
comparisons necessary for these checks inside the body of the assertion
function, so that the cost of performing the check can be attributed
to the assertion.

For the smaller set of cases where more complex conditions are checked,
a general HardeningAssert which takes a bool is provided.

PiperOrigin-RevId: 899780652
Change-Id: I00602302a5d42d483053ad66d8c3e796d1708567
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index a0e71ed..98772b1 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -656,6 +656,7 @@
     linkopts = ABSL_DEFAULT_LINKOPTS,
     deps = [
         ":config",
+        ":core_headers",
         ":hardening",
         "@googletest//:gtest",
         "@googletest//:gtest_main",
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index ad1932e..582b14c 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -427,6 +427,7 @@
     ${ABSL_TEST_COPTS}
   DEPS
     absl::config
+    absl::core_headers
     absl::hardening
     GTest::gtest_main
 )
diff --git a/absl/base/internal/hardening.cc b/absl/base/internal/hardening.cc
index bd7da60..2844eec 100644
--- a/absl/base/internal/hardening.cc
+++ b/absl/base/internal/hardening.cc
@@ -15,10 +15,10 @@
 
 #ifdef _WIN32
 #include <intrin.h>
-// kFastFailRangeCheckFailure mirrors the FAST_FAIL_RANGE_CHECK_FAILURE macro.
-// Typically FAST_FAIL_RANGE_CHECK_FAILURE would be imported from winnt.h
+// kFastFailInvalidArg mirrors the FAST_FAIL_INVALID_ARG macro.
+// Typically FAST_FAIL_INVALID_ARG would be imported from winnt.h
 // but winnt.h pulls in other dependencies and introduces build failures.
-constexpr unsigned int kFastFailRangeCheckFailure = 8u;
+constexpr unsigned int kFastFailInvalidArg = 5u;
 #endif
 
 #include "absl/base/internal/hardening.h"
@@ -32,9 +32,9 @@
 
 namespace base_internal {
 
-[[noreturn]] ABSL_ATTRIBUTE_NOINLINE void FailedBoundsCheckAbort() {
+[[noreturn]] ABSL_ATTRIBUTE_NOINLINE void HardeningAbort() {
 #ifdef _WIN32
-  __fastfail(kFastFailRangeCheckFailure);
+  __fastfail(kFastFailInvalidArg);
 #else
   ABSL_INTERNAL_HARDENING_ABORT();
 #endif
diff --git a/absl/base/internal/hardening.h b/absl/base/internal/hardening.h
index 02207cc..31638a4 100644
--- a/absl/base/internal/hardening.h
+++ b/absl/base/internal/hardening.h
@@ -17,8 +17,8 @@
 // File: hardening.h
 // -----------------------------------------------------------------------------
 //
-// This header file defines macros and functions for checking bounds on
-// accesses to Abseil container types.
+// This header file defines macros and functions for performing Abseil
+// hardening checks and aborts.
 
 #ifndef ABSL_BASE_INTERNAL_HARDENING_H_
 #define ABSL_BASE_INTERNAL_HARDENING_H_
@@ -41,15 +41,101 @@
 
 namespace base_internal {
 
-[[noreturn]] ABSL_ATTRIBUTE_NOINLINE void FailedBoundsCheckAbort();
+[[noreturn]] ABSL_ATTRIBUTE_NOINLINE void HardeningAbort();
+
+// `HardeningAssert` performs runtime checks when Abseil Hardening is enabled,
+// even if `NDEBUG` is defined.
+//
+// When `NDEBUG` is not defined, `HardeningAssert`'s behavior is identical to
+// `ABSL_ASSERT`.
+//
+// Prefer a more specific assertion function over this more general one,
+// as assertion functions which perform the comparison themselves
+// can have the cost of the comparison attributed to them.
+inline void HardeningAssert(bool cond) {
+  ABSL_ASSERT(cond);
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
+  if (ABSL_PREDICT_FALSE(!cond)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
+#endif
+}
+
+// `HardeningAssertSlow` is used to perform runtime checks which are too
+// computationally expensive to enable widely by default.
+//
+// When `NDEBUG` is not defined, `HardeningAssertSlow`'s behavior is identical
+// to `ABSL_ASSERT`.
+inline void HardeningAssertSlow(bool cond) {
+  ABSL_ASSERT(cond);
+#if (ABSL_OPTION_HARDENED == 1) && defined(NDEBUG)
+  if (ABSL_PREDICT_FALSE(!cond)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
+#endif
+}
+
+template <typename T>
+inline void HardeningAssertGT(T val1, T val2) {
+  ABSL_ASSERT(val1 > val2);
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
+  if (!ABSL_PREDICT_TRUE(val1 > val2)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
+#endif
+}
+
+template <typename T>
+inline void HardeningAssertGE(T val1, T val2) {
+  ABSL_ASSERT(val1 >= val2);
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
+  if (!ABSL_PREDICT_TRUE(val1 >= val2)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
+#endif
+}
+
+template <typename T>
+inline void HardeningAssertLT(T val1, T val2) {
+  ABSL_ASSERT(val1 < val2);
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
+  if (!ABSL_PREDICT_TRUE(val1 < val2)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
+#endif
+}
+
+template <typename T>
+inline void HardeningAssertLE(T val1, T val2) {
+  ABSL_ASSERT(val1 <= val2);
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
+  if (!ABSL_PREDICT_TRUE(val1 <= val2)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
+#endif
+}
 
 inline void HardeningAssertInBounds(size_t index, size_t size) {
+  HardeningAssertLT(index, size);
+}
+
+template <typename T>
+inline void HardeningAssertNonEmpty(const T& container) {
+  ABSL_ASSERT(!container.empty());
 #if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
-  if (ABSL_PREDICT_FALSE(index >= size)) {
-    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE FailedBoundsCheckAbort();
+  if (ABSL_PREDICT_FALSE(container.empty())) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
   }
-#else
-  ABSL_ASSERT(index < size);
+#endif
+}
+
+template <typename T>
+inline void HardeningAssertNonNull(T ptr) {
+  ABSL_ASSERT(ptr != nullptr);
+#if (ABSL_OPTION_HARDENED == 1 || ABSL_OPTION_HARDENED == 2) && defined(NDEBUG)
+  if (ABSL_PREDICT_FALSE(ptr == nullptr)) {
+    ABSL_INTERNAL_ATTRIBUTE_NO_MERGE HardeningAbort();
+  }
 #endif
 }
 
diff --git a/absl/base/internal/hardening_test.cc b/absl/base/internal/hardening_test.cc
index 8666f29..cffa87c 100644
--- a/absl/base/internal/hardening_test.cc
+++ b/absl/base/internal/hardening_test.cc
@@ -14,21 +14,154 @@
 
 #include "absl/base/internal/hardening.h"
 
+#include <vector>
+
 #include "gtest/gtest.h"
+#include "absl/base/macros.h"
 #include "absl/base/options.h"
 
 namespace {
 
-TEST(BoundsCheckTest, HardeningAssertInBounds) {
+bool IsHardened() {
+  bool hardened = false;
+  ABSL_HARDENING_ASSERT([&hardened]() {
+      hardened = true;
+      return true;
+    }()
+  );
+  return hardened;
+}
+
+bool IsHardenedSlow() {
+  bool hardened = false;
+  ABSL_HARDENING_ASSERT_SLOW([&hardened]() {
+      hardened = true;
+      return true;
+    }()
+  );
+  return hardened;
+}
+
+TEST(HardeningTest, HardeningAssertSlow) {
+  absl::base_internal::HardeningAssertSlow(true);
+  if (!IsHardenedSlow()) {
+    absl::base_internal::HardeningAssertSlow(false);
+  }
+}
+
+TEST(HardeningDeathTest, HardeningAssertSlow) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardenedSlow()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    EXPECT_DEATH(absl::base_internal::HardeningAssertSlow(false), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertGT) {
+  absl::base_internal::HardeningAssertGT(1, 0);
+}
+
+TEST(HardeningDeathTest, HardeningAssertGT) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    EXPECT_DEATH(absl::base_internal::HardeningAssertGT(1, 1), "");
+    EXPECT_DEATH(absl::base_internal::HardeningAssertGT(0, 1), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertGE) {
+  absl::base_internal::HardeningAssertGE(1, 0);
+  absl::base_internal::HardeningAssertGE(1, 1);
+}
+
+TEST(HardeningDeathTest, HardeningAssertGE) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    EXPECT_DEATH(absl::base_internal::HardeningAssertGE(0, 1), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertLT) {
+  absl::base_internal::HardeningAssertLT(0, 1);
+}
+
+TEST(HardeningDeathTest, HardeningAssertLT) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    EXPECT_DEATH(absl::base_internal::HardeningAssertLT(1, 1), "");
+    EXPECT_DEATH(absl::base_internal::HardeningAssertLT(1, 0), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertLE) {
+  absl::base_internal::HardeningAssertLE(0, 1);
+  absl::base_internal::HardeningAssertLE(1, 1);
+}
+
+TEST(HardeningDeathTest, HardeningAssertLE) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    EXPECT_DEATH(absl::base_internal::HardeningAssertLE(1, 0), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertInBounds) {
   absl::base_internal::HardeningAssertInBounds(0, 10);
 }
 
-TEST(BoundsChecksDeathTest, HardeningAssertInBounds) {
-#if GTEST_HAS_DEATH_TEST && (!defined(NDEBUG) || (ABSL_OPTION_HARDENED == 1 || \
-                                                  ABSL_OPTION_HARDENED == 2))
-  // The underlying mechanism of termination varies, and may include SIGILL
-  // or SIGABRT.
-  EXPECT_DEATH(absl::base_internal::HardeningAssertInBounds(10, 10), "");
+TEST(HardeningDeathTest, HardeningAssertInBounds) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    EXPECT_DEATH(absl::base_internal::HardeningAssertInBounds(10, 10), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertNonEmpty) {
+  std::vector<int> v = {1};
+  absl::base_internal::HardeningAssertNonEmpty(v);
+}
+
+TEST(HardeningDeathTest, HardeningAssertNonEmpty) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    std::vector<int> v = {};
+    EXPECT_DEATH(absl::base_internal::HardeningAssertNonEmpty(v), "");
+  }
+#endif
+}
+
+TEST(HardeningTest, HardeningAssertNonNull) {
+  int x = 1;
+  absl::base_internal::HardeningAssertNonNull(&x);
+}
+
+TEST(HardeningDeathTest, HardeningAssertNonNull) {
+#if GTEST_HAS_DEATH_TEST
+  if (IsHardened()) {
+    // The underlying mechanism of termination varies, and may include SIGILL
+    // or SIGABRT.
+    int *x = nullptr;
+    EXPECT_DEATH(absl::base_internal::HardeningAssertNonNull(x), "");
+  }
 #endif
 }