[libcxx-commits] [libcxx] [libc++] Implement P2077R3: Heterogeneous erasure overloads for associative containers (PR #174680)

Rafail Shakhin ogly via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jan 9 15:08:14 PST 2026


https://github.com/rsaddatimov updated https://github.com/llvm/llvm-project/pull/174680

>From 8878a3d6e144a2da25d2aea931affceb320a9aac Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 05:11:31 +0300
Subject: [PATCH 01/15] Initial commit: start with set & multiset tests

---
 libcxx/include/__tree                         |  23 ++--
 libcxx/include/map                            |  12 +-
 libcxx/include/set                            |  70 +++++++++-
 libcxx/include/unordered_map                  |  36 +++--
 libcxx/include/unordered_set                  |  36 +++--
 .../multiset/extract.transparent.pass.cpp     |  27 ++++
 .../erase.transparent.pass.cpp                |  27 ++++
 .../set/erase.transparent.pass.cpp            |  27 ++++
 .../set/extract.transparent.pass.cpp          |  27 ++++
 .../support/test_transparent_associative.hpp  | 124 ++++++++++++++++++
 10 files changed, 365 insertions(+), 44 deletions(-)
 create mode 100644 libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/support/test_transparent_associative.hpp

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index fbb48f8196964..6dca5e59e8172 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1188,10 +1188,10 @@ public:
   template <class _Comp2>
   _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const&);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p);
@@ -1426,7 +1426,8 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer __construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
+  __node_pointer
+  __construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
     if (!__src)
       return nullptr;
 
@@ -1471,7 +1472,8 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer __assign_from_tree(
+  __node_pointer
+  __assign_from_tree(
       __node_pointer __dest, __node_pointer __src, _Assignment __assign, _ConstructionAlg __construct_subtree) {
     if (!__src) {
       destroy(__dest);
@@ -1943,7 +1945,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr = __nh.__ptr_;
+  __node_pointer __ptr     = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{iterator(static_cast<__node_pointer>(__child)), false, std::move(__nh)};
@@ -1973,17 +1975,18 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+template <class _NodeHandle, class _Key>
+_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const _Key& __key) {
   iterator __it = find(__key);
   if (__it == end())
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__it);
+  return __node_handle_extract_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 03c92e152e04f..8ad70accd9b47 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1280,7 +1280,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(map<key_type, mapped_type, _Compare2, allocator_type>& __source) {
@@ -1339,9 +1339,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2,
-            enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
-                        int> = 0>
+  template <typename _K2>
+    requires __is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1880,7 +1879,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(multimap<key_type, mapped_type, _Compare2, allocator_type>& __source) {
@@ -1939,7 +1938,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
+  template <typename _K2>
+    requires __is_transparent_v<_Compare, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
diff --git a/libcxx/include/set b/libcxx/include/set
index 265c2f6cb6501..c931cf23525fa 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,13 +125,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -370,6 +376,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -377,6 +386,9 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
+    template <typename K>
+      size_type erase(K&& k); // C++23
+
     void clear() noexcept;
 
     template<class C2>
@@ -770,6 +782,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
   _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_unique(__k); }
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -787,8 +811,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -847,7 +883,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
+  template <typename _K2>
+    requires __is_transparent_v<_Compare, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1245,6 +1282,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
   _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1262,8 +1311,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1324,7 +1385,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
+  template <typename _K2>
+    requires __is_transparent_v<_Compare, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index ca53348eb5e2a..4de5ea87a19cb 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1216,11 +1216,13 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -1230,7 +1232,8 @@ public:
     return __table_.__count_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_unique(__k);
   }
@@ -1239,7 +1242,8 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1252,11 +1256,13 @@ public:
     return __table_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_unique(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_unique(__k);
   }
@@ -1936,11 +1942,13 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -1950,7 +1958,8 @@ public:
     return __table_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_multi(__k);
   }
@@ -1959,7 +1968,8 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1972,11 +1982,13 @@ public:
     return __table_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_multi(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_multi(__k);
   }
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 760f145091895..442c9ce26361f 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -844,11 +844,13 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -858,7 +860,8 @@ public:
     return __table_.__count_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_unique(__k);
   }
@@ -867,7 +870,8 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -880,11 +884,13 @@ public:
     return __table_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_unique(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_unique(__k);
   }
@@ -1443,11 +1449,13 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -1457,7 +1465,8 @@ public:
     return __table_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_multi(__k);
   }
@@ -1466,7 +1475,8 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1479,11 +1489,13 @@ public:
     return __table_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_multi(__k);
   }
-  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
+  template <class _K2>
+    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_multi(__k);
   }
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..fc04c8d85c267
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..a7252f0aa039c
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..d2ca1fe599374
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..62c72808b83d1
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
new file mode 100644
index 0000000000000..a59f10e426ecc
--- /dev/null
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_TRANSPARENT_ASSOCIATIVE_H
+#define TEST_TRANSPARENT_ASSOCIATIVE_H
+
+#include "test_macros.h"
+
+#include <cassert>
+
+#if TEST_STD_VER > 23
+
+template <typename T>
+struct StoredType;
+
+template <typename T>
+struct SearchedType {
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+
+  operator StoredType<T>() const {
+    ++*conversions_;
+    return StoredType<T>{value_};
+  }
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const SearchedType<T>&) const = default;
+
+private:
+  T value_;
+  int* conversions_;
+};
+
+template <typename T>
+struct StoredType {
+  StoredType() = default;
+  StoredType(T value) : value_(value) {}
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const StoredType<T>&) const = default;
+
+private:
+  T value_;
+};
+
+struct transparent_comparator_base {
+  using is_transparent = void;
+
+  template <typename T>
+  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs < rhs;
+  }
+};
+
+struct transparent_comparator_final final : public transparent_comparator_base {};
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#endif // TEST_STD_VER > 23
+
+#endif // TEST_TRANSPARENT_ASSOCIATIVE_H

>From a6ad52bcfc5d0d4fa8c471cec1162f80db0390c9 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 06:22:38 +0300
Subject: [PATCH 02/15] Fix test & version stuff

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/22.rst               |  1 +
 libcxx/docs/Status/Cxx23Papers.csv            |  2 +-
 libcxx/include/__tree                         |  6 ++--
 libcxx/include/version                        |  2 +-
 .../map.version.compile.pass.cpp              | 32 ++++++-------------
 .../set.version.compile.pass.cpp              | 32 ++++++-------------
 .../unordered_map.version.compile.pass.cpp    | 32 ++++++-------------
 .../unordered_set.version.compile.pass.cpp    | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../support/test_transparent_associative.hpp  |  4 +--
 .../generate_feature_test_macro_components.py |  1 -
 12 files changed, 58 insertions(+), 120 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..9beb5b511b5bb 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -314,7 +314,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_allocate_at_least``                            ``202302L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_associative_heterogeneous_erasure``            *unimplemented*
+    ``__cpp_lib_associative_heterogeneous_erasure``            ``202110L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_bind_back``                                    ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 3e2b3e6633bb1..f9313a6f5907b 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__) (The paper is partially implemented. ``zip_transform_view``,
   ``adjacent_view``, and ``adjacent_transform_view`` are implemented in this release)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..842bfb713a390 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|In Progress|","","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 6dca5e59e8172..8a12e75033d20 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1426,8 +1426,7 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
+  __node_pointer __construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
     if (!__src)
       return nullptr;
 
@@ -1472,8 +1471,7 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __assign_from_tree(
+  __node_pointer __assign_from_tree(
       __node_pointer __dest, __node_pointer __src, _Assignment __assign, _ConstructionAlg __construct_subtree) {
     if (!__src) {
       destroy(__dest);
diff --git a/libcxx/include/version b/libcxx/include/version
index 1d2422e64ba0f..7ea4f43b068cb 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -486,7 +486,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 23
 # define __cpp_lib_adaptor_iterator_pair_constructor    202106L
 # define __cpp_lib_allocate_at_least                    202302L
-// # define __cpp_lib_associative_heterogeneous_erasure    202110L
+# define __cpp_lib_associative_heterogeneous_erasure    202110L
 # define __cpp_lib_bind_back                            202202L
 # define __cpp_lib_byteswap                             202110L
 # define __cpp_lib_constexpr_bitset                     202207L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
index 3db3861c72b5c..806aba542b7e9 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
@@ -231,17 +231,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -312,17 +306,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index 5dc69f29d0ecd..abc32dad7e7c9 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -193,17 +193,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -254,17 +248,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
index 221d8aaebc14b..ba777e5434008 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
@@ -225,17 +225,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -306,17 +300,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
index d1c1335df7c80..b168215ea737b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
@@ -187,17 +187,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -248,17 +242,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d54e229a87fcb..00ce5753208ae 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -4600,17 +4600,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -6270,17 +6264,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index a59f10e426ecc..6ec13d29000ea 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -13,7 +13,7 @@
 
 #include <cassert>
 
-#if TEST_STD_VER > 23
+#if TEST_STD_VER >= 23
 
 template <typename T>
 struct StoredType;
@@ -119,6 +119,6 @@ void test_non_transparent_extract(Container c) {
   assert(conversions == 3);
 }
 
-#endif // TEST_STD_VER > 23
+#endif // TEST_STD_VER >= 23
 
 #endif // TEST_TRANSPARENT_ASSOCIATIVE_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 370bc5d26b535..213e5815a36a0 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -152,7 +152,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_associative_heterogeneous_erasure",
             "values": {"c++23": 202110},
             "headers": ["map", "set", "unordered_map", "unordered_set"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_associative_heterogeneous_insertion",

>From 49da61bd5ed84ab48a35a3607bdbced9edf13e69 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:07:16 +0300
Subject: [PATCH 03/15] Back to sfinae

---
 libcxx/include/map           |  8 ++++----
 libcxx/include/set           |  6 ++----
 libcxx/include/unordered_map | 36 ++++++++++++------------------------
 libcxx/include/unordered_set | 36 ++++++++++++------------------------
 4 files changed, 30 insertions(+), 56 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 8ad70accd9b47..dc40a652fad74 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1339,8 +1339,9 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2>
-    requires __is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>
+  template <typename _K2,
+            enable_if_t<__is_transparent_v<_Compare, _K2> || __is_transparently_comparable_v<_Compare, key_type, _K2>,
+                        int> = 0>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1938,8 +1939,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2>
-    requires __is_transparent_v<_Compare, _K2>
+  template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
diff --git a/libcxx/include/set b/libcxx/include/set
index c931cf23525fa..7854d585b6a19 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -883,8 +883,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2>
-    requires __is_transparent_v<_Compare, _K2>
+  template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1385,8 +1384,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
-  template <typename _K2>
-    requires __is_transparent_v<_Compare, _K2>
+  template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 4de5ea87a19cb..ca53348eb5e2a 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1216,13 +1216,11 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -1232,8 +1230,7 @@ public:
     return __table_.__count_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_unique(__k);
   }
@@ -1242,8 +1239,7 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1256,13 +1252,11 @@ public:
     return __table_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_unique(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_unique(__k);
   }
@@ -1942,13 +1936,11 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -1958,8 +1950,7 @@ public:
     return __table_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_multi(__k);
   }
@@ -1968,8 +1959,7 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1982,13 +1972,11 @@ public:
     return __table_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_multi(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_multi(__k);
   }
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 442c9ce26361f..760f145091895 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -844,13 +844,11 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -860,8 +858,7 @@ public:
     return __table_.__count_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_unique(__k);
   }
@@ -870,8 +867,7 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -884,13 +880,11 @@ public:
     return __table_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_unique(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_unique(__k);
   }
@@ -1449,13 +1443,11 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __table_.find(__k); }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __table_.find(__k); }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
     return __table_.find(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
     return __table_.find(__k);
   }
@@ -1465,8 +1457,7 @@ public:
     return __table_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
     return __table_.__count_multi(__k);
   }
@@ -1475,8 +1466,7 @@ public:
 #  if _LIBCPP_STD_VER >= 20
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
 
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
@@ -1489,13 +1479,11 @@ public:
     return __table_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 20
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
     return __table_.__equal_range_multi(__k);
   }
-  template <class _K2>
-    requires __is_transparent_v<hasher> && __is_transparent_v<key_equal>
+  template <class _K2, enable_if_t<__is_transparent_v<hasher, _K2> && __is_transparent_v<key_equal, _K2>>* = nullptr>
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
     return __table_.__equal_range_multi(__k);
   }

>From d79bff3f7437967e47f143f060b9254d5ba86669 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:10:53 +0300
Subject: [PATCH 04/15] Don't uglify nodiscard

---
 libcxx/include/set | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 7854d585b6a19..cb66dd8b3d745 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -819,7 +819,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 
@@ -1318,7 +1318,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 

>From 150452dc2f0be2caaf0cdd4a591ae6e0907454c7 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:29:05 +0300
Subject: [PATCH 05/15] Overloads for map & multimap

---
 libcxx/include/map                            | 59 +++++++++++++++++++
 .../map.erasure/erase.transparent.pass.cpp    | 28 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 .../erase.transparent.pass.cpp                | 29 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 5 files changed, 170 insertions(+)
 create mode 100644 libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/map b/libcxx/include/map
index dc40a652fad74..1993e2bbdeb21 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -149,6 +149,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -172,7 +175,10 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -428,13 +434,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -1263,6 +1275,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f.__i_, __l.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1282,6 +1306,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(map<key_type, mapped_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1865,6 +1901,17 @@ public:
     return __tree_.erase(__f.__i_, __l.__i_);
   }
 
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
 #  if _LIBCPP_STD_VER >= 17
   _LIBCPP_HIDE_FROM_ABI iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
@@ -1882,6 +1929,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(multimap<key_type, mapped_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2fe83c20acf67
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..0dd4d9afdeb9b
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..7e3f625370d73
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..9faa74f69e2b0
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}

>From 3484676c0a7a01052369870323e4131332ab9a75 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 18:12:03 +0300
Subject: [PATCH 06/15] Overloads for unordered_map & unordered_multimap

---
 libcxx/include/__hash_table                   | 16 +--
 libcxx/include/unordered_map                  | 62 +++++++++++-
 libcxx/include/unordered_set                  |  4 +-
 .../unord.map/erase.transparent.pass.cpp      | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../unord.multimap/erase.transparent.pass.cpp | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../test/support/test_transparent_unordered.h | 99 ++++++++++++++-----
 8 files changed, 366 insertions(+), 39 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ef487fb06dd5e..ff52ac5c01f2d 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -882,10 +882,10 @@ public:
   template <class _Table>
   _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(_Table& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const& __key);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1634,18 +1634,18 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_insert_unique(const_iter
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
-template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(key_type const& __key) {
+template <class _NodeHandle, class _Key>
+_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const _Key& __key) {
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__i);
+  return __node_handle_extract_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _NodeHandle
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index ca53348eb5e2a..7268088ffd298 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,6 +139,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      size_type erase(K&& k);                                                         // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
 
@@ -162,7 +165,10 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -425,13 +431,19 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
+    template <typename K>
+      node_type extract(K&& k);                                // C++23
+
     iterator insert(node_type&& nh);                           // C++17
     iterator insert(const_iterator hint, node_type&& nh);      // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -1158,6 +1170,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1175,9 +1199,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_map<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1878,6 +1913,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1895,9 +1942,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multimap<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 760f145091895..0347915499de7 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -805,7 +805,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
   template <class _H2, class _P2>
@@ -1394,7 +1394,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract<node_type>(__position);
+    return __table_.template __node_handle_extract_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2d0138ef6472f
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..08a2e05e777bc
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..b15d915a618d9
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..8eefc2b5f689b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index e2d02402d808b..f77688b42e194 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,48 +55,40 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
-  int *conversions_;
+  int* conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) { }
+  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
-    return lhs.value_ == rhs.value_;
-  }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
-    return lhs.value_ == rhs.get_value();
-  }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
 };
 
-template<template<class...> class UnorderedSet, class Hash, class Equal>
+template <template <class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template<template<class...> class UnorderedMap, class Hash, class Equal>
+template <template <class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template<class Container>
+template <class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -105,7 +97,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -116,7 +108,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -125,7 +117,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -136,7 +128,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -145,7 +137,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -156,10 +148,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -168,10 +160,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
@@ -182,6 +174,59 @@ void test_non_transparent_equal_range(Container c) {
   assert(conversions == 3);
 }
 
+#  if TEST_STD_VER >= 23
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#  endif // TEST_STD_VER >= 23
+
 #endif // TEST_STD_VER > 17
 
 #endif // TEST_TRANSPARENT_UNORDERED_H

>From db5d7dd8fcafce65dadd871e8af2141cba47816e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:12:42 +0300
Subject: [PATCH 07/15] Overloads for unordered_set & unordered_multiset

---
 libcxx/include/unordered_map                  |  2 +-
 libcxx/include/unordered_set                  | 58 +++++++++++++++++++
 .../unord.multiset/erase.transparent.pass.cpp | 56 ++++++++++++++++++
 .../extract.transparent.pass.cpp              | 56 ++++++++++++++++++
 .../unord.set/erase.transparent.pass.cpp      | 56 ++++++++++++++++++
 .../unord.set/extract.transparent.pass.cpp    | 56 ++++++++++++++++++
 .../test/support/test_transparent_unordered.h |  4 +-
 7 files changed, 285 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp

diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 7268088ffd298..2c6208ccebdcb 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -140,7 +140,7 @@ public:
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
     template <typename K>
-      size_type erase(K&& k);                                                         // C++23
+      node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 0347915499de7..97a31a3b44384 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,13 +131,19 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                       // C++23
+
     insert_return_type insert(node_type&& nh);                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -379,13 +385,19 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
+    template <typename K>
+      node_type extract(K&& k);                             // C++23
+
     iterator insert(node_type&& nh);                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -788,6 +800,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -808,6 +832,17 @@ public:
     return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_set<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1400,6 +1435,17 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multiset<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1431,6 +1477,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI void swap(unordered_multiset& __u) _NOEXCEPT_(__is_nothrow_swappable_v<__table>) {
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..41420d41613b1
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e094201a8cedd
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..efc55a0ea8964
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e84facdaabcb0
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f77688b42e194..f2439be716d44 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -55,7 +55,7 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;
@@ -76,7 +76,7 @@ struct StoredType {
   // in a container full of StoredType<T>.
   friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;

>From f3740a8c3f7d9db6a65722c3eed23142a289affc Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:22:02 +0300
Subject: [PATCH 08/15] nodiscard test cases

---
 .../containers/associative/map/map.nodiscard.verify.cpp       | 4 ++++
 .../containers/associative/map/multimap.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/multiset.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/set.nodiscard.verify.cpp       | 4 ++++
 .../libcxx/containers/unord/unord.map/nodiscard.verify.cpp    | 4 ++++
 .../containers/unord/unord.multimap/nodiscard.verify.cpp      | 4 ++++
 .../containers/unord/unord.multiset/nodiscard.verify.cpp      | 4 ++++
 .../libcxx/containers/unord/unord.set/nodiscard.verify.cpp    | 4 ++++
 8 files changed, 32 insertions(+)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 20218b50e6c60..7a418502a722b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -121,4 +121,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 99e4fbceabf12..49ac8c6a76064 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -106,4 +106,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index 889b3cc62d191..a90f69fc96af4 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 0453541d1b862..02f2634363679 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 0747556905200..34df0f1d44335 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index cdb47d2b514ab..9ce6bb6a5d46c 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index ff62874caf01b..dbb10eaa40dd3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -83,6 +83,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index 628c31a93099f..d289d0ebfbfd0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -82,6 +82,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From ebce6d88a6e8d9f71a687bbf668924f4f339c1e7 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 18:55:54 +0300
Subject: [PATCH 09/15] Refactor nodiscard tests

---
 .../associative/map/map.nodiscard.verify.cpp   | 18 +++++++++---------
 .../map/multimap.nodiscard.verify.cpp          | 18 +++++++++---------
 .../set/multiset.nodiscard.verify.cpp          | 18 +++++++++---------
 .../associative/set/set.nodiscard.verify.cpp   | 18 +++++++++---------
 .../unord/unord.map/nodiscard.verify.cpp       | 18 +++++++++---------
 .../unord/unord.multimap/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.multiset/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.set/nodiscard.verify.cpp       | 17 +++++++++--------
 8 files changed, 72 insertions(+), 71 deletions(-)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 7a418502a722b..c21456965190b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -74,19 +74,23 @@ void test() {
   m.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.value_comp();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
+#if TEST_STD_VER >= 14
+  std::map<int, int, TransparentCompare> tm;
+  const std::map<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::map<int, int, TransparentCompare> tm;
-  const std::map<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -121,8 +125,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 49ac8c6a76064..bb38153b22a8e 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -59,19 +59,23 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multimap<int, int, TransparentCompare> tm;
+  const std::multimap<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multimap<int, int, TransparentCompare> tm;
-  const std::multimap<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -106,8 +110,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index a90f69fc96af4..3a762b059e89a 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multiset<int, TransparentCompare> ts;
+  const std::multiset<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multiset<int, TransparentCompare> ts;
-  const std::multiset<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 02f2634363679..acee396bfbb99 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::set<int, TransparentCompare> ts;
+  const std::set<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::set<int, TransparentCompare> ts;
-  const std::set<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 34df0f1d44335..19e6764c1f064 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index 9ce6bb6a5d46c..511d9e2062d48 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index dbb10eaa40dd3..1f0e76a412017 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -83,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index d289d0ebfbfd0..2a3aca497e8c4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,10 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -82,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From 08a3e50d699b32feff814d6b4ac234d785601155 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:02:05 +0300
Subject: [PATCH 10/15] Drop reformatting

---
 libcxx/include/__tree                         |  2 +-
 .../test/support/test_transparent_unordered.h | 46 +++++++++++--------
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 8a12e75033d20..6d1a2a3520b98 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1943,7 +1943,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr     = __nh.__ptr_;
+  __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{iterator(static_cast<__node_pointer>(__child)), false, std::move(__nh)};
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f2439be716d44..5aee507191351 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,40 +55,48 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
-  int* conversions_;
+  int *conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
+  StoredType(T value) : value_(value) { }
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
+    return lhs.value_ == rhs.value_;
+  }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
 };
 
-template <template <class...> class UnorderedSet, class Hash, class Equal>
+template<template<class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template <template <class...> class UnorderedMap, class Hash, class Equal>
+template<template<class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template <class Container>
+template<class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -97,7 +105,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -108,7 +116,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -117,7 +125,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -128,7 +136,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -137,7 +145,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -148,10 +156,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -160,10 +168,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));

>From 1a6df126bfe6da1229d89fb9fd3a1dd191d64eeb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:07:29 +0300
Subject: [PATCH 11/15] typename ==> class

---
 libcxx/include/map           | 4 ++--
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 1993e2bbdeb21..70bd73b652bc3 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -149,7 +149,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -434,7 +434,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/set b/libcxx/include/set
index cb66dd8b3d745..43dc1733ce32b 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,7 +125,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -376,7 +376,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 2c6208ccebdcb..010fef2381737 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,7 +139,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -431,7 +431,7 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                // C++23
 
     iterator insert(node_type&& nh);                           // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 97a31a3b44384..f2db77a570299 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,7 +131,7 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                       // C++23
 
     insert_return_type insert(node_type&& nh);                        // C++17

>From 4a346311a18cfffb667fd5b05e17262d8eda93fb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:11:11 +0300
Subject: [PATCH 12/15] Better extract testing

---
 .../support/test_transparent_associative.hpp  | 56 ++++++++++++++-----
 .../test/support/test_transparent_unordered.h | 50 ++++++++++++-----
 2 files changed, 77 insertions(+), 29 deletions(-)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 6ec13d29000ea..2448f3cd16083 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -41,6 +41,10 @@ struct StoredType {
   StoredType() = default;
   StoredType(T value) : value_(value) {}
 
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+
   T get_value() const { return value_; }
 
   auto operator<=>(const StoredType<T>&) const = default;
@@ -72,17 +76,17 @@ struct transparent_comparator_final final : public transparent_comparator_base {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
+
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
-
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -96,25 +100,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 5aee507191351..1bc6fb07dee6f 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -186,17 +186,17 @@ void test_non_transparent_equal_range(Container c) {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
 
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -210,25 +210,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }

>From 5e04e94abc521f32055d3935443c190ec7e4b6a2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:43:59 +0300
Subject: [PATCH 13/15] Include concepts in test

---
 libcxx/test/support/test_transparent_associative.hpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 2448f3cd16083..dae3acc725162 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -15,6 +15,8 @@
 
 #if TEST_STD_VER >= 23
 
+#  include <concepts>
+
 template <typename T>
 struct StoredType;
 

>From 267eaf6a9a09b7fc5974cb0da85df5262f2e06eb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 21:04:33 +0300
Subject: [PATCH 14/15] Include type_traits in test

---
 libcxx/test/support/test_transparent_associative.hpp | 1 +
 libcxx/test/support/test_transparent_unordered.h     | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index dae3acc725162..e3cac5e8a51cb 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -16,6 +16,7 @@
 #if TEST_STD_VER >= 23
 
 #  include <concepts>
+#  include <type_traits>
 
 template <typename T>
 struct StoredType;
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 1bc6fb07dee6f..14faea4d8e5b7 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -16,6 +16,9 @@
 
 #if TEST_STD_VER > 17
 
+#  include <concepts>
+#  include <type_traits>
+
 template <typename T>
 struct StoredType;
 

>From dc248de46a602b7c576a4cbd80e33736ddd653f3 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:07:55 +0300
Subject: [PATCH 15/15] Correct header suffix

---
 .../associative/map/map.erasure/erase.transparent.pass.cpp    | 4 ++--
 .../map/map.modifiers/extract.transparent.pass.cpp            | 4 ++--
 .../multimap/multimap.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../multimap/multimap.modifiers/extract.transparent.pass.cpp  | 4 ++--
 .../associative/multiset/extract.transparent.pass.cpp         | 4 ++--
 .../multiset/multiset.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../containers/associative/set/extract.transparent.pass.cpp   | 4 ++--
 .../set/{ => set.erasure}/erase.transparent.pass.cpp          | 4 ++--
 .../std/containers/unord/unord.map/erase.transparent.pass.cpp | 2 +-
 .../unord.map.modifiers/extract.transparent.pass.cpp          | 2 +-
 .../unord/unord.multimap/erase.transparent.pass.cpp           | 2 +-
 .../unord.multimap.modifiers/extract.transparent.pass.cpp     | 2 +-
 .../unord/unord.multiset/erase.transparent.pass.cpp           | 2 +-
 .../unord/unord.multiset/extract.transparent.pass.cpp         | 2 +-
 .../std/containers/unord/unord.set/erase.transparent.pass.cpp | 2 +-
 .../containers/unord/unord.set/extract.transparent.pass.cpp   | 2 +-
 ...sparent_associative.hpp => test_transparent_associative.h} | 0
 17 files changed, 24 insertions(+), 24 deletions(-)
 rename libcxx/test/std/containers/associative/set/{ => set.erasure}/erase.transparent.pass.cpp (91%)
 rename libcxx/test/support/{test_transparent_associative.hpp => test_transparent_associative.h} (100%)

diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index 2fe83c20acf67..c8cfb77069d62 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 0dd4d9afdeb9b..97cdcda5e519e 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 7e3f625370d73..9a257b82b9388 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 9faa74f69e2b0..f467d28996ed7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
index fc04c8d85c267..f05905e7483a1 100644
--- a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
index a7252f0aa039c..2669b07e8fd8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 62c72808b83d1..0b036971e119e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
similarity index 91%
rename from libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
rename to libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index d2ca1fe599374..87e99da2a9bef 100644
--- a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
index 2d0138ef6472f..edbaec04d3329 100644
--- a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
index 08a2e05e777bc..a14dca09159ac 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
index b15d915a618d9..e09b66da0ea10 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
index 8eefc2b5f689b..4af37297ec0ae 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
index 41420d41613b1..3fa33f111b1c8 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
index e094201a8cedd..9ec51b55d2278 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
index efc55a0ea8964..944faf091d5df 100644
--- a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
index e84facdaabcb0..06ad1deaf05d1 100644
--- a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.h
similarity index 100%
rename from libcxx/test/support/test_transparent_associative.hpp
rename to libcxx/test/support/test_transparent_associative.h



More information about the libcxx-commits mailing list