[libcxx-commits] [libcxx] [libc++] Create a `constexpr` hash to use during constant evaluation (PR #214946)

Vinay Deshmukh via libcxx-commits libcxx-commits at lists.llvm.org
Sun Oct 18 07:46:05 PDT 2026


https://github.com/vinay-deshmukh updated https://github.com/llvm/llvm-project/pull/214946

>From 817a1a5c598f182cee778de8822cad23a6264568 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 8 Aug 2026 10:33:03 +0100
Subject: [PATCH 01/38] Parameterize first hash test for different hash
 function

---
 .../unord.hash/integral.pass.cpp              | 138 +++++++++---------
 1 file changed, 71 insertions(+), 67 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index c798b3aa77ea55..0c09ec5fc9f0e3 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -30,17 +30,16 @@
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T>
-void
-test()
-{
+template <class T, template <typename> typename THash = std::hash >
+void test() {
 #if TEST_STD_VER >= 11
-    test_hash_disabled<const T>();
-    test_hash_disabled<volatile T>();
-    test_hash_disabled<const volatile T>();
+  test_hash_disabled<const T, THash<const T>>();
+  test_hash_disabled<volatile T, THash<volatile T>>();
+  test_hash_disabled<const volatile T, THash<const volatile T>>();
 #endif
 
-    typedef std::hash<T> H;
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -61,70 +60,75 @@ test()
     }
 }
 
-int main(int, char**)
-{
-    test<bool>();
-    test<char>();
-    test<signed char>();
-    test<unsigned char>();
-    test<char16_t>();
-    test<char32_t>();
+template <template <typename> typename THash >
+bool test_with_hash() {
+  test<bool, THash>();
+  test<char, THash>();
+  test<signed char, THash>();
+  test<unsigned char, THash>();
+  test<char16_t, THash>();
+  test<char32_t, THash>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-    test<wchar_t>();
+  test<wchar_t, THash>();
 #endif
-    test<short>();
-    test<unsigned short>();
-    test<int>();
-    test<unsigned int>();
-    test<long>();
-    test<unsigned long>();
-    test<long long>();
-    test<unsigned long long>();
-
-//  LWG #2119
-    test<std::ptrdiff_t>();
-    test<std::size_t>();
-
-    test<std::int8_t>();
-    test<std::int16_t>();
-    test<std::int32_t>();
-    test<std::int64_t>();
-
-    test<std::int_fast8_t>();
-    test<std::int_fast16_t>();
-    test<std::int_fast32_t>();
-    test<std::int_fast64_t>();
-
-    test<std::int_least8_t>();
-    test<std::int_least16_t>();
-    test<std::int_least32_t>();
-    test<std::int_least64_t>();
-
-    test<std::intmax_t>();
-    test<std::intptr_t>();
-
-    test<std::uint8_t>();
-    test<std::uint16_t>();
-    test<std::uint32_t>();
-    test<std::uint64_t>();
-
-    test<std::uint_fast8_t>();
-    test<std::uint_fast16_t>();
-    test<std::uint_fast32_t>();
-    test<std::uint_fast64_t>();
-
-    test<std::uint_least8_t>();
-    test<std::uint_least16_t>();
-    test<std::uint_least32_t>();
-    test<std::uint_least64_t>();
-
-    test<std::uintmax_t>();
-    test<std::uintptr_t>();
+  test<short, THash>();
+  test<unsigned short, THash>();
+  test<int, THash>();
+  test<unsigned int, THash>();
+  test<long, THash>();
+  test<unsigned long, THash>();
+  test<long long, THash>();
+  test<unsigned long long, THash>();
+
+  //  LWG #2119
+  test<std::ptrdiff_t, THash>();
+  test<std::size_t, THash>();
+
+  test<std::int8_t, THash>();
+  test<std::int16_t, THash>();
+  test<std::int32_t, THash>();
+  test<std::int64_t, THash>();
+
+  test<std::int_fast8_t, THash>();
+  test<std::int_fast16_t, THash>();
+  test<std::int_fast32_t, THash>();
+  test<std::int_fast64_t, THash>();
+
+  test<std::int_least8_t, THash>();
+  test<std::int_least16_t, THash>();
+  test<std::int_least32_t, THash>();
+  test<std::int_least64_t, THash>();
+
+  test<std::intmax_t, THash>();
+  test<std::intptr_t, THash>();
+
+  test<std::uint8_t, THash>();
+  test<std::uint16_t, THash>();
+  test<std::uint32_t, THash>();
+  test<std::uint64_t, THash>();
+
+  test<std::uint_fast8_t, THash>();
+  test<std::uint_fast16_t, THash>();
+  test<std::uint_fast32_t, THash>();
+  test<std::uint_fast64_t, THash>();
+
+  test<std::uint_least8_t, THash>();
+  test<std::uint_least16_t, THash>();
+  test<std::uint_least32_t, THash>();
+  test<std::uint_least64_t, THash>();
+
+  test<std::uintmax_t, THash>();
+  test<std::uintptr_t, THash>();
 
 #ifndef TEST_HAS_NO_INT128
-    test<__int128_t>();
-    test<__uint128_t>();
+  test<__int128_t, THash>();
+  test<__uint128_t, THash>();
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  assert(test_with_hash<std::hash>());
   return 0;
 }

>From 1b6383a715716d0ba9c787097311e24640433867 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 8 Aug 2026 10:43:07 +0100
Subject: [PATCH 02/38] Setup a failing test for std::__constexpr_hash

---
 .../function.objects/unord.hash/integral.pass.cpp   | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index 0c09ec5fc9f0e3..d4d9bb4fb5041b 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -128,7 +128,20 @@ bool test_with_hash() {
   return true;
 }
 
+// TODO: move to libcxx/include/__functional/hash.h
+namespace std {
+
+template <class _Key>
+struct __constexpr_hash {};
+
+} // namespace std
+
 int main(int, char**) {
   assert(test_with_hash<std::hash>());
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<std::__constexpr_hash>());
+#endif
+
   return 0;
 }

>From ce52820da7d6dfe85ff92cfad1b69bcf07fcb33d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 8 Aug 2026 14:15:15 +0100
Subject: [PATCH 03/38] Pass integer.pass.cpp

---
 .../unord.hash/integral.pass.cpp              | 50 +++++++++++++++++--
 libcxx/test/support/poisoned_hash_helper.h    |  2 +-
 2 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index d4d9bb4fb5041b..7bc1425cb291fe 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -31,7 +31,7 @@
 #endif
 
 template <class T, template <typename> typename THash = std::hash >
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
   test_hash_disabled<const T, THash<const T>>();
   test_hash_disabled<volatile T, THash<volatile T>>();
@@ -61,7 +61,7 @@ void test() {
 }
 
 template <template <typename> typename THash >
-bool test_with_hash() {
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
   test<bool, THash>();
   test<char, THash>();
   test<signed char, THash>();
@@ -128,13 +128,55 @@ bool test_with_hash() {
   return true;
 }
 
+#if TEST_STD_VER >= 26
 // TODO: move to libcxx/include/__functional/hash.h
 namespace std {
 
-template <class _Key>
-struct __constexpr_hash {};
+// TODO: use _ prefix
+template <typename _Tp>
+concept EnabledForHash = requires(_Tp t) {
+  { std::bool_constant<__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
+};
+
+template <typename _Tp>
+concept DisabledForHash = not EnabledForHash<_Tp>;
+
+// TODO: document the constraints of using this at runtime OR make it consteval only
+template <typename _Tp>
+struct __constexpr_hash;
+
+template <DisabledForHash _Tp>
+struct __constexpr_hash<_Tp> {
+  __constexpr_hash()                                   = delete;
+  __constexpr_hash(const __constexpr_hash&)            = delete;
+  __constexpr_hash& operator=(const __constexpr_hash&) = delete;
+};
+
+template <EnabledForHash _Tp>
+struct __constexpr_hash<_Tp> {
+  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const _Tp& __v) const noexcept {
+    if constexpr (std::is_same_v<_Tp, nullptr_t>) {
+      return 662607004ull;
+    } else if constexpr (std::is_integral_v<_Tp>) {
+      if constexpr (sizeof(_Tp) <= sizeof(size_t)) {
+        return static_cast<size_t>(__v);
+      } else {
+        constexpr size_t multiple = sizeof(_Tp) / sizeof(size_t);
+        char region[multiple];
+
+        // TODO: 0, 1, 2, 3, 4
+        return region[multiple - 1]; // TODO: hash-ing
+      }
+    }
+    __builtin_unreachable(); // todo: revisit
+  }
+
+  __constexpr_hash() noexcept                          = default;
+  __constexpr_hash& operator=(const __constexpr_hash&) = default;
+};
 
 } // namespace std
+#endif
 
 int main(int, char**) {
   assert(test_with_hash<std::hash>());
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index cd71cd70d6a840..ec3f6e9af813fd 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -87,7 +87,7 @@ TEST_CONSTEXPR_CXX20 void test_hash_enabled(Key const& key = Key{}) {
 
 // Test that the specified Hash meets the requirements of a disabled hash.
 template <class Key, class Hash = std::hash<Key>>
-void test_hash_disabled() {
+TEST_CONSTEXPR_CXX26 void test_hash_disabled() {
   // Disabled hash requirements
   static_assert(!std::is_default_constructible<Hash>::value, "");
   static_assert(!std::is_copy_constructible<Hash>::value, "");

>From ae2da4414f761421c0eee17cd97cbeae03ee6c2b Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 10 Aug 2026 08:43:19 +0100
Subject: [PATCH 04/38] -Wc++17 extensions

---
 .../std/utilities/function.objects/unord.hash/integral.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index 7bc1425cb291fe..4974916ed4c80e 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -30,7 +30,7 @@
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T, template <typename> typename THash = std::hash >
+template <class T, template <class> class THash = std::hash >
 TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
   test_hash_disabled<const T, THash<const T>>();

>From 190e96559aecedd698d270d0778508bbc0e83323 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 23 Aug 2026 18:03:46 +0100
Subject: [PATCH 05/38] Move to test header

---
 .../unord.hash/integral.pass.cpp              | 53 +----------------
 libcxx/test/support/constexpr_hash.h          | 57 +++++++++++++++++++
 2 files changed, 59 insertions(+), 51 deletions(-)
 create mode 100644 libcxx/test/support/constexpr_hash.h

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index 4974916ed4c80e..f2c217f10db96c 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -24,6 +24,7 @@
 #include <limits>
 #include <type_traits>
 
+#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
@@ -128,61 +129,11 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
   return true;
 }
 
-#if TEST_STD_VER >= 26
-// TODO: move to libcxx/include/__functional/hash.h
-namespace std {
-
-// TODO: use _ prefix
-template <typename _Tp>
-concept EnabledForHash = requires(_Tp t) {
-  { std::bool_constant<__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
-};
-
-template <typename _Tp>
-concept DisabledForHash = not EnabledForHash<_Tp>;
-
-// TODO: document the constraints of using this at runtime OR make it consteval only
-template <typename _Tp>
-struct __constexpr_hash;
-
-template <DisabledForHash _Tp>
-struct __constexpr_hash<_Tp> {
-  __constexpr_hash()                                   = delete;
-  __constexpr_hash(const __constexpr_hash&)            = delete;
-  __constexpr_hash& operator=(const __constexpr_hash&) = delete;
-};
-
-template <EnabledForHash _Tp>
-struct __constexpr_hash<_Tp> {
-  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const _Tp& __v) const noexcept {
-    if constexpr (std::is_same_v<_Tp, nullptr_t>) {
-      return 662607004ull;
-    } else if constexpr (std::is_integral_v<_Tp>) {
-      if constexpr (sizeof(_Tp) <= sizeof(size_t)) {
-        return static_cast<size_t>(__v);
-      } else {
-        constexpr size_t multiple = sizeof(_Tp) / sizeof(size_t);
-        char region[multiple];
-
-        // TODO: 0, 1, 2, 3, 4
-        return region[multiple - 1]; // TODO: hash-ing
-      }
-    }
-    __builtin_unreachable(); // todo: revisit
-  }
-
-  __constexpr_hash() noexcept                          = default;
-  __constexpr_hash& operator=(const __constexpr_hash&) = default;
-};
-
-} // namespace std
-#endif
-
 int main(int, char**) {
   assert(test_with_hash<std::hash>());
 
 #if TEST_STD_VER >= 26
-  static_assert(test_with_hash<std::__constexpr_hash>());
+  static_assert(test_with_hash<support::constexpr_hash>());
 #endif
 
   return 0;
diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
new file mode 100644
index 00000000000000..b7881f0c73df94
--- /dev/null
+++ b/libcxx/test/support/constexpr_hash.h
@@ -0,0 +1,57 @@
+
+
+#include <__type_traits/is_unqualified.h>
+
+#include "test_macros.h"
+#include <type_traits>
+
+namespace support {
+
+#if TEST_STD_VER >= 26
+
+// TODO: use _ prefix
+template <typename _Tp>
+concept EnabledForHash = requires(_Tp t) {
+  { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
+};
+
+template <typename _Tp>
+concept DisabledForHash = not EnabledForHash<_Tp>;
+
+// TODO: document the constraints of using this at runtime OR make it consteval only
+template <typename _Tp>
+struct constexpr_hash;
+
+template <DisabledForHash _Tp>
+struct constexpr_hash<_Tp> {
+  constexpr_hash()                                   = delete;
+  constexpr_hash(const constexpr_hash&)            = delete;
+  constexpr_hash& operator=(const constexpr_hash&) = delete;
+};
+
+template <EnabledForHash _Tp>
+struct constexpr_hash<_Tp> {
+  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const _Tp& __v) const noexcept {
+    if constexpr (std::is_same_v<_Tp, nullptr_t>) {
+      return 662607004ull;
+    } else if constexpr (std::is_integral_v<_Tp>) {
+      if constexpr (sizeof(_Tp) <= sizeof(size_t)) {
+        return static_cast<size_t>(__v);
+      } else {
+        constexpr size_t multiple = sizeof(_Tp) / sizeof(size_t);
+        char region[multiple];
+
+        // TODO: 0, 1, 2, 3, 4
+        return region[multiple - 1]; // TODO: hash-ing
+      }
+    }
+    __builtin_unreachable(); // todo: revisit
+  }
+
+  constexpr_hash() noexcept                          = default;
+  constexpr_hash& operator=(const constexpr_hash&) = default;
+};
+
+} // namespace support
+
+#endif

>From ae90cb4d7dbfd421d9af691293c35f596bad828b Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 23 Aug 2026 18:31:40 +0100
Subject: [PATCH 06/38] floating.pass.cpp

---
 .../unord.hash/floating.pass.cpp              | 32 ++++++++++++-------
 libcxx/test/support/constexpr_hash.h          | 20 ++++++++++++
 2 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
index f6c31d068b2317..c377aab5e869d5 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
@@ -25,23 +25,23 @@
 #include <limits>
 #include <cmath>
 
+#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T>
-void
-test()
-{
+template <class T, template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
-    test_hash_disabled<const T>();
-    test_hash_disabled<volatile T>();
-    test_hash_disabled<const volatile T>();
+  test_hash_disabled<const T, THash<const T>>();
+  test_hash_disabled<volatile T, THash<volatile T>>();
+  test_hash_disabled<const volatile T, THash<const volatile T>>();
 #endif
 
-    typedef std::hash<T> H;
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -78,11 +78,19 @@ test()
     assert(pinf != ninf);
 }
 
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test<float, THash>();
+  test<double, THash>();
+  test<long double, THash>();
+  return true;
+}
+
 int main(int, char**)
 {
-    test<float>();
-    test<double>();
-    test<long double>();
-
+  assert(test_with_hash<std::hash>());
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
   return 0;
 }
diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index b7881f0c73df94..e298500f60ad52 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -4,6 +4,7 @@
 
 #include "test_macros.h"
 #include <type_traits>
+#include <bit>
 
 namespace support {
 
@@ -44,6 +45,25 @@ struct constexpr_hash<_Tp> {
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
       }
+    } else if constexpr (std::is_floating_point_v<_Tp>) {
+    if (__v == 0.0f)
+      return 0;
+    // return __scalar_hash<_Tp>::operator()(__v);
+
+        constexpr size_t multiple = sizeof(_Tp) / sizeof(char);
+        std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(__v);
+
+        // TODO: pack indexing?
+        size_t res = 0;
+        for(size_t i = 0; i < multiple; ++i) {
+            res ^= region[i];
+        }
+        return res;
+
+        // TODO: 0, 1, 2, 3, 4
+        // return region[multiple - 1]; // TODO: hash-ing
+        // return 1234ULL;
+
     }
     __builtin_unreachable(); // todo: revisit
   }

>From 69d7d90dfa9e9690218e7efbb9b24a846fe056cf Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 24 Aug 2026 08:31:18 +0100
Subject: [PATCH 07/38] disable pointer types explicitly

---
 .../unord.hash/pointer.pass.cpp               | 30 +++++++++++++------
 libcxx/test/support/constexpr_hash.h          | 25 +++++++++++++---
 2 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp
index 784831018a0061..c6c2a6e805f2ba 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp
@@ -24,13 +24,13 @@
 #include <functional>
 #include <type_traits>
 
+#include "constexpr_hash.h"
 #include "test_macros.h"
 
-template <class T>
-void
-test()
-{
-    typedef std::hash<T> H;
+template <class T, template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test() {
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -44,9 +44,11 @@ test()
     assert(h(&i) != h(&j));
 }
 
-void test_nullptr() {
+template < template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test_nullptr() {
   typedef std::nullptr_t T;
-  typedef std::hash<T> H;
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
 #if TEST_STD_VER <= 17
   static_assert((std::is_same<typename H::argument_type, T>::value), "");
   static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -54,10 +56,20 @@ void test_nullptr() {
   ASSERT_NOEXCEPT(H()(T()));
 }
 
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test_nullptr< THash>();
+  return true;
+}
+
 int main(int, char**)
 {
-    test<int*>();
-    test_nullptr();
+  assert(test_with_hash<std::hash>());
+  test<int*>();
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index e298500f60ad52..0debee277ed0a5 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -14,10 +14,26 @@ namespace support {
 template <typename _Tp>
 concept EnabledForHash = requires(_Tp t) {
   { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
-};
+}
+
+// we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
+//  note: bit_cast from a pointer type is not allowed in a constant expression
+// read of member '__a' of union with active member '__t' is not allowed in a constant expression
+/*
+
+    union {
+      _Tp __t;
+      size_t __a;
+    } __u;
+    __u.__a = 0;
+    __u.__t = __v;
+    return __u.__a;
+*/
+and not std::is_pointer_v<_Tp>;
+
 
 template <typename _Tp>
-concept DisabledForHash = not EnabledForHash<_Tp>;
+concept DisabledForHash = not EnabledForHash<_Tp> ;
 
 // TODO: document the constraints of using this at runtime OR make it consteval only
 template <typename _Tp>
@@ -45,8 +61,9 @@ struct constexpr_hash<_Tp> {
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
       }
-    } else if constexpr (std::is_floating_point_v<_Tp>) {
+    } else if constexpr (std::is_floating_point_v<_Tp> ) {
     if (__v == 0.0f)
+
       return 0;
     // return __scalar_hash<_Tp>::operator()(__v);
 
@@ -64,7 +81,7 @@ struct constexpr_hash<_Tp> {
         // return region[multiple - 1]; // TODO: hash-ing
         // return 1234ULL;
 
-    }
+    } 
     __builtin_unreachable(); // todo: revisit
   }
 

>From e6378f30fff8c24a6233e7fde3d5dcf83ab1999d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 24 Aug 2026 08:48:13 +0100
Subject: [PATCH 08/38] enum.pass.cpp

---
 .../function.objects/unord.hash/enum.pass.cpp | 39 +++++++-----
 libcxx/test/support/constexpr_hash.h          | 60 ++++++++++---------
 2 files changed, 57 insertions(+), 42 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
index 05a19329f909a8..cdc982f12c638f 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
@@ -19,6 +19,7 @@
 #include <limits>
 #include <type_traits>
 
+#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
@@ -33,17 +34,16 @@ enum class EightBitColors : std::uint8_t { red, orange, yellow, green, blue, ind
 
 enum Fruits { apple, pear, grape, mango, cantaloupe };
 
-template <class T>
-void
-test()
-{
+template <class T, template <class> typename THash>
+TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
     test_hash_disabled<const T>();
     test_hash_disabled<volatile T>();
     test_hash_disabled<const volatile T>();
 #endif
 
-    typedef std::hash<T> H;
+    // typedef std::hash<T> H;
+    typedef THash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -52,7 +52,8 @@ test()
     typedef typename std::underlying_type<T>::type under_type;
 
     H h1;
-    std::hash<under_type> h2;
+    // std::hash<under_type> h2;
+    THash<under_type> h2;
     for (int i = 0; i <= 5; ++i)
     {
         T t(static_cast<T> (i));
@@ -62,16 +63,26 @@ test()
     }
 }
 
-int main(int, char**)
-{
-    test<Cardinals>();
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test<Cardinals, THash>();
 
-    test<Colors>();
-    test<ShortColors>();
-    test<LongColors>();
-    test<EightBitColors>();
+  test<Colors, THash>();
+  test<ShortColors, THash>();
+  test<LongColors, THash>();
+  test<EightBitColors, THash>();
 
-    test<Fruits>();
+  test<Fruits, THash>();
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test_with_hash<std::hash>());
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 0debee277ed0a5..8c79b15db87d60 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -1,6 +1,7 @@
 
 
 #include <__type_traits/is_unqualified.h>
+#include <__type_traits/underlying_type.h>
 
 #include "test_macros.h"
 #include <type_traits>
@@ -12,14 +13,15 @@ namespace support {
 
 // TODO: use _ prefix
 template <typename _Tp>
-concept EnabledForHash = requires(_Tp t) {
-  { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
-}
+concept EnabledForHash =
+    requires(_Tp t) {
+      { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
+    }
 
-// we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
-//  note: bit_cast from a pointer type is not allowed in a constant expression
-// read of member '__a' of union with active member '__t' is not allowed in a constant expression
-/*
+    // we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
+    //  note: bit_cast from a pointer type is not allowed in a constant expression
+    // read of member '__a' of union with active member '__t' is not allowed in a constant expression
+    /*
 
     union {
       _Tp __t;
@@ -29,11 +31,10 @@ concept EnabledForHash = requires(_Tp t) {
     __u.__t = __v;
     return __u.__a;
 */
-and not std::is_pointer_v<_Tp>;
-
+    and not std::is_pointer_v<_Tp>;
 
 template <typename _Tp>
-concept DisabledForHash = not EnabledForHash<_Tp> ;
+concept DisabledForHash = not EnabledForHash<_Tp>;
 
 // TODO: document the constraints of using this at runtime OR make it consteval only
 template <typename _Tp>
@@ -41,7 +42,7 @@ struct constexpr_hash;
 
 template <DisabledForHash _Tp>
 struct constexpr_hash<_Tp> {
-  constexpr_hash()                                   = delete;
+  constexpr_hash()                                 = delete;
   constexpr_hash(const constexpr_hash&)            = delete;
   constexpr_hash& operator=(const constexpr_hash&) = delete;
 };
@@ -61,31 +62,34 @@ struct constexpr_hash<_Tp> {
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
       }
-    } else if constexpr (std::is_floating_point_v<_Tp> ) {
-    if (__v == 0.0f)
+    } else if constexpr (std::is_floating_point_v<_Tp>) {
+      if (__v == 0.0f)
 
-      return 0;
-    // return __scalar_hash<_Tp>::operator()(__v);
+        return 0;
+      // return __scalar_hash<_Tp>::operator()(__v);
 
-        constexpr size_t multiple = sizeof(_Tp) / sizeof(char);
-        std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(__v);
+      constexpr size_t multiple         = sizeof(_Tp) / sizeof(char);
+      std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(__v);
 
-        // TODO: pack indexing?
-        size_t res = 0;
-        for(size_t i = 0; i < multiple; ++i) {
-            res ^= region[i];
-        }
-        return res;
+      // TODO: pack indexing?
+      size_t res = 0;
+      for (size_t i = 0; i < multiple; ++i) {
+        res ^= region[i];
+      }
+      return res;
 
-        // TODO: 0, 1, 2, 3, 4
-        // return region[multiple - 1]; // TODO: hash-ing
-        // return 1234ULL;
+      // TODO: 0, 1, 2, 3, 4
+      // return region[multiple - 1]; // TODO: hash-ing
+      // return 1234ULL;
 
-    } 
+    } else if constexpr (std::is_enum_v<_Tp>) {
+      using type = std::__underlying_type_t<_Tp>;
+      return constexpr_hash<type>()(static_cast<type>(__v));
+    }
     __builtin_unreachable(); // todo: revisit
   }
 
-  constexpr_hash() noexcept                          = default;
+  constexpr_hash() noexcept                        = default;
   constexpr_hash& operator=(const constexpr_hash&) = default;
 };
 

>From 89fc017527db8466fdf95f8983ee5bb2760a9e5d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 26 Aug 2026 08:54:26 +0100
Subject: [PATCH 09/38] non_enum passes

---
 .../unord.hash/non_enum.pass.cpp              | 30 ++++++---
 libcxx/test/support/constexpr_hash.h          | 62 +++++++++----------
 2 files changed, 53 insertions(+), 39 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp
index 94cc9d912f3a7a..8632e907f1caf5 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp
@@ -18,22 +18,36 @@
 #include <cassert>
 #include <type_traits>
 
+#include "constexpr_hash.h"
 #include "test_macros.h"
 
 struct X {};
 
-int main(int, char**)
-{
-    using H = std::hash<X>;
-    static_assert(!std::is_default_constructible<H>::value, "");
-    static_assert(!std::is_copy_constructible<H>::value, "");
-    static_assert(!std::is_move_constructible<H>::value, "");
-    static_assert(!std::is_copy_assignable<H>::value, "");
-    static_assert(!std::is_move_assignable<H>::value, "");
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  using H = THash<X>;
+
+  static_assert(!std::is_default_constructible<H>::value, "");
+  static_assert(!std::is_copy_constructible<H>::value, "");
+  static_assert(!std::is_move_constructible<H>::value, "");
+  static_assert(!std::is_copy_assignable<H>::value, "");
+  static_assert(!std::is_move_assignable<H>::value, "");
 #if TEST_STD_VER > 14
     static_assert(!std::is_invocable<H, X&>::value, "");
     static_assert(!std::is_invocable<H, X const&>::value, "");
 #endif
 
+    return true;
+}
+
+int main(int, char**) {
+  assert(test_with_hash<std::hash>());
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
+  // using H = std::hash<X>;
+  // using H = support::constexpr_hash<X>;
+
   return 0;
 }
diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 8c79b15db87d60..9281384e1f30b6 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -11,48 +11,41 @@ namespace support {
 
 #if TEST_STD_VER >= 26
 
-// TODO: use _ prefix
-template <typename _Tp>
-concept EnabledForHash =
-    requires(_Tp t) {
-      { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
-    }
-
-    // we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
-    //  note: bit_cast from a pointer type is not allowed in a constant expression
-    // read of member '__a' of union with active member '__t' is not allowed in a constant expression
-    /*
-
-    union {
-      _Tp __t;
-      size_t __a;
-    } __u;
-    __u.__a = 0;
-    __u.__t = __v;
-    return __u.__a;
-*/
-    and not std::is_pointer_v<_Tp>;
-
-template <typename _Tp>
-concept DisabledForHash = not EnabledForHash<_Tp>;
-
 // TODO: document the constraints of using this at runtime OR make it consteval only
 template <typename _Tp>
 struct constexpr_hash;
 
-template <DisabledForHash _Tp>
-struct constexpr_hash<_Tp> {
+// we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
+template <class _Tp>
+struct constexpr_hash<_Tp*> {
   constexpr_hash()                                 = delete;
   constexpr_hash(const constexpr_hash&)            = delete;
   constexpr_hash& operator=(const constexpr_hash&) = delete;
 };
 
-template <EnabledForHash _Tp>
+template <>
+struct constexpr_hash<std::nullptr_t> {
+  constexpr_hash() noexcept                        = default;
+  constexpr_hash& operator=(const constexpr_hash&) = default;
+
+  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(std::nullptr_t) const noexcept {
+    return 662607004ull;
+  }
+};
+
+template <typename _Tp>
+  requires requires(_Tp t) {
+    { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
+
+    {
+      std::bool_constant< std::bool_constant<std::is_integral_v<_Tp>>() ||
+                          std::bool_constant<std::is_floating_point_v<_Tp>>() ||
+                          std::bool_constant<std::is_enum_v<_Tp>>() >()
+    } -> std::same_as<std::true_type>;
+  }
 struct constexpr_hash<_Tp> {
   [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const _Tp& __v) const noexcept {
-    if constexpr (std::is_same_v<_Tp, nullptr_t>) {
-      return 662607004ull;
-    } else if constexpr (std::is_integral_v<_Tp>) {
+    if constexpr (std::is_integral_v<_Tp>) {
       if constexpr (sizeof(_Tp) <= sizeof(size_t)) {
         return static_cast<size_t>(__v);
       } else {
@@ -93,6 +86,13 @@ struct constexpr_hash<_Tp> {
   constexpr_hash& operator=(const constexpr_hash&) = default;
 };
 
+template <class _Tp>
+struct constexpr_hash {
+  constexpr_hash()                                 = delete;
+  constexpr_hash(const constexpr_hash&)            = delete;
+  constexpr_hash& operator=(const constexpr_hash&) = delete;
+};
+
 } // namespace support
 
 #endif

>From df2ad7abc83d6f4fbfc82b4f16a4fd924034d61d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Fri, 28 Aug 2026 09:08:45 +0100
Subject: [PATCH 10/38] enabled_hashes.pass.cpp

---
 .../unord.hash/enabled_hashes.pass.cpp        |  5 ++++
 libcxx/test/support/constexpr_hash.h          |  8 +++--
 libcxx/test/support/poisoned_hash_helper.h    | 30 +++++++++++++++++--
 3 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp
index 53d8ed788d866b..43afca3407174c 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp
@@ -18,9 +18,14 @@
 #include "poisoned_hash_helper.h"
 
 #include "test_macros.h"
+#include "constexpr_hash.h"
 
 int main(int, char**) {
   test_library_hash_specializations_available();
 
+#if TEST_STD_VER >= 26
+  static_assert(test_constexpr_hash_specializations_available());
+#endif
+
   return 0;
 }
diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 9281384e1f30b6..b3278ea28c3822 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -1,4 +1,6 @@
 
+#ifndef SUPPORT_CONSTEXPR_HASH_H
+#define SUPPORT_CONSTEXPR_HASH_H
 
 #include <__type_traits/is_unqualified.h>
 #include <__type_traits/underlying_type.h>
@@ -49,8 +51,8 @@ struct constexpr_hash<_Tp> {
       if constexpr (sizeof(_Tp) <= sizeof(size_t)) {
         return static_cast<size_t>(__v);
       } else {
-        constexpr size_t multiple = sizeof(_Tp) / sizeof(size_t);
-        char region[multiple];
+        constexpr size_t multiple           = sizeof(_Tp) / sizeof(size_t);
+        std::array<size_t, multiple> region = std::bit_cast<std::array<size_t, multiple>>(__v);
 
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
@@ -96,3 +98,5 @@ struct constexpr_hash {
 } // namespace support
 
 #endif
+
+#endif // SUPPORT_CONSTEXPR_HASH_H
\ No newline at end of file
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index ec3f6e9af813fd..e1cc82244200e1 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -16,6 +16,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "constexpr_hash.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
 
@@ -128,9 +129,9 @@ using LibraryHashTypes =
                          types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
 
 struct TestHashEnabled {
-  template <class T>
+  template <class Key>
   void operator()() const {
-    test_hash_enabled<T>();
+    test_hash_enabled<Key>();
   }
 };
 
@@ -141,4 +142,29 @@ void test_library_hash_specializations_available() {
   types::for_each(Types(), TestHashEnabled());
 }
 
+#if TEST_STD_VER >= 26
+
+struct TestConstexprHashEnabled {
+  template <class Key>
+  constexpr void operator()() const {
+    test_hash_enabled<Key, support::constexpr_hash<Key>>();
+  }
+};
+
+// We use the same types that std::hash uses, except for pointers since pointers can't be hashed during constant evaluation
+using SupportedConstexprHashTypes =
+    types::concatenate_t<types::arithmetic_types,
+                         types::type_list<Enum,
+                                          EnumClass, //void*, void const*, Class*,
+                                          std::nullptr_t>>;
+
+// Test that each of the library hash specializations for arithmetic types,
+// enum types, and pointer types are available and enabled.
+template <class Types = SupportedConstexprHashTypes>
+constexpr bool test_constexpr_hash_specializations_available() {
+  types::for_each(Types(), TestConstexprHashEnabled());
+  return true;
+}
+#endif
+
 #endif // SUPPORT_POISONED_HASH_HELPER_H

>From 0dac4df85918d1419287683ea87fe2cad6163f30 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 10 Oct 2026 12:02:16 +0100
Subject: [PATCH 11/38] header, remove doc, NL

---
 libcxx/test/support/constexpr_hash.h | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index b3278ea28c3822..d9b57cb4296624 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -1,3 +1,14 @@
+//===- constexpr_hash.h - Classes for representing statements -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines a constexpr `hash`
+//
+//===----------------------------------------------------------------------===//
 
 #ifndef SUPPORT_CONSTEXPR_HASH_H
 #define SUPPORT_CONSTEXPR_HASH_H
@@ -13,7 +24,6 @@ namespace support {
 
 #if TEST_STD_VER >= 26
 
-// TODO: document the constraints of using this at runtime OR make it consteval only
 template <typename _Tp>
 struct constexpr_hash;
 
@@ -99,4 +109,4 @@ struct constexpr_hash {
 
 #endif
 
-#endif // SUPPORT_CONSTEXPR_HASH_H
\ No newline at end of file
+#endif // SUPPORT_CONSTEXPR_HASH_H

>From 2551725347d4b740c420fec2d4f3bb56397fc57d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 10 Oct 2026 12:18:13 +0100
Subject: [PATCH 12/38] use std::to_underlying over __underlying_type_t

---
 libcxx/test/support/constexpr_hash.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index d9b57cb4296624..b42a7d9027ef3f 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -14,7 +14,7 @@
 #define SUPPORT_CONSTEXPR_HASH_H
 
 #include <__type_traits/is_unqualified.h>
-#include <__type_traits/underlying_type.h>
+#include <utility>
 
 #include "test_macros.h"
 #include <type_traits>
@@ -88,7 +88,7 @@ struct constexpr_hash<_Tp> {
       // return 1234ULL;
 
     } else if constexpr (std::is_enum_v<_Tp>) {
-      using type = std::__underlying_type_t<_Tp>;
+      using type = decltype(std::to_underlying(std::declval<_Tp>()));
       return constexpr_hash<type>()(static_cast<type>(__v));
     }
     __builtin_unreachable(); // todo: revisit

>From 4b6040300397bb5cf4cdb6bc8efc49d62444dc44 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 10 Oct 2026 12:22:51 +0100
Subject: [PATCH 13/38] use <cstddef>

---
 libcxx/test/support/constexpr_hash.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index b42a7d9027ef3f..4f0c9569dfb77a 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -19,6 +19,7 @@
 #include "test_macros.h"
 #include <type_traits>
 #include <bit>
+#include <cstddef>
 
 namespace support {
 

>From 80988eaa151e43ae1bdce76475528d9a48aad937 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 11 Oct 2026 13:06:04 +0100
Subject: [PATCH 14/38] Avoid __ugly names

---
 libcxx/test/support/constexpr_hash.h | 40 ++++++++++++++--------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 4f0c9569dfb77a..f1824de405a67b 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -25,12 +25,12 @@ namespace support {
 
 #if TEST_STD_VER >= 26
 
-template <typename _Tp>
+template <typename Key>
 struct constexpr_hash;
 
 // we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
-template <class _Tp>
-struct constexpr_hash<_Tp*> {
+template <class Key>
+struct constexpr_hash<Key*> {
   constexpr_hash()                                 = delete;
   constexpr_hash(const constexpr_hash&)            = delete;
   constexpr_hash& operator=(const constexpr_hash&) = delete;
@@ -46,35 +46,35 @@ struct constexpr_hash<std::nullptr_t> {
   }
 };
 
-template <typename _Tp>
-  requires requires(_Tp t) {
-    { std::bool_constant<std::__is_unqualified_v<_Tp>>() } -> std::same_as<std::true_type>;
+template <typename Key>
+  requires requires(Key t) {
+    { std::bool_constant<std::__is_unqualified_v<Key>>() } -> std::same_as<std::true_type>;
 
     {
-      std::bool_constant< std::bool_constant<std::is_integral_v<_Tp>>() ||
-                          std::bool_constant<std::is_floating_point_v<_Tp>>() ||
-                          std::bool_constant<std::is_enum_v<_Tp>>() >()
+      std::bool_constant< std::bool_constant<std::is_integral_v<Key>>() ||
+                          std::bool_constant<std::is_floating_point_v<Key>>() ||
+                          std::bool_constant<std::is_enum_v<Key>>() >()
     } -> std::same_as<std::true_type>;
   }
-struct constexpr_hash<_Tp> {
-  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const _Tp& __v) const noexcept {
-    if constexpr (std::is_integral_v<_Tp>) {
-      if constexpr (sizeof(_Tp) <= sizeof(size_t)) {
+struct constexpr_hash<Key> {
+  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& __v) const noexcept {
+    if constexpr (std::is_integral_v<Key>) {
+      if constexpr (sizeof(Key) <= sizeof(size_t)) {
         return static_cast<size_t>(__v);
       } else {
-        constexpr size_t multiple           = sizeof(_Tp) / sizeof(size_t);
+        constexpr size_t multiple           = sizeof(Key) / sizeof(size_t);
         std::array<size_t, multiple> region = std::bit_cast<std::array<size_t, multiple>>(__v);
 
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
       }
-    } else if constexpr (std::is_floating_point_v<_Tp>) {
+    } else if constexpr (std::is_floating_point_v<Key>) {
       if (__v == 0.0f)
 
         return 0;
-      // return __scalar_hash<_Tp>::operator()(__v);
+      // return __scalar_hash<Key>::operator()(__v);
 
-      constexpr size_t multiple         = sizeof(_Tp) / sizeof(char);
+      constexpr size_t multiple         = sizeof(Key) / sizeof(char);
       std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(__v);
 
       // TODO: pack indexing?
@@ -88,8 +88,8 @@ struct constexpr_hash<_Tp> {
       // return region[multiple - 1]; // TODO: hash-ing
       // return 1234ULL;
 
-    } else if constexpr (std::is_enum_v<_Tp>) {
-      using type = decltype(std::to_underlying(std::declval<_Tp>()));
+    } else if constexpr (std::is_enum_v<Key>) {
+      using type = decltype(std::to_underlying(std::declval<Key>()));
       return constexpr_hash<type>()(static_cast<type>(__v));
     }
     __builtin_unreachable(); // todo: revisit
@@ -99,7 +99,7 @@ struct constexpr_hash<_Tp> {
   constexpr_hash& operator=(const constexpr_hash&) = default;
 };
 
-template <class _Tp>
+template <class Key>
 struct constexpr_hash {
   constexpr_hash()                                 = delete;
   constexpr_hash(const constexpr_hash&)            = delete;

>From 933ac6376214e4c2da3527ea5fcec7f1c92f47fa Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 11 Oct 2026 13:06:36 +0100
Subject: [PATCH 15/38] [[nodiscard]]

---
 libcxx/test/support/constexpr_hash.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index f1824de405a67b..c2baef0b9399bd 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -41,7 +41,7 @@ struct constexpr_hash<std::nullptr_t> {
   constexpr_hash() noexcept                        = default;
   constexpr_hash& operator=(const constexpr_hash&) = default;
 
-  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(std::nullptr_t) const noexcept {
+  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(std::nullptr_t) const noexcept {
     return 662607004ull;
   }
 };
@@ -57,7 +57,7 @@ template <typename Key>
     } -> std::same_as<std::true_type>;
   }
 struct constexpr_hash<Key> {
-  [[__nodiscard__]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& __v) const noexcept {
+  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& __v) const noexcept {
     if constexpr (std::is_integral_v<Key>) {
       if constexpr (sizeof(Key) <= sizeof(size_t)) {
         return static_cast<size_t>(__v);

>From bbfa6908c282014b31ef2bce70794b3a17d02fc2 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 11 Oct 2026 13:16:09 +0100
Subject: [PATCH 16/38] move primary template up to avoid forward decl

---
 libcxx/test/support/constexpr_hash.h | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index c2baef0b9399bd..21d8a517eda1d8 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -25,8 +25,12 @@ namespace support {
 
 #if TEST_STD_VER >= 26
 
-template <typename Key>
-struct constexpr_hash;
+template <class Key>
+struct constexpr_hash {
+  constexpr_hash()                                 = delete;
+  constexpr_hash(const constexpr_hash&)            = delete;
+  constexpr_hash& operator=(const constexpr_hash&) = delete;
+};
 
 // we need to disable hashing for `pointer` types, since we can't cast a pointer to a fixed width integer during constant evaluation via std::bit_cast, std::memcpy or union type-aliasing
 template <class Key>
@@ -99,13 +103,6 @@ struct constexpr_hash<Key> {
   constexpr_hash& operator=(const constexpr_hash&) = default;
 };
 
-template <class Key>
-struct constexpr_hash {
-  constexpr_hash()                                 = delete;
-  constexpr_hash(const constexpr_hash&)            = delete;
-  constexpr_hash& operator=(const constexpr_hash&) = delete;
-};
-
 } // namespace support
 
 #endif

>From 7e6ea831e463a0e0efcb51d7508b02219c270061 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 11 Oct 2026 13:19:24 +0100
Subject: [PATCH 17/38] key over __v

---
 libcxx/test/support/constexpr_hash.h | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 21d8a517eda1d8..9fe5efe2b51dd0 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -61,25 +61,25 @@ template <typename Key>
     } -> std::same_as<std::true_type>;
   }
 struct constexpr_hash<Key> {
-  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& __v) const noexcept {
+  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& key) const noexcept {
     if constexpr (std::is_integral_v<Key>) {
       if constexpr (sizeof(Key) <= sizeof(size_t)) {
-        return static_cast<size_t>(__v);
+        return static_cast<size_t>(key);
       } else {
         constexpr size_t multiple           = sizeof(Key) / sizeof(size_t);
-        std::array<size_t, multiple> region = std::bit_cast<std::array<size_t, multiple>>(__v);
+        std::array<size_t, multiple> region = std::bit_cast<std::array<size_t, multiple>>(key);
 
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
       }
     } else if constexpr (std::is_floating_point_v<Key>) {
-      if (__v == 0.0f)
+      if (key == 0.0f)
 
         return 0;
-      // return __scalar_hash<Key>::operator()(__v);
+      // return __scalar_hash<Key>::operator()(key);
 
       constexpr size_t multiple         = sizeof(Key) / sizeof(char);
-      std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(__v);
+      std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(key);
 
       // TODO: pack indexing?
       size_t res = 0;
@@ -94,7 +94,7 @@ struct constexpr_hash<Key> {
 
     } else if constexpr (std::is_enum_v<Key>) {
       using type = decltype(std::to_underlying(std::declval<Key>()));
-      return constexpr_hash<type>()(static_cast<type>(__v));
+      return constexpr_hash<type>()(static_cast<type>(key));
     }
     __builtin_unreachable(); // todo: revisit
   }

>From cec5662c5a4e8090d6421d76407f8955dc5270e5 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 19:00:57 +0100
Subject: [PATCH 18/38] use simpler concept

---
 libcxx/test/support/constexpr_hash.h | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 9fe5efe2b51dd0..922acb140ffa66 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -13,11 +13,11 @@
 #ifndef SUPPORT_CONSTEXPR_HASH_H
 #define SUPPORT_CONSTEXPR_HASH_H
 
-#include <__type_traits/is_unqualified.h>
 #include <utility>
 
 #include "test_macros.h"
 #include <type_traits>
+#include <concepts>
 #include <bit>
 #include <cstddef>
 
@@ -51,18 +51,14 @@ struct constexpr_hash<std::nullptr_t> {
 };
 
 template <typename Key>
-  requires requires(Key t) {
-    { std::bool_constant<std::__is_unqualified_v<Key>>() } -> std::same_as<std::true_type>;
-
-    {
-      std::bool_constant< std::bool_constant<std::is_integral_v<Key>>() ||
-                          std::bool_constant<std::is_floating_point_v<Key>>() ||
-                          std::bool_constant<std::is_enum_v<Key>>() >()
-    } -> std::same_as<std::true_type>;
-  }
+concept UnqualifiedArithmeticOrEnum =
+    std::is_same_v<Key, std::remove_cvref_t<Key>> &&
+    (std::integral<Key> || std::floating_point<Key> || std::is_enum_v<Key>);
+
+template <UnqualifiedArithmeticOrEnum Key>
 struct constexpr_hash<Key> {
   [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& key) const noexcept {
-    if constexpr (std::is_integral_v<Key>) {
+    if constexpr (std::integral<Key>) {
       if constexpr (sizeof(Key) <= sizeof(size_t)) {
         return static_cast<size_t>(key);
       } else {
@@ -72,7 +68,7 @@ struct constexpr_hash<Key> {
         // TODO: 0, 1, 2, 3, 4
         return region[multiple - 1]; // TODO: hash-ing
       }
-    } else if constexpr (std::is_floating_point_v<Key>) {
+    } else if constexpr (std::floating_point<Key>) {
       if (key == 0.0f)
 
         return 0;

>From 5262c7d43d55e4fcf3c09482e18c918cbbea1f30 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 19:15:23 +0100
Subject: [PATCH 19/38] std::unreachable

---
 libcxx/test/support/constexpr_hash.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 922acb140ffa66..b303152ec5ccef 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -92,7 +92,7 @@ struct constexpr_hash<Key> {
       using type = decltype(std::to_underlying(std::declval<Key>()));
       return constexpr_hash<type>()(static_cast<type>(key));
     }
-    __builtin_unreachable(); // todo: revisit
+    std::unreachable(); // todo: revisit
   }
 
   constexpr_hash() noexcept                        = default;

>From 16e829b6b67db49d479e1bfc95ceef17633592d8 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 20:26:29 +0100
Subject: [PATCH 20/38] 2 copies

---
 .../unord.hash/enabled_hashes.pass.cpp        |   5 -
 .../function.objects/unord.hash/enum.pass.cpp |  39 ++---
 .../unord.hash/floating.pass.cpp              |  32 ++--
 .../unord.hash/integral.pass.cpp              | 144 ++++++++----------
 .../unord.hash/non_enum.pass.cpp              |  30 +---
 .../unord.hash/pointer.pass.cpp               |  30 ++--
 .../constexpr_hash/enabled_hashes.pass.cpp    |  31 ++++
 .../constexpr_hash/enum.compile.fail.cpp      |  25 +++
 .../test.support/constexpr_hash/enum.pass.cpp |  88 +++++++++++
 .../constexpr_hash/floating.pass.cpp          |  96 ++++++++++++
 .../constexpr_hash/integral.pass.cpp          | 140 +++++++++++++++++
 .../constexpr_hash/non_enum.pass.cpp          |  53 +++++++
 .../constexpr_hash/pointer.pass.cpp           |  75 +++++++++
 13 files changed, 618 insertions(+), 170 deletions(-)
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp
index 43afca3407174c..53d8ed788d866b 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enabled_hashes.pass.cpp
@@ -18,14 +18,9 @@
 #include "poisoned_hash_helper.h"
 
 #include "test_macros.h"
-#include "constexpr_hash.h"
 
 int main(int, char**) {
   test_library_hash_specializations_available();
 
-#if TEST_STD_VER >= 26
-  static_assert(test_constexpr_hash_specializations_available());
-#endif
-
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
index cdc982f12c638f..05a19329f909a8 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enum.pass.cpp
@@ -19,7 +19,6 @@
 #include <limits>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
@@ -34,16 +33,17 @@ enum class EightBitColors : std::uint8_t { red, orange, yellow, green, blue, ind
 
 enum Fruits { apple, pear, grape, mango, cantaloupe };
 
-template <class T, template <class> typename THash>
-TEST_CONSTEXPR_CXX26 void test() {
+template <class T>
+void
+test()
+{
 #if TEST_STD_VER >= 11
     test_hash_disabled<const T>();
     test_hash_disabled<volatile T>();
     test_hash_disabled<const volatile T>();
 #endif
 
-    // typedef std::hash<T> H;
-    typedef THash<T> H;
+    typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -52,8 +52,7 @@ TEST_CONSTEXPR_CXX26 void test() {
     typedef typename std::underlying_type<T>::type under_type;
 
     H h1;
-    // std::hash<under_type> h2;
-    THash<under_type> h2;
+    std::hash<under_type> h2;
     for (int i = 0; i <= 5; ++i)
     {
         T t(static_cast<T> (i));
@@ -63,26 +62,16 @@ TEST_CONSTEXPR_CXX26 void test() {
     }
 }
 
-template <template <typename> typename THash >
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test<Cardinals, THash>();
+int main(int, char**)
+{
+    test<Cardinals>();
 
-  test<Colors, THash>();
-  test<ShortColors, THash>();
-  test<LongColors, THash>();
-  test<EightBitColors, THash>();
+    test<Colors>();
+    test<ShortColors>();
+    test<LongColors>();
+    test<EightBitColors>();
 
-  test<Fruits, THash>();
-
-  return true;
-}
-
-int main(int, char**) {
-  assert(test_with_hash<std::hash>());
-
-#if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
-#endif
+    test<Fruits>();
 
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
index c377aab5e869d5..f6c31d068b2317 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/floating.pass.cpp
@@ -25,23 +25,23 @@
 #include <limits>
 #include <cmath>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T, template <class> class THash = std::hash >
-TEST_CONSTEXPR_CXX26 void test() {
+template <class T>
+void
+test()
+{
 #if TEST_STD_VER >= 11
-  test_hash_disabled<const T, THash<const T>>();
-  test_hash_disabled<volatile T, THash<volatile T>>();
-  test_hash_disabled<const volatile T, THash<const volatile T>>();
+    test_hash_disabled<const T>();
+    test_hash_disabled<volatile T>();
+    test_hash_disabled<const volatile T>();
 #endif
 
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+    typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -78,19 +78,11 @@ TEST_CONSTEXPR_CXX26 void test() {
     assert(pinf != ninf);
 }
 
-template <template <typename> typename THash >
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test<float, THash>();
-  test<double, THash>();
-  test<long double, THash>();
-  return true;
-}
-
 int main(int, char**)
 {
-  assert(test_with_hash<std::hash>());
-#if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
-#endif
+    test<float>();
+    test<double>();
+    test<long double>();
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
index f2c217f10db96c..c798b3aa77ea55 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/integral.pass.cpp
@@ -24,23 +24,23 @@
 #include <limits>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T, template <class> class THash = std::hash >
-TEST_CONSTEXPR_CXX26 void test() {
+template <class T>
+void
+test()
+{
 #if TEST_STD_VER >= 11
-  test_hash_disabled<const T, THash<const T>>();
-  test_hash_disabled<volatile T, THash<volatile T>>();
-  test_hash_disabled<const volatile T, THash<const volatile T>>();
+    test_hash_disabled<const T>();
+    test_hash_disabled<volatile T>();
+    test_hash_disabled<const volatile T>();
 #endif
 
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+    typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -61,79 +61,69 @@ TEST_CONSTEXPR_CXX26 void test() {
     }
 }
 
-template <template <typename> typename THash >
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test<bool, THash>();
-  test<char, THash>();
-  test<signed char, THash>();
-  test<unsigned char, THash>();
-  test<char16_t, THash>();
-  test<char32_t, THash>();
+int main(int, char**)
+{
+    test<bool>();
+    test<char>();
+    test<signed char>();
+    test<unsigned char>();
+    test<char16_t>();
+    test<char32_t>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t, THash>();
+    test<wchar_t>();
 #endif
-  test<short, THash>();
-  test<unsigned short, THash>();
-  test<int, THash>();
-  test<unsigned int, THash>();
-  test<long, THash>();
-  test<unsigned long, THash>();
-  test<long long, THash>();
-  test<unsigned long long, THash>();
-
-  //  LWG #2119
-  test<std::ptrdiff_t, THash>();
-  test<std::size_t, THash>();
-
-  test<std::int8_t, THash>();
-  test<std::int16_t, THash>();
-  test<std::int32_t, THash>();
-  test<std::int64_t, THash>();
-
-  test<std::int_fast8_t, THash>();
-  test<std::int_fast16_t, THash>();
-  test<std::int_fast32_t, THash>();
-  test<std::int_fast64_t, THash>();
-
-  test<std::int_least8_t, THash>();
-  test<std::int_least16_t, THash>();
-  test<std::int_least32_t, THash>();
-  test<std::int_least64_t, THash>();
-
-  test<std::intmax_t, THash>();
-  test<std::intptr_t, THash>();
-
-  test<std::uint8_t, THash>();
-  test<std::uint16_t, THash>();
-  test<std::uint32_t, THash>();
-  test<std::uint64_t, THash>();
-
-  test<std::uint_fast8_t, THash>();
-  test<std::uint_fast16_t, THash>();
-  test<std::uint_fast32_t, THash>();
-  test<std::uint_fast64_t, THash>();
-
-  test<std::uint_least8_t, THash>();
-  test<std::uint_least16_t, THash>();
-  test<std::uint_least32_t, THash>();
-  test<std::uint_least64_t, THash>();
-
-  test<std::uintmax_t, THash>();
-  test<std::uintptr_t, THash>();
+    test<short>();
+    test<unsigned short>();
+    test<int>();
+    test<unsigned int>();
+    test<long>();
+    test<unsigned long>();
+    test<long long>();
+    test<unsigned long long>();
+
+//  LWG #2119
+    test<std::ptrdiff_t>();
+    test<std::size_t>();
+
+    test<std::int8_t>();
+    test<std::int16_t>();
+    test<std::int32_t>();
+    test<std::int64_t>();
+
+    test<std::int_fast8_t>();
+    test<std::int_fast16_t>();
+    test<std::int_fast32_t>();
+    test<std::int_fast64_t>();
+
+    test<std::int_least8_t>();
+    test<std::int_least16_t>();
+    test<std::int_least32_t>();
+    test<std::int_least64_t>();
+
+    test<std::intmax_t>();
+    test<std::intptr_t>();
+
+    test<std::uint8_t>();
+    test<std::uint16_t>();
+    test<std::uint32_t>();
+    test<std::uint64_t>();
+
+    test<std::uint_fast8_t>();
+    test<std::uint_fast16_t>();
+    test<std::uint_fast32_t>();
+    test<std::uint_fast64_t>();
+
+    test<std::uint_least8_t>();
+    test<std::uint_least16_t>();
+    test<std::uint_least32_t>();
+    test<std::uint_least64_t>();
+
+    test<std::uintmax_t>();
+    test<std::uintptr_t>();
 
 #ifndef TEST_HAS_NO_INT128
-  test<__int128_t, THash>();
-  test<__uint128_t, THash>();
-#endif
-
-  return true;
-}
-
-int main(int, char**) {
-  assert(test_with_hash<std::hash>());
-
-#if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
+    test<__int128_t>();
+    test<__uint128_t>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp
index 8632e907f1caf5..94cc9d912f3a7a 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/non_enum.pass.cpp
@@ -18,36 +18,22 @@
 #include <cassert>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 struct X {};
 
-template <template <typename> typename THash >
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  using H = THash<X>;
-
-  static_assert(!std::is_default_constructible<H>::value, "");
-  static_assert(!std::is_copy_constructible<H>::value, "");
-  static_assert(!std::is_move_constructible<H>::value, "");
-  static_assert(!std::is_copy_assignable<H>::value, "");
-  static_assert(!std::is_move_assignable<H>::value, "");
+int main(int, char**)
+{
+    using H = std::hash<X>;
+    static_assert(!std::is_default_constructible<H>::value, "");
+    static_assert(!std::is_copy_constructible<H>::value, "");
+    static_assert(!std::is_move_constructible<H>::value, "");
+    static_assert(!std::is_copy_assignable<H>::value, "");
+    static_assert(!std::is_move_assignable<H>::value, "");
 #if TEST_STD_VER > 14
     static_assert(!std::is_invocable<H, X&>::value, "");
     static_assert(!std::is_invocable<H, X const&>::value, "");
 #endif
 
-    return true;
-}
-
-int main(int, char**) {
-  assert(test_with_hash<std::hash>());
-
-#if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
-#endif
-  // using H = std::hash<X>;
-  // using H = support::constexpr_hash<X>;
-
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp
index c6c2a6e805f2ba..784831018a0061 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/pointer.pass.cpp
@@ -24,13 +24,13 @@
 #include <functional>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
-template <class T, template <class> class THash = std::hash >
-TEST_CONSTEXPR_CXX26 void test() {
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+template <class T>
+void
+test()
+{
+    typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
     static_assert((std::is_same<typename H::argument_type, T>::value), "");
     static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -44,11 +44,9 @@ TEST_CONSTEXPR_CXX26 void test() {
     assert(h(&i) != h(&j));
 }
 
-template < template <class> class THash = std::hash >
-TEST_CONSTEXPR_CXX26 void test_nullptr() {
+void test_nullptr() {
   typedef std::nullptr_t T;
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+  typedef std::hash<T> H;
 #if TEST_STD_VER <= 17
   static_assert((std::is_same<typename H::argument_type, T>::value), "");
   static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -56,20 +54,10 @@ TEST_CONSTEXPR_CXX26 void test_nullptr() {
   ASSERT_NOEXCEPT(H()(T()));
 }
 
-template <template <typename> typename THash >
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test_nullptr< THash>();
-  return true;
-}
-
 int main(int, char**)
 {
-  assert(test_with_hash<std::hash>());
-  test<int*>();
-
-#if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
-#endif
+    test<int*>();
+    test_nullptr();
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
new file mode 100644
index 00000000000000..43afca3407174c
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <functional>
+
+// Test that <functional> provides all of the arithmetic, enum, and pointer
+// hash specializations.
+
+#include <functional>
+
+#include "poisoned_hash_helper.h"
+
+#include "test_macros.h"
+#include "constexpr_hash.h"
+
+int main(int, char**) {
+  test_library_hash_specializations_available();
+
+#if TEST_STD_VER >= 26
+  static_assert(test_constexpr_hash_specializations_available());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp b/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp
new file mode 100644
index 00000000000000..4401bc1b6507f8
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <functional>
+
+//  Hashing a struct w/o a defined hash should fail.
+
+#include <functional>
+#include <cassert>
+#include <type_traits>
+
+struct X {};
+
+int main(int, char**)
+{
+    X x;
+    std::size_t h = std::hash<X>{} ( x );
+
+  return 0;
+}
diff --git a/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
new file mode 100644
index 00000000000000..cdc982f12c638f
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// <functional>
+
+// Make sure that we can hash enumeration values.
+
+#include <functional>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#include "constexpr_hash.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 11
+#  include "poisoned_hash_helper.h"
+#endif
+
+enum class Colors { red, orange, yellow, green, blue, indigo, violet };
+enum class Cardinals { zero, one, two, three, five=5 };
+enum class LongColors : short { red, orange, yellow, green, blue, indigo, violet };
+enum class ShortColors : long { red, orange, yellow, green, blue, indigo, violet };
+enum class EightBitColors : std::uint8_t { red, orange, yellow, green, blue, indigo, violet };
+
+enum Fruits { apple, pear, grape, mango, cantaloupe };
+
+template <class T, template <class> typename THash>
+TEST_CONSTEXPR_CXX26 void test() {
+#if TEST_STD_VER >= 11
+    test_hash_disabled<const T>();
+    test_hash_disabled<volatile T>();
+    test_hash_disabled<const volatile T>();
+#endif
+
+    // typedef std::hash<T> H;
+    typedef THash<T> H;
+#if TEST_STD_VER <= 17
+    static_assert((std::is_same<typename H::argument_type, T>::value), "");
+    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+#endif
+    ASSERT_NOEXCEPT(H()(T()));
+    typedef typename std::underlying_type<T>::type under_type;
+
+    H h1;
+    // std::hash<under_type> h2;
+    THash<under_type> h2;
+    for (int i = 0; i <= 5; ++i)
+    {
+        T t(static_cast<T> (i));
+        const bool small = std::integral_constant<bool, sizeof(T) <= sizeof(std::size_t)>::value; // avoid compiler warnings
+        if (small)
+            assert(h1(t) == h2(static_cast<under_type>(i)));
+    }
+}
+
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test<Cardinals, THash>();
+
+  test<Colors, THash>();
+  test<ShortColors, THash>();
+  test<LongColors, THash>();
+  test<EightBitColors, THash>();
+
+  test<Fruits, THash>();
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test_with_hash<std::hash>());
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
new file mode 100644
index 00000000000000..c377aab5e869d5
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// <functional>
+
+// template <class T>
+// struct hash
+//     : public unary_function<T, size_t>
+// {
+//     size_t operator()(T val) const;
+// };
+
+// Not very portable
+
+#include <functional>
+#include <cassert>
+#include <type_traits>
+#include <limits>
+#include <cmath>
+
+#include "constexpr_hash.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 11
+#  include "poisoned_hash_helper.h"
+#endif
+
+template <class T, template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test() {
+#if TEST_STD_VER >= 11
+  test_hash_disabled<const T, THash<const T>>();
+  test_hash_disabled<volatile T, THash<volatile T>>();
+  test_hash_disabled<const volatile T, THash<const volatile T>>();
+#endif
+
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
+#if TEST_STD_VER <= 17
+    static_assert((std::is_same<typename H::argument_type, T>::value), "");
+    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+#endif
+    ASSERT_NOEXCEPT(H()(T()));
+    H h;
+
+    std::size_t t0 = h(0.);
+    std::size_t tn0 = h(-0.);
+    std::size_t tp1 = h(static_cast<T>(0.1));
+    std::size_t t1 = h(1);
+    std::size_t tn1 = h(-1);
+    std::size_t pinf = h(INFINITY);
+    std::size_t ninf = h(-INFINITY);
+    assert(t0 == tn0);
+    assert(t0 != tp1);
+    assert(t0 != t1);
+    assert(t0 != tn1);
+    assert(t0 != pinf);
+    assert(t0 != ninf);
+
+    assert(tp1 != t1);
+    assert(tp1 != tn1);
+    assert(tp1 != pinf);
+    assert(tp1 != ninf);
+
+    assert(t1 != tn1);
+    assert(t1 != pinf);
+    assert(t1 != ninf);
+
+    assert(tn1 != pinf);
+    assert(tn1 != ninf);
+
+    assert(pinf != ninf);
+}
+
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test<float, THash>();
+  test<double, THash>();
+  test<long double, THash>();
+  return true;
+}
+
+int main(int, char**)
+{
+  assert(test_with_hash<std::hash>());
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
+  return 0;
+}
diff --git a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
new file mode 100644
index 00000000000000..f2c217f10db96c
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
@@ -0,0 +1,140 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// <functional>
+
+// template <class T>
+// struct hash
+//     : public unary_function<T, size_t>
+// {
+//     size_t operator()(T val) const;
+// };
+
+#include <functional>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#include "constexpr_hash.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 11
+#  include "poisoned_hash_helper.h"
+#endif
+
+template <class T, template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test() {
+#if TEST_STD_VER >= 11
+  test_hash_disabled<const T, THash<const T>>();
+  test_hash_disabled<volatile T, THash<volatile T>>();
+  test_hash_disabled<const volatile T, THash<const volatile T>>();
+#endif
+
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
+#if TEST_STD_VER <= 17
+    static_assert((std::is_same<typename H::argument_type, T>::value), "");
+    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+#endif
+    ASSERT_NOEXCEPT(H()(T()));
+    H h;
+
+    for (int i = 0; i <= 5; ++i)
+    {
+        T t(static_cast<T>(i));
+        const bool small = std::integral_constant<bool, sizeof(T) <= sizeof(std::size_t)>::value; // avoid compiler warnings
+        if (small)
+        {
+            const std::size_t result = h(t);
+            LIBCPP_ASSERT(result == static_cast<std::size_t>(t));
+            ((void)result); // Prevent unused warning
+        }
+    }
+}
+
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test<bool, THash>();
+  test<char, THash>();
+  test<signed char, THash>();
+  test<unsigned char, THash>();
+  test<char16_t, THash>();
+  test<char32_t, THash>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t, THash>();
+#endif
+  test<short, THash>();
+  test<unsigned short, THash>();
+  test<int, THash>();
+  test<unsigned int, THash>();
+  test<long, THash>();
+  test<unsigned long, THash>();
+  test<long long, THash>();
+  test<unsigned long long, THash>();
+
+  //  LWG #2119
+  test<std::ptrdiff_t, THash>();
+  test<std::size_t, THash>();
+
+  test<std::int8_t, THash>();
+  test<std::int16_t, THash>();
+  test<std::int32_t, THash>();
+  test<std::int64_t, THash>();
+
+  test<std::int_fast8_t, THash>();
+  test<std::int_fast16_t, THash>();
+  test<std::int_fast32_t, THash>();
+  test<std::int_fast64_t, THash>();
+
+  test<std::int_least8_t, THash>();
+  test<std::int_least16_t, THash>();
+  test<std::int_least32_t, THash>();
+  test<std::int_least64_t, THash>();
+
+  test<std::intmax_t, THash>();
+  test<std::intptr_t, THash>();
+
+  test<std::uint8_t, THash>();
+  test<std::uint16_t, THash>();
+  test<std::uint32_t, THash>();
+  test<std::uint64_t, THash>();
+
+  test<std::uint_fast8_t, THash>();
+  test<std::uint_fast16_t, THash>();
+  test<std::uint_fast32_t, THash>();
+  test<std::uint_fast64_t, THash>();
+
+  test<std::uint_least8_t, THash>();
+  test<std::uint_least16_t, THash>();
+  test<std::uint_least32_t, THash>();
+  test<std::uint_least64_t, THash>();
+
+  test<std::uintmax_t, THash>();
+  test<std::uintptr_t, THash>();
+
+#ifndef TEST_HAS_NO_INT128
+  test<__int128_t, THash>();
+  test<__uint128_t, THash>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test_with_hash<std::hash>());
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
new file mode 100644
index 00000000000000..8632e907f1caf5
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <functional>
+
+//  Hashing a struct w/o a defined hash should *not* fail, but it should
+// create a type that is not constructible and not callable.
+// See also: https://cplusplus.github.io/LWG/lwg-defects.html#2543
+
+#include <functional>
+#include <cassert>
+#include <type_traits>
+
+#include "constexpr_hash.h"
+#include "test_macros.h"
+
+struct X {};
+
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  using H = THash<X>;
+
+  static_assert(!std::is_default_constructible<H>::value, "");
+  static_assert(!std::is_copy_constructible<H>::value, "");
+  static_assert(!std::is_move_constructible<H>::value, "");
+  static_assert(!std::is_copy_assignable<H>::value, "");
+  static_assert(!std::is_move_assignable<H>::value, "");
+#if TEST_STD_VER > 14
+    static_assert(!std::is_invocable<H, X&>::value, "");
+    static_assert(!std::is_invocable<H, X const&>::value, "");
+#endif
+
+    return true;
+}
+
+int main(int, char**) {
+  assert(test_with_hash<std::hash>());
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
+  // using H = std::hash<X>;
+  // using H = support::constexpr_hash<X>;
+
+  return 0;
+}
diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
new file mode 100644
index 00000000000000..c6c2a6e805f2ba
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// <functional>
+
+// template <class T>
+// struct hash
+//     : public unary_function<T, size_t>
+// {
+//     size_t operator()(T val) const;
+// };
+
+// Not very portable
+
+#include <cassert>
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+
+#include "constexpr_hash.h"
+#include "test_macros.h"
+
+template <class T, template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test() {
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
+#if TEST_STD_VER <= 17
+    static_assert((std::is_same<typename H::argument_type, T>::value), "");
+    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+#endif
+    ASSERT_NOEXCEPT(H()(T()));
+    H h;
+
+    typedef typename std::remove_pointer<T>::type type;
+    type i;
+    type j;
+    assert(h(&i) != h(&j));
+}
+
+template < template <class> class THash = std::hash >
+TEST_CONSTEXPR_CXX26 void test_nullptr() {
+  typedef std::nullptr_t T;
+  // typedef std::hash<T> H;
+  typedef THash<T> H;
+#if TEST_STD_VER <= 17
+  static_assert((std::is_same<typename H::argument_type, T>::value), "");
+  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+#endif
+  ASSERT_NOEXCEPT(H()(T()));
+}
+
+template <template <typename> typename THash >
+TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+  test_nullptr< THash>();
+  return true;
+}
+
+int main(int, char**)
+{
+  assert(test_with_hash<std::hash>());
+  test<int*>();
+
+#if TEST_STD_VER >= 26
+  static_assert(test_with_hash<support::constexpr_hash>());
+#endif
+
+  return 0;
+}

>From d46cecf4672d7739c1dfd0568f41a9c55465758a Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 21:41:22 +0100
Subject: [PATCH 21/38] setup support tests

---
 .../unord.hash/enum.compile.fail.cpp          |   3 +-
 .../constexpr_hash/enabled_hashes.pass.cpp    |  11 +-
 .../constexpr_hash/enum.compile.fail.cpp      |  17 +-
 .../test.support/constexpr_hash/enum.pass.cpp |  64 ++++----
 .../constexpr_hash/floating.pass.cpp          | 102 ++++++------
 .../constexpr_hash/integral.pass.cpp          | 149 ++++++++----------
 .../constexpr_hash/non_enum.pass.cpp          |  20 +--
 .../constexpr_hash/pointer.pass.cpp           |  50 +++---
 .../constexpr_hash/pointer.verify.cpp         |  28 ++++
 9 files changed, 215 insertions(+), 229 deletions(-)
 create mode 100644 libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp

diff --git a/libcxx/test/std/utilities/function.objects/unord.hash/enum.compile.fail.cpp b/libcxx/test/std/utilities/function.objects/unord.hash/enum.compile.fail.cpp
index 4401bc1b6507f8..a3b360f09599f5 100644
--- a/libcxx/test/std/utilities/function.objects/unord.hash/enum.compile.fail.cpp
+++ b/libcxx/test/std/utilities/function.objects/unord.hash/enum.compile.fail.cpp
@@ -11,8 +11,7 @@
 //  Hashing a struct w/o a defined hash should fail.
 
 #include <functional>
-#include <cassert>
-#include <type_traits>
+#include <cstddef>
 
 struct X {};
 
diff --git a/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
index 43afca3407174c..4fbb78d357bc0c 100644
--- a/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
@@ -6,19 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++26
 
-// <functional>
+// "support/constexpr_hash.h"
 
-// Test that <functional> provides all of the arithmetic, enum, and pointer
-// hash specializations.
+// Test that "support/constexpr_hash.h" provides all of the arithmetic, enum, and pointer
+// hash specializations, and that they work during constant evaluation.
 
-#include <functional>
+#include "constexpr_hash.h"
 
 #include "poisoned_hash_helper.h"
 
 #include "test_macros.h"
-#include "constexpr_hash.h"
 
 int main(int, char**) {
   test_library_hash_specializations_available();
diff --git a/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp b/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp
index 4401bc1b6507f8..733344466f6a1d 100644
--- a/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/enum.compile.fail.cpp
@@ -6,20 +6,21 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <functional>
+// REQUIRES: std-at-least-c++26
+
+// "support/constexpr_hash.h"
 
 //  Hashing a struct w/o a defined hash should fail.
 
-#include <functional>
-#include <cassert>
-#include <type_traits>
+#include "constexpr_hash.h"
+
+#include <cstddef>
 
 struct X {};
 
-int main(int, char**)
-{
-    X x;
-    std::size_t h = std::hash<X>{} ( x );
+int main(int, char**) {
+  X x;
+  std::size_t h = support::constexpr_hash<X>{}(x);
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
index cdc982f12c638f..1410d59f3f6211 100644
--- a/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
@@ -8,18 +8,18 @@
 
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// <functional>
+// "support/constexpr_hash.h"
 
 // Make sure that we can hash enumeration values.
 
-#include <functional>
+#include "constexpr_hash.h"
+
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <limits>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
@@ -27,61 +27,57 @@
 #endif
 
 enum class Colors { red, orange, yellow, green, blue, indigo, violet };
-enum class Cardinals { zero, one, two, three, five=5 };
+enum class Cardinals { zero, one, two, three, five = 5 };
 enum class LongColors : short { red, orange, yellow, green, blue, indigo, violet };
 enum class ShortColors : long { red, orange, yellow, green, blue, indigo, violet };
 enum class EightBitColors : std::uint8_t { red, orange, yellow, green, blue, indigo, violet };
 
 enum Fruits { apple, pear, grape, mango, cantaloupe };
 
-template <class T, template <class> typename THash>
+template <class T>
 TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
-    test_hash_disabled<const T>();
-    test_hash_disabled<volatile T>();
-    test_hash_disabled<const volatile T>();
+  test_hash_disabled<const T, support::constexpr_hash<const T>>();
+  test_hash_disabled<volatile T, support::constexpr_hash<volatile T>>();
+  test_hash_disabled<const volatile T, support::constexpr_hash<const volatile T>>();
 #endif
 
-    // typedef std::hash<T> H;
-    typedef THash<T> H;
+  typedef support::constexpr_hash<T> H;
 #if TEST_STD_VER <= 17
-    static_assert((std::is_same<typename H::argument_type, T>::value), "");
-    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+  static_assert((std::is_same<typename H::argument_type, T>::value), "");
+  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
 #endif
-    ASSERT_NOEXCEPT(H()(T()));
-    typedef typename std::underlying_type<T>::type under_type;
-
-    H h1;
-    // std::hash<under_type> h2;
-    THash<under_type> h2;
-    for (int i = 0; i <= 5; ++i)
-    {
-        T t(static_cast<T> (i));
-        const bool small = std::integral_constant<bool, sizeof(T) <= sizeof(std::size_t)>::value; // avoid compiler warnings
-        if (small)
-            assert(h1(t) == h2(static_cast<under_type>(i)));
-    }
+  ASSERT_NOEXCEPT(H()(T()));
+  typedef typename std::underlying_type<T>::type under_type;
+
+  H h1;
+  support::constexpr_hash<under_type> h2;
+  for (int i = 0; i <= 5; ++i) {
+    T t(static_cast<T>(i));
+    const bool small = std::integral_constant<bool, sizeof(T) <= sizeof(std::size_t)>::value; // avoid compiler warnings
+    if (small)
+      assert(h1(t) == h2(static_cast<under_type>(i)));
+  }
 }
 
-template <template <typename> typename THash >
 TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test<Cardinals, THash>();
+  test<Cardinals>();
 
-  test<Colors, THash>();
-  test<ShortColors, THash>();
-  test<LongColors, THash>();
-  test<EightBitColors, THash>();
+  test<Colors>();
+  test<ShortColors>();
+  test<LongColors>();
+  test<EightBitColors>();
 
-  test<Fruits, THash>();
+  test<Fruits>();
 
   return true;
 }
 
 int main(int, char**) {
-  assert(test_with_hash<std::hash>());
+  assert(test_with_hash());
 
 #if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
+  static_assert(test_with_hash());
 #endif
 
   return 0;
diff --git a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
index c377aab5e869d5..78d065f676a7af 100644
--- a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
@@ -8,89 +8,79 @@
 
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// <functional>
-
-// template <class T>
-// struct hash
-//     : public unary_function<T, size_t>
-// {
-//     size_t operator()(T val) const;
-// };
+// "support/constexpr_hash.h"
 
 // Not very portable
 
-#include <functional>
+#include "constexpr_hash.h"
+
 #include <cassert>
 #include <type_traits>
 #include <limits>
 #include <cmath>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T, template <class> class THash = std::hash >
+template <class T>
 TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
-  test_hash_disabled<const T, THash<const T>>();
-  test_hash_disabled<volatile T, THash<volatile T>>();
-  test_hash_disabled<const volatile T, THash<const volatile T>>();
+  test_hash_disabled<const T, support::constexpr_hash<const T>>();
+  test_hash_disabled<volatile T, support::constexpr_hash<volatile T>>();
+  test_hash_disabled<const volatile T, support::constexpr_hash<const volatile T>>();
 #endif
 
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+  typedef support::constexpr_hash<T> H;
 #if TEST_STD_VER <= 17
-    static_assert((std::is_same<typename H::argument_type, T>::value), "");
-    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+  static_assert((std::is_same<typename H::argument_type, T>::value), "");
+  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
 #endif
-    ASSERT_NOEXCEPT(H()(T()));
-    H h;
-
-    std::size_t t0 = h(0.);
-    std::size_t tn0 = h(-0.);
-    std::size_t tp1 = h(static_cast<T>(0.1));
-    std::size_t t1 = h(1);
-    std::size_t tn1 = h(-1);
-    std::size_t pinf = h(INFINITY);
-    std::size_t ninf = h(-INFINITY);
-    assert(t0 == tn0);
-    assert(t0 != tp1);
-    assert(t0 != t1);
-    assert(t0 != tn1);
-    assert(t0 != pinf);
-    assert(t0 != ninf);
-
-    assert(tp1 != t1);
-    assert(tp1 != tn1);
-    assert(tp1 != pinf);
-    assert(tp1 != ninf);
-
-    assert(t1 != tn1);
-    assert(t1 != pinf);
-    assert(t1 != ninf);
-
-    assert(tn1 != pinf);
-    assert(tn1 != ninf);
-
-    assert(pinf != ninf);
+  ASSERT_NOEXCEPT(H()(T()));
+  H h;
+
+  std::size_t t0   = h(0.);
+  std::size_t tn0  = h(-0.);
+  std::size_t tp1  = h(static_cast<T>(0.1));
+  std::size_t t1   = h(1);
+  std::size_t tn1  = h(-1);
+  std::size_t pinf = h(INFINITY);
+  std::size_t ninf = h(-INFINITY);
+  assert(t0 == tn0);
+  assert(t0 != tp1);
+  assert(t0 != t1);
+  assert(t0 != tn1);
+  assert(t0 != pinf);
+  assert(t0 != ninf);
+
+  assert(tp1 != t1);
+  assert(tp1 != tn1);
+  assert(tp1 != pinf);
+  assert(tp1 != ninf);
+
+  assert(t1 != tn1);
+  assert(t1 != pinf);
+  assert(t1 != ninf);
+
+  assert(tn1 != pinf);
+  assert(tn1 != ninf);
+
+  assert(pinf != ninf);
 }
 
-template <template <typename> typename THash >
 TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test<float, THash>();
-  test<double, THash>();
-  test<long double, THash>();
+  test<float>();
+  test<double>();
+  test<long double>();
   return true;
 }
 
-int main(int, char**)
-{
-  assert(test_with_hash<std::hash>());
+int main(int, char**) {
+  assert(test_with_hash());
 #if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
+  static_assert(test_with_hash());
 #endif
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
index f2c217f10db96c..051084cac06f0c 100644
--- a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
@@ -8,132 +8,121 @@
 
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// <functional>
+// "support/constexpr_hash.h"
 
-// template <class T>
-// struct hash
-//     : public unary_function<T, size_t>
-// {
-//     size_t operator()(T val) const;
-// };
+#include "constexpr_hash.h"
 
-#include <functional>
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <limits>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
 #  include "poisoned_hash_helper.h"
 #endif
 
-template <class T, template <class> class THash = std::hash >
+template <class T>
 TEST_CONSTEXPR_CXX26 void test() {
 #if TEST_STD_VER >= 11
-  test_hash_disabled<const T, THash<const T>>();
-  test_hash_disabled<volatile T, THash<volatile T>>();
-  test_hash_disabled<const volatile T, THash<const volatile T>>();
+  test_hash_disabled<const T, support::constexpr_hash<const T>>();
+  test_hash_disabled<volatile T, support::constexpr_hash<volatile T>>();
+  test_hash_disabled<const volatile T, support::constexpr_hash<const volatile T>>();
 #endif
 
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+  typedef support::constexpr_hash<T> H;
 #if TEST_STD_VER <= 17
-    static_assert((std::is_same<typename H::argument_type, T>::value), "");
-    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+  static_assert((std::is_same<typename H::argument_type, T>::value), "");
+  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
 #endif
-    ASSERT_NOEXCEPT(H()(T()));
-    H h;
-
-    for (int i = 0; i <= 5; ++i)
-    {
-        T t(static_cast<T>(i));
-        const bool small = std::integral_constant<bool, sizeof(T) <= sizeof(std::size_t)>::value; // avoid compiler warnings
-        if (small)
-        {
-            const std::size_t result = h(t);
-            LIBCPP_ASSERT(result == static_cast<std::size_t>(t));
-            ((void)result); // Prevent unused warning
-        }
+  ASSERT_NOEXCEPT(H()(T()));
+  H h;
+
+  for (int i = 0; i <= 5; ++i) {
+    T t(static_cast<T>(i));
+    const bool small = std::integral_constant<bool, sizeof(T) <= sizeof(std::size_t)>::value; // avoid compiler warnings
+    if (small) {
+      const std::size_t result = h(t);
+      LIBCPP_ASSERT(result == static_cast<std::size_t>(t));
+      ((void)result); // Prevent unused warning
     }
+  }
 }
 
-template <template <typename> typename THash >
 TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test<bool, THash>();
-  test<char, THash>();
-  test<signed char, THash>();
-  test<unsigned char, THash>();
-  test<char16_t, THash>();
-  test<char32_t, THash>();
+  test<bool>();
+  test<char>();
+  test<signed char>();
+  test<unsigned char>();
+  test<char16_t>();
+  test<char32_t>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t, THash>();
+  test<wchar_t>();
 #endif
-  test<short, THash>();
-  test<unsigned short, THash>();
-  test<int, THash>();
-  test<unsigned int, THash>();
-  test<long, THash>();
-  test<unsigned long, THash>();
-  test<long long, THash>();
-  test<unsigned long long, THash>();
+  test<short>();
+  test<unsigned short>();
+  test<int>();
+  test<unsigned int>();
+  test<long>();
+  test<unsigned long>();
+  test<long long>();
+  test<unsigned long long>();
 
   //  LWG #2119
-  test<std::ptrdiff_t, THash>();
-  test<std::size_t, THash>();
+  test<std::ptrdiff_t>();
+  test<std::size_t>();
 
-  test<std::int8_t, THash>();
-  test<std::int16_t, THash>();
-  test<std::int32_t, THash>();
-  test<std::int64_t, THash>();
+  test<std::int8_t>();
+  test<std::int16_t>();
+  test<std::int32_t>();
+  test<std::int64_t>();
 
-  test<std::int_fast8_t, THash>();
-  test<std::int_fast16_t, THash>();
-  test<std::int_fast32_t, THash>();
-  test<std::int_fast64_t, THash>();
+  test<std::int_fast8_t>();
+  test<std::int_fast16_t>();
+  test<std::int_fast32_t>();
+  test<std::int_fast64_t>();
 
-  test<std::int_least8_t, THash>();
-  test<std::int_least16_t, THash>();
-  test<std::int_least32_t, THash>();
-  test<std::int_least64_t, THash>();
+  test<std::int_least8_t>();
+  test<std::int_least16_t>();
+  test<std::int_least32_t>();
+  test<std::int_least64_t>();
 
-  test<std::intmax_t, THash>();
-  test<std::intptr_t, THash>();
+  test<std::intmax_t>();
+  test<std::intptr_t>();
 
-  test<std::uint8_t, THash>();
-  test<std::uint16_t, THash>();
-  test<std::uint32_t, THash>();
-  test<std::uint64_t, THash>();
+  test<std::uint8_t>();
+  test<std::uint16_t>();
+  test<std::uint32_t>();
+  test<std::uint64_t>();
 
-  test<std::uint_fast8_t, THash>();
-  test<std::uint_fast16_t, THash>();
-  test<std::uint_fast32_t, THash>();
-  test<std::uint_fast64_t, THash>();
+  test<std::uint_fast8_t>();
+  test<std::uint_fast16_t>();
+  test<std::uint_fast32_t>();
+  test<std::uint_fast64_t>();
 
-  test<std::uint_least8_t, THash>();
-  test<std::uint_least16_t, THash>();
-  test<std::uint_least32_t, THash>();
-  test<std::uint_least64_t, THash>();
+  test<std::uint_least8_t>();
+  test<std::uint_least16_t>();
+  test<std::uint_least32_t>();
+  test<std::uint_least64_t>();
 
-  test<std::uintmax_t, THash>();
-  test<std::uintptr_t, THash>();
+  test<std::uintmax_t>();
+  test<std::uintptr_t>();
 
 #ifndef TEST_HAS_NO_INT128
-  test<__int128_t, THash>();
-  test<__uint128_t, THash>();
+  test<__int128_t>();
+  test<__uint128_t>();
 #endif
 
   return true;
 }
 
 int main(int, char**) {
-  assert(test_with_hash<std::hash>());
+  assert(test_with_hash());
 
 #if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
+  static_assert(test_with_hash());
 #endif
 
   return 0;
diff --git a/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
index 8632e907f1caf5..4ee2887982ef89 100644
--- a/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
@@ -8,24 +8,22 @@
 
 // UNSUPPORTED: c++03
 
-// <functional>
+// "support/constexpr_hash.h"
 
 //  Hashing a struct w/o a defined hash should *not* fail, but it should
 // create a type that is not constructible and not callable.
 // See also: https://cplusplus.github.io/LWG/lwg-defects.html#2543
 
-#include <functional>
+#include "constexpr_hash.h"
 #include <cassert>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
 struct X {};
 
-template <template <typename> typename THash >
 TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  using H = THash<X>;
+  using H = support::constexpr_hash<X>;
 
   static_assert(!std::is_default_constructible<H>::value, "");
   static_assert(!std::is_copy_constructible<H>::value, "");
@@ -33,21 +31,19 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
   static_assert(!std::is_copy_assignable<H>::value, "");
   static_assert(!std::is_move_assignable<H>::value, "");
 #if TEST_STD_VER > 14
-    static_assert(!std::is_invocable<H, X&>::value, "");
-    static_assert(!std::is_invocable<H, X const&>::value, "");
+  static_assert(!std::is_invocable<H, X&>::value, "");
+  static_assert(!std::is_invocable<H, X const&>::value, "");
 #endif
 
-    return true;
+  return true;
 }
 
 int main(int, char**) {
-  assert(test_with_hash<std::hash>());
+  assert(test_with_hash());
 
 #if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
+  static_assert(test_with_hash());
 #endif
-  // using H = std::hash<X>;
-  // using H = support::constexpr_hash<X>;
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
index c6c2a6e805f2ba..508d3d02b63257 100644
--- a/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
@@ -8,47 +8,37 @@
 
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// <functional>
-
-// template <class T>
-// struct hash
-//     : public unary_function<T, size_t>
-// {
-//     size_t operator()(T val) const;
-// };
+// "support/constexpr_hash.h"
 
 // Not very portable
 
+#include "constexpr_hash.h"
+
 #include <cassert>
 #include <cstddef>
-#include <functional>
 #include <type_traits>
 
-#include "constexpr_hash.h"
 #include "test_macros.h"
 
-template <class T, template <class> class THash = std::hash >
+template <class T>
 TEST_CONSTEXPR_CXX26 void test() {
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+  typedef support::constexpr_hash<T> H;
 #if TEST_STD_VER <= 17
-    static_assert((std::is_same<typename H::argument_type, T>::value), "");
-    static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
+  static_assert((std::is_same<typename H::argument_type, T>::value), "");
+  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
 #endif
-    ASSERT_NOEXCEPT(H()(T()));
-    H h;
+  ASSERT_NOEXCEPT(H()(T()));
+  H h;
 
-    typedef typename std::remove_pointer<T>::type type;
-    type i;
-    type j;
-    assert(h(&i) != h(&j));
+  typedef typename std::remove_pointer<T>::type type;
+  type i;
+  type j;
+  assert(h(&i) != h(&j));
 }
 
-template < template <class> class THash = std::hash >
 TEST_CONSTEXPR_CXX26 void test_nullptr() {
   typedef std::nullptr_t T;
-  // typedef std::hash<T> H;
-  typedef THash<T> H;
+  typedef support::constexpr_hash<T> H;
 #if TEST_STD_VER <= 17
   static_assert((std::is_same<typename H::argument_type, T>::value), "");
   static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
@@ -56,19 +46,17 @@ TEST_CONSTEXPR_CXX26 void test_nullptr() {
   ASSERT_NOEXCEPT(H()(T()));
 }
 
-template <template <typename> typename THash >
 TEST_CONSTEXPR_CXX26 bool test_with_hash() {
-  test_nullptr< THash>();
+  test_nullptr();
   return true;
 }
 
-int main(int, char**)
-{
-  assert(test_with_hash<std::hash>());
-  test<int*>();
+int main(int, char**) {
+  assert(test_with_hash());
+  // test<int*>();
 
 #if TEST_STD_VER >= 26
-  static_assert(test_with_hash<support::constexpr_hash>());
+  static_assert(test_with_hash());
 #endif
 
   return 0;
diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
new file mode 100644
index 00000000000000..5be4d3fb9af8c8
--- /dev/null
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
@@ -0,0 +1,28 @@
+
+
+#include "constexpr_hash.h"
+
+#include <cstddef>
+
+constexpr std::size_t test_pointer() {
+  typedef int* T;
+  typedef support::constexpr_hash<T> H;
+
+  //expected-error at +2 {{call to deleted constructor}}
+  //expected-error at +1 {{ does not provide a call operator}}
+  return (H()(T()));
+}
+
+int main(int, char**) {
+  typedef int* T;
+  typedef support::constexpr_hash<T> H;
+
+  //expected-error at +2 {{call to deleted constructor}}
+  //expected-error at +1 {{ does not provide a call operator}}
+  std::size_t h = (H()(T()));
+
+  //expected-error at +1 {{static assertion expression is not an integral constant expression}}
+  static_assert(0 != test_pointer());
+
+  return 0;
+}
\ No newline at end of file

>From bbf06f556f2d7ed48d3a4ded9ee528f84f58565b Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 21:49:50 +0100
Subject: [PATCH 22/38] only cpp26

---
 .../constexpr_hash/enabled_hashes.pass.cpp    |  2 --
 .../test.support/constexpr_hash/enum.pass.cpp | 19 +++++------------
 .../constexpr_hash/floating.pass.cpp          | 18 ++++------------
 .../constexpr_hash/integral.pass.cpp          | 19 +++++------------
 .../constexpr_hash/non_enum.pass.cpp          |  9 +++-----
 .../constexpr_hash/pointer.pass.cpp           | 21 ++++++-------------
 .../constexpr_hash/pointer.verify.cpp         |  3 +++
 7 files changed, 26 insertions(+), 65 deletions(-)

diff --git a/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
index 4fbb78d357bc0c..7456e56d2bb034 100644
--- a/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/enabled_hashes.pass.cpp
@@ -22,9 +22,7 @@
 int main(int, char**) {
   test_library_hash_specializations_available();
 
-#if TEST_STD_VER >= 26
   static_assert(test_constexpr_hash_specializations_available());
-#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
index 1410d59f3f6211..e10cc97d39bd00 100644
--- a/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/enum.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// REQUIRES: std-at-least-c++26
 
 // "support/constexpr_hash.h"
 
@@ -22,9 +22,7 @@
 
 #include "test_macros.h"
 
-#if TEST_STD_VER >= 11
-#  include "poisoned_hash_helper.h"
-#endif
+#include "poisoned_hash_helper.h"
 
 enum class Colors { red, orange, yellow, green, blue, indigo, violet };
 enum class Cardinals { zero, one, two, three, five = 5 };
@@ -35,18 +33,13 @@ enum class EightBitColors : std::uint8_t { red, orange, yellow, green, blue, ind
 enum Fruits { apple, pear, grape, mango, cantaloupe };
 
 template <class T>
-TEST_CONSTEXPR_CXX26 void test() {
-#if TEST_STD_VER >= 11
+constexpr void test() {
   test_hash_disabled<const T, support::constexpr_hash<const T>>();
   test_hash_disabled<volatile T, support::constexpr_hash<volatile T>>();
   test_hash_disabled<const volatile T, support::constexpr_hash<const volatile T>>();
-#endif
 
   typedef support::constexpr_hash<T> H;
-#if TEST_STD_VER <= 17
-  static_assert((std::is_same<typename H::argument_type, T>::value), "");
-  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
-#endif
+
   ASSERT_NOEXCEPT(H()(T()));
   typedef typename std::underlying_type<T>::type under_type;
 
@@ -60,7 +53,7 @@ TEST_CONSTEXPR_CXX26 void test() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+constexpr bool test_with_hash() {
   test<Cardinals>();
 
   test<Colors>();
@@ -76,9 +69,7 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
 int main(int, char**) {
   assert(test_with_hash());
 
-#if TEST_STD_VER >= 26
   static_assert(test_with_hash());
-#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
index 78d065f676a7af..5c51725b9bef5a 100644
--- a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// REQUIRES: std-at-least-c++26
 
 // "support/constexpr_hash.h"
 
@@ -21,23 +21,15 @@
 
 #include "test_macros.h"
 
-#if TEST_STD_VER >= 11
-#  include "poisoned_hash_helper.h"
-#endif
+#include "poisoned_hash_helper.h"
 
 template <class T>
-TEST_CONSTEXPR_CXX26 void test() {
-#if TEST_STD_VER >= 11
+constexpr void test() {
   test_hash_disabled<const T, support::constexpr_hash<const T>>();
   test_hash_disabled<volatile T, support::constexpr_hash<volatile T>>();
   test_hash_disabled<const volatile T, support::constexpr_hash<const volatile T>>();
-#endif
 
   typedef support::constexpr_hash<T> H;
-#if TEST_STD_VER <= 17
-  static_assert((std::is_same<typename H::argument_type, T>::value), "");
-  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
-#endif
   ASSERT_NOEXCEPT(H()(T()));
   H h;
 
@@ -70,7 +62,7 @@ TEST_CONSTEXPR_CXX26 void test() {
   assert(pinf != ninf);
 }
 
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+constexpr bool test_with_hash() {
   test<float>();
   test<double>();
   test<long double>();
@@ -79,8 +71,6 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
 
 int main(int, char**) {
   assert(test_with_hash());
-#if TEST_STD_VER >= 26
   static_assert(test_with_hash());
-#endif
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
index 051084cac06f0c..a4c51f79b686f3 100644
--- a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// REQUIRES: std-at-least-c++26
 
 // "support/constexpr_hash.h"
 
@@ -20,23 +20,16 @@
 
 #include "test_macros.h"
 
-#if TEST_STD_VER >= 11
-#  include "poisoned_hash_helper.h"
-#endif
+#include "poisoned_hash_helper.h"
 
 template <class T>
-TEST_CONSTEXPR_CXX26 void test() {
-#if TEST_STD_VER >= 11
+constexpr void test() {
   test_hash_disabled<const T, support::constexpr_hash<const T>>();
   test_hash_disabled<volatile T, support::constexpr_hash<volatile T>>();
   test_hash_disabled<const volatile T, support::constexpr_hash<const volatile T>>();
-#endif
 
   typedef support::constexpr_hash<T> H;
-#if TEST_STD_VER <= 17
-  static_assert((std::is_same<typename H::argument_type, T>::value), "");
-  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
-#endif
+
   ASSERT_NOEXCEPT(H()(T()));
   H h;
 
@@ -51,7 +44,7 @@ TEST_CONSTEXPR_CXX26 void test() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+constexpr bool test_with_hash() {
   test<bool>();
   test<char>();
   test<signed char>();
@@ -121,9 +114,7 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
 int main(int, char**) {
   assert(test_with_hash());
 
-#if TEST_STD_VER >= 26
   static_assert(test_with_hash());
-#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
index 4ee2887982ef89..5555a8283ddc6e 100644
--- a/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/non_enum.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++26
 
 // "support/constexpr_hash.h"
 
@@ -22,7 +22,7 @@
 
 struct X {};
 
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+constexpr bool test_with_hash() {
   using H = support::constexpr_hash<X>;
 
   static_assert(!std::is_default_constructible<H>::value, "");
@@ -30,10 +30,9 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
   static_assert(!std::is_move_constructible<H>::value, "");
   static_assert(!std::is_copy_assignable<H>::value, "");
   static_assert(!std::is_move_assignable<H>::value, "");
-#if TEST_STD_VER > 14
+
   static_assert(!std::is_invocable<H, X&>::value, "");
   static_assert(!std::is_invocable<H, X const&>::value, "");
-#endif
 
   return true;
 }
@@ -41,9 +40,7 @@ TEST_CONSTEXPR_CXX26 bool test_with_hash() {
 int main(int, char**) {
   assert(test_with_hash());
 
-#if TEST_STD_VER >= 26
   static_assert(test_with_hash());
-#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
index 508d3d02b63257..e5899a305651d8 100644
--- a/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// REQUIRES: std-at-least-c++26
 
 // "support/constexpr_hash.h"
 
@@ -21,12 +21,9 @@
 #include "test_macros.h"
 
 template <class T>
-TEST_CONSTEXPR_CXX26 void test() {
+constexpr void test() {
   typedef support::constexpr_hash<T> H;
-#if TEST_STD_VER <= 17
-  static_assert((std::is_same<typename H::argument_type, T>::value), "");
-  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
-#endif
+
   ASSERT_NOEXCEPT(H()(T()));
   H h;
 
@@ -36,28 +33,22 @@ TEST_CONSTEXPR_CXX26 void test() {
   assert(h(&i) != h(&j));
 }
 
-TEST_CONSTEXPR_CXX26 void test_nullptr() {
+constexpr void test_nullptr() {
   typedef std::nullptr_t T;
   typedef support::constexpr_hash<T> H;
-#if TEST_STD_VER <= 17
-  static_assert((std::is_same<typename H::argument_type, T>::value), "");
-  static_assert((std::is_same<typename H::result_type, std::size_t>::value), "");
-#endif
+
   ASSERT_NOEXCEPT(H()(T()));
 }
 
-TEST_CONSTEXPR_CXX26 bool test_with_hash() {
+constexpr bool test_with_hash() {
   test_nullptr();
   return true;
 }
 
 int main(int, char**) {
   assert(test_with_hash());
-  // test<int*>();
 
-#if TEST_STD_VER >= 26
   static_assert(test_with_hash());
-#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
index 5be4d3fb9af8c8..22345c452c8321 100644
--- a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
@@ -1,4 +1,7 @@
 
+// REQUIRES: std-at-least-c++26
+
+// "support/constexpr_hash.h"
 
 #include "constexpr_hash.h"
 

>From 45d127eda79e8f7966091ff61e12e020b88ba0de Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 21:51:36 +0100
Subject: [PATCH 23/38] fix doc for constexpr_hash.h

---
 libcxx/test/support/constexpr_hash.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index b303152ec5ccef..b18b3aacf9e74d 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -1,4 +1,4 @@
-//===- constexpr_hash.h - Classes for representing statements -------------*- C++ -*-===//
+//===- constexpr_hash.h - A hash function to use during constant evaluation -------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From bffdc2cde87bc3792724d403dc18777c85a9dc7b Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 21:53:57 +0100
Subject: [PATCH 24/38] std::size_t in operator() return type

---
 libcxx/test/support/constexpr_hash.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index b18b3aacf9e74d..dfaa1b2298bb0d 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -45,7 +45,7 @@ struct constexpr_hash<std::nullptr_t> {
   constexpr_hash() noexcept                        = default;
   constexpr_hash& operator=(const constexpr_hash&) = default;
 
-  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(std::nullptr_t) const noexcept {
+  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI std::size_t operator()(std::nullptr_t) const noexcept {
     return 662607004ull;
   }
 };
@@ -57,7 +57,7 @@ concept UnqualifiedArithmeticOrEnum =
 
 template <UnqualifiedArithmeticOrEnum Key>
 struct constexpr_hash<Key> {
-  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI size_t operator()(const Key& key) const noexcept {
+  [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI std::size_t operator()(const Key& key) const noexcept {
     if constexpr (std::integral<Key>) {
       if constexpr (sizeof(Key) <= sizeof(size_t)) {
         return static_cast<size_t>(key);

>From 766c90262f682dbde8e4fe72e81e437a836f35b5 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 22:06:05 +0100
Subject: [PATCH 25/38] test non-small integers

---
 libcxx/test/support/constexpr_hash.h          | 32 +++++++------------
 .../constexpr_hash/integral.pass.cpp          |  5 +++
 2 files changed, 17 insertions(+), 20 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index dfaa1b2298bb0d..e7c6fce0c1c319 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -55,6 +55,16 @@ concept UnqualifiedArithmeticOrEnum =
     std::is_same_v<Key, std::remove_cvref_t<Key>> &&
     (std::integral<Key> || std::floating_point<Key> || std::is_enum_v<Key>);
 
+template <size_t N>
+constexpr std::size_t array_hash(std::array<char, N> arr) {
+  // use a simple implementation for "hash"-ing
+  std::size_t res{0};
+  for (char c : arr) {
+    res ^= c;
+  }
+  return res;
+}
+
 template <UnqualifiedArithmeticOrEnum Key>
 struct constexpr_hash<Key> {
   [[nodiscard]] constexpr _LIBCPP_HIDE_FROM_ABI std::size_t operator()(const Key& key) const noexcept {
@@ -62,31 +72,13 @@ struct constexpr_hash<Key> {
       if constexpr (sizeof(Key) <= sizeof(size_t)) {
         return static_cast<size_t>(key);
       } else {
-        constexpr size_t multiple           = sizeof(Key) / sizeof(size_t);
-        std::array<size_t, multiple> region = std::bit_cast<std::array<size_t, multiple>>(key);
-
-        // TODO: 0, 1, 2, 3, 4
-        return region[multiple - 1]; // TODO: hash-ing
+        return array_hash(std::bit_cast<std::array<char, sizeof(Key)>>(key));
       }
     } else if constexpr (std::floating_point<Key>) {
       if (key == 0.0f)
-
         return 0;
-      // return __scalar_hash<Key>::operator()(key);
-
-      constexpr size_t multiple         = sizeof(Key) / sizeof(char);
-      std::array<char, multiple> region = std::bit_cast<std::array<char, multiple>>(key);
-
-      // TODO: pack indexing?
-      size_t res = 0;
-      for (size_t i = 0; i < multiple; ++i) {
-        res ^= region[i];
-      }
-      return res;
 
-      // TODO: 0, 1, 2, 3, 4
-      // return region[multiple - 1]; // TODO: hash-ing
-      // return 1234ULL;
+      return array_hash(std::bit_cast<std::array<char, sizeof(Key)>>(key));
 
     } else if constexpr (std::is_enum_v<Key>) {
       using type = decltype(std::to_underlying(std::declval<Key>()));
diff --git a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
index a4c51f79b686f3..23d9756957f6ee 100644
--- a/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/integral.pass.cpp
@@ -40,6 +40,11 @@ constexpr void test() {
       const std::size_t result = h(t);
       LIBCPP_ASSERT(result == static_cast<std::size_t>(t));
       ((void)result); // Prevent unused warning
+    } else {
+      T t2(static_cast<T>(i + 1));
+      const std::size_t result1 = h(t);
+      const std::size_t result2 = h(t2);
+      LIBCPP_ASSERT(result1 != result2);
     }
   }
 }

>From 3dac286c0246dc36610c245ae3450a90cd911615 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 22:39:47 +0100
Subject: [PATCH 26/38] trailing NL

---
 .../test/support/test.support/constexpr_hash/pointer.verify.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
index 22345c452c8321..04de4fc7e3bc23 100644
--- a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
@@ -28,4 +28,4 @@ int main(int, char**) {
   static_assert(0 != test_pointer());
 
   return 0;
-}
\ No newline at end of file
+}

>From f0522111ddda5f585366d7d23b6ebe1720077adb Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Tue, 13 Oct 2026 22:44:57 +0100
Subject: [PATCH 27/38] license header

---
 .../support/test.support/constexpr_hash/pointer.verify.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
index 04de4fc7e3bc23..6e9e3e9956aa94 100644
--- a/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/pointer.verify.cpp
@@ -1,3 +1,10 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++26
 

>From 87f718fcb85efee0cf57714c9824914ea5060977 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 14 Oct 2026 09:22:10 +0100
Subject: [PATCH 28/38] use std::byte over char

---
 libcxx/test/support/constexpr_hash.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index e7c6fce0c1c319..75e6d09737c01c 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -56,11 +56,11 @@ concept UnqualifiedArithmeticOrEnum =
     (std::integral<Key> || std::floating_point<Key> || std::is_enum_v<Key>);
 
 template <size_t N>
-constexpr std::size_t array_hash(std::array<char, N> arr) {
+constexpr std::size_t array_hash(std::array<std::byte, N> arr) {
   // use a simple implementation for "hash"-ing
   std::size_t res{0};
-  for (char c : arr) {
-    res ^= c;
+  for (auto c : arr) {
+    res ^= (std::size_t)c;
   }
   return res;
 }
@@ -72,19 +72,19 @@ struct constexpr_hash<Key> {
       if constexpr (sizeof(Key) <= sizeof(size_t)) {
         return static_cast<size_t>(key);
       } else {
-        return array_hash(std::bit_cast<std::array<char, sizeof(Key)>>(key));
+        return array_hash(std::bit_cast<std::array<std::byte, sizeof(Key)>>(key));
       }
     } else if constexpr (std::floating_point<Key>) {
       if (key == 0.0f)
         return 0;
 
-      return array_hash(std::bit_cast<std::array<char, sizeof(Key)>>(key));
+      return array_hash(std::bit_cast<std::array<std::byte, sizeof(Key)>>(key));
 
     } else if constexpr (std::is_enum_v<Key>) {
       using type = decltype(std::to_underlying(std::declval<Key>()));
       return constexpr_hash<type>()(static_cast<type>(key));
     }
-    std::unreachable(); // todo: revisit
+    std::unreachable();
   }
 
   constexpr_hash() noexcept                        = default;

>From ab4fd2cd89425b5db46be00136fc42e57f586260 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 15 Oct 2026 09:07:32 +0100
Subject: [PATCH 29/38] array include

---
 libcxx/test/support/constexpr_hash.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 75e6d09737c01c..8d3c372e973d42 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -20,6 +20,7 @@
 #include <concepts>
 #include <bit>
 #include <cstddef>
+#include <array>
 
 namespace support {
 

>From ecf0cc095e240e2066c577778f23e663cbf3901d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Fri, 16 Oct 2026 07:22:55 +0100
Subject: [PATCH 30/38] handle x86 long double portably, update hash function

---
 libcxx/test/support/constexpr_hash.h | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 8d3c372e973d42..0e89cf65a3150d 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -20,6 +20,7 @@
 #include <concepts>
 #include <bit>
 #include <cstddef>
+#include <cmath>
 #include <array>
 
 namespace support {
@@ -58,10 +59,11 @@ concept UnqualifiedArithmeticOrEnum =
 
 template <size_t N>
 constexpr std::size_t array_hash(std::array<std::byte, N> arr) {
-  // use a simple implementation for "hash"-ing
-  std::size_t res{0};
+  // based on fnv-1
+  std::size_t res{14695981039346656037ULL};
   for (auto c : arr) {
     res ^= (std::size_t)c;
+    res *= 1099511628211ULL;
   }
   return res;
 }
@@ -78,6 +80,26 @@ struct constexpr_hash<Key> {
     } else if constexpr (std::floating_point<Key>) {
       if (key == 0.0f)
         return 0;
+      if (std::isnan(key)) {
+        return 0xc3a5c85c97cb3127ULL;
+      }
+      if (std::isinf(key)) {
+        return std::signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
+      }
+
+      if constexpr (std::is_same_v<Key, long double>) {
+        // long double can have padding on certain platforms which are uninitialized, and hence, can't be used during constant evaluation
+        // to get around that we break it down to 2 double (which have all bits initialized).
+
+        double hi = static_cast<double>(key);
+        double lo = static_cast<double>(key - static_cast<long double>(hi));
+
+        struct Pair {
+          double hi, low;
+        } pair = {hi, lo};
+
+        return array_hash(std::bit_cast<std::array<std::byte, sizeof(Pair)>>(pair));
+      }
 
       return array_hash(std::bit_cast<std::array<std::byte, sizeof(Key)>>(key));
 

>From 17f94c89f844677ba7d2a04051d58aebfd88adfb Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Fri, 16 Oct 2026 08:03:42 +0100
Subject: [PATCH 31/38] fix ifdef location

---
 libcxx/test/support/constexpr_hash.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 0e89cf65a3150d..7a7295f844c7bd 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -23,10 +23,10 @@
 #include <cmath>
 #include <array>
 
-namespace support {
-
 #if TEST_STD_VER >= 26
 
+namespace support {
+
 template <class Key>
 struct constexpr_hash {
   constexpr_hash()                                 = delete;

>From 11d21dd486421e981b90515f9e7efdfe47e56c75 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Fri, 16 Oct 2026 23:59:05 +0100
Subject: [PATCH 32/38] cleaner

---
 libcxx/test/support/poisoned_hash_helper.h                   | 5 +----
 .../support/test.support/constexpr_hash/floating.pass.cpp    | 2 --
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index e1cc82244200e1..338519990ffed0 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -153,10 +153,7 @@ struct TestConstexprHashEnabled {
 
 // We use the same types that std::hash uses, except for pointers since pointers can't be hashed during constant evaluation
 using SupportedConstexprHashTypes =
-    types::concatenate_t<types::arithmetic_types,
-                         types::type_list<Enum,
-                                          EnumClass, //void*, void const*, Class*,
-                                          std::nullptr_t>>;
+    types::concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, std::nullptr_t>>;
 
 // Test that each of the library hash specializations for arithmetic types,
 // enum types, and pointer types are available and enabled.
diff --git a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
index 5c51725b9bef5a..fc4c192a245467 100644
--- a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
@@ -10,8 +10,6 @@
 
 // "support/constexpr_hash.h"
 
-// Not very portable
-
 #include "constexpr_hash.h"
 
 #include <cassert>

>From 8b4e346b7554a4b395783382a0d3cdaf814ce146 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 17 Oct 2026 09:30:55 +0100
Subject: [PATCH 33/38] disable floating.pass.cpp on windows

---
 .../test/support/test.support/constexpr_hash/floating.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
index fc4c192a245467..c92c3014a14ce8 100644
--- a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
@@ -7,6 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++26
+// UNSUPPORTED: windows
+// FIXME: https://github.com/llvm/llvm-project/issues/214358
+// std::isnan, std::isinf, std::signbit are not constexpr on Windows
 
 // "support/constexpr_hash.h"
 

>From 2023dcb53812ce02493762391e5119f2042598ab Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Fri, 16 Oct 2026 22:43:23 +0100
Subject: [PATCH 34/38] attempt windows fix

---
 libcxx/test/support/constexpr_hash.h | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 7a7295f844c7bd..eb69977d565760 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -80,11 +80,28 @@ struct constexpr_hash<Key> {
     } else if constexpr (std::floating_point<Key>) {
       if (key == 0.0f)
         return 0;
-      if (std::isnan(key)) {
-        return 0xc3a5c85c97cb3127ULL;
-      }
-      if (std::isinf(key)) {
-        return std::signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
+      if constexpr (requires {
+                      std::isnan(key);
+                      std::isinf(key);
+                      std::signbit(key);
+                    }) {
+        if (std::isnan(key)) {
+          return 0xc3a5c85c97cb3127ULL;
+        }
+        if (std::isinf(key)) {
+          return std::signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
+        }
+      } else {
+        // FIXME:
+        // std::isnan, std::isinf, std::signbit is not constexpr on Windows
+        //  https://github.com/llvm/llvm-project/issues/105174
+
+        if (__builtin_isnan(key)) {
+          return 0xc3a5c85c97cb3127ULL;
+        }
+        if (__builtin_isinf(key)) {
+          return __builtin_signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
+        }
       }
 
       if constexpr (std::is_same_v<Key, long double>) {

>From 379a24d6efc1b37830c52173141265037f20345b Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 17 Oct 2026 09:42:37 +0100
Subject: [PATCH 35/38] reattempt windows fix

---
 libcxx/test/support/constexpr_hash.h                  | 11 +++++++----
 .../test.support/constexpr_hash/floating.pass.cpp     |  3 ---
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index eb69977d565760..072f39e967eff5 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -81,9 +81,12 @@ struct constexpr_hash<Key> {
       if (key == 0.0f)
         return 0;
       if constexpr (requires {
-                      std::isnan(key);
-                      std::isinf(key);
-                      std::signbit(key);
+                      requires([]() constexpr {
+                        (void)std::isnan(Key{});
+                        (void)std::isinf(Key{});
+                        (void)std::signbit(Key{});
+                        return true;
+                      })();
                     }) {
         if (std::isnan(key)) {
           return 0xc3a5c85c97cb3127ULL;
@@ -94,7 +97,7 @@ struct constexpr_hash<Key> {
       } else {
         // FIXME:
         // std::isnan, std::isinf, std::signbit is not constexpr on Windows
-        //  https://github.com/llvm/llvm-project/issues/105174
+        // https://github.com/llvm/llvm-project/issues/214358
 
         if (__builtin_isnan(key)) {
           return 0xc3a5c85c97cb3127ULL;
diff --git a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
index c92c3014a14ce8..fc4c192a245467 100644
--- a/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
+++ b/libcxx/test/support/test.support/constexpr_hash/floating.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++26
-// UNSUPPORTED: windows
-// FIXME: https://github.com/llvm/llvm-project/issues/214358
-// std::isnan, std::isinf, std::signbit are not constexpr on Windows
 
 // "support/constexpr_hash.h"
 

>From b2aab4a27dd61dd5aeb4abd36dfae5c65bab658d Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 17 Oct 2026 22:55:45 +0100
Subject: [PATCH 36/38] for _WIN32 use builtins

---
 libcxx/test/support/constexpr_hash.h | 44 ++++++++++++----------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 072f39e967eff5..5c9c27ab55db99 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -80,32 +80,26 @@ struct constexpr_hash<Key> {
     } else if constexpr (std::floating_point<Key>) {
       if (key == 0.0f)
         return 0;
-      if constexpr (requires {
-                      requires([]() constexpr {
-                        (void)std::isnan(Key{});
-                        (void)std::isinf(Key{});
-                        (void)std::signbit(Key{});
-                        return true;
-                      })();
-                    }) {
-        if (std::isnan(key)) {
-          return 0xc3a5c85c97cb3127ULL;
-        }
-        if (std::isinf(key)) {
-          return std::signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
-        }
-      } else {
-        // FIXME:
-        // std::isnan, std::isinf, std::signbit is not constexpr on Windows
-        // https://github.com/llvm/llvm-project/issues/214358
-
-        if (__builtin_isnan(key)) {
-          return 0xc3a5c85c97cb3127ULL;
-        }
-        if (__builtin_isinf(key)) {
-          return __builtin_signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
-        }
+#  if defined(_WIN32)
+      // FIXME:
+      // std::isnan, std::isinf, std::signbit is not constexpr on Windows
+      // https://github.com/llvm/llvm-project/issues/214358
+
+      if (__builtin_isnan(key)) {
+        return 0xc3a5c85c97cb3127ULL;
+      }
+      if (__builtin_isinf(key)) {
+        return __builtin_signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
+      }
+
+#  else
+      if (std::isnan(key)) {
+        return 0xc3a5c85c97cb3127ULL;
+      }
+      if (std::isinf(key)) {
+        return std::signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
       }
+#  endif
 
       if constexpr (std::is_same_v<Key, long double>) {
         // long double can have padding on certain platforms which are uninitialized, and hence, can't be used during constant evaluation

>From a03692f3f0f727611f2aca090e65ccbf13a86d04 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 18 Oct 2026 13:36:22 +0100
Subject: [PATCH 37/38] cast for 32 bit

---
 libcxx/test/support/constexpr_hash.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 5c9c27ab55db99..594f00e88251a3 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -86,10 +86,10 @@ struct constexpr_hash<Key> {
       // https://github.com/llvm/llvm-project/issues/214358
 
       if (__builtin_isnan(key)) {
-        return 0xc3a5c85c97cb3127ULL;
+        return (std::size_t)0xc3a5c85c97cb3127ULL;
       }
       if (__builtin_isinf(key)) {
-        return __builtin_signbit(key) ? 0xb492b66fbe98f273ULL : 0x9ae16a3b2f90404fULL;
+        return __builtin_signbit(key) ? (std::size_t)0xb492b66fbe98f273ULL : (std::size_t)0x9ae16a3b2f90404fULL;
       }
 
 #  else

>From 5eb8d2121291523ef37106d8cfab28b6747e55e8 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 18 Oct 2026 15:45:21 +0100
Subject: [PATCH 38/38] one cast

---
 libcxx/test/support/constexpr_hash.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/support/constexpr_hash.h b/libcxx/test/support/constexpr_hash.h
index 594f00e88251a3..449c93350598e4 100644
--- a/libcxx/test/support/constexpr_hash.h
+++ b/libcxx/test/support/constexpr_hash.h
@@ -60,7 +60,7 @@ concept UnqualifiedArithmeticOrEnum =
 template <size_t N>
 constexpr std::size_t array_hash(std::array<std::byte, N> arr) {
   // based on fnv-1
-  std::size_t res{14695981039346656037ULL};
+  std::size_t res = (std::size_t)14695981039346656037ULL;
   for (auto c : arr) {
     res ^= (std::size_t)c;
     res *= 1099511628211ULL;



More information about the libcxx-commits mailing list