Skip to content

[libc++] Implement P1901R2: owner_hash and owner_equal for shared_ptr/weak_ptr#210551

Open
firedog1234 wants to merge 3 commits into
llvm:mainfrom
firedog1234:p1901r2-owner-hash-equal
Open

[libc++] Implement P1901R2: owner_hash and owner_equal for shared_ptr/weak_ptr#210551
firedog1234 wants to merge 3 commits into
llvm:mainfrom
firedog1234:p1901r2-owner-hash-equal

Conversation

@firedog1234

Copy link
Copy Markdown
Contributor

Summary

Implements P1901R2 (C++26), adding ownership-based hashing and equality support for std::shared_ptr and std::weak_ptr.

This change:

  • Implements shared_ptr::owner_hash() / weak_ptr::owner_hash()
  • Implements shared_ptr::owner_equal() / weak_ptr::owner_equal()
  • Adds the free function objects std::owner_hash and std::owner_equal
  • Enables std::weak_ptr to be used as keys in unordered associative containers

Fixes #105372.

Test Plan

  • Added libc++ tests for:
    • util.smartptr.shared.obs/
    • util.smartptr.weak.obs/
    • util.smartptr.ownerhash/
    • util.smartptr.ownerequal/

Acknowledgements

Claude assisted with test development and documentation.

@firedog1234
firedog1234 requested a review from a team as a code owner July 18, 2026 21:44
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 18, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-libcxx

Author: Avi Patel (firedog1234)

Changes

Summary

Implements P1901R2 (C++26), adding ownership-based hashing and equality support for std::shared_ptr and std::weak_ptr.

This change:

  • Implements shared_ptr::owner_hash() / weak_ptr::owner_hash()
  • Implements shared_ptr::owner_equal() / weak_ptr::owner_equal()
  • Adds the free function objects std::owner_hash and std::owner_equal
  • Enables std::weak_ptr to be used as keys in unordered associative containers

Fixes #105372.

Test Plan

  • Added libc++ tests for:
    • util.smartptr.shared.obs/
    • util.smartptr.weak.obs/
    • util.smartptr.ownerhash/
    • util.smartptr.ownerequal/

Acknowledgements

Claude assisted with test development and documentation.


Patch is 27.09 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/210551.diff

17 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/ReleaseNotes/23.rst (+1)
  • (modified) libcxx/docs/Status/Cxx26Papers.csv (+1-1)
  • (modified) libcxx/include/__memory/shared_ptr.h (+71)
  • (modified) libcxx/include/memory (+32)
  • (modified) libcxx/include/version (+1-1)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp (+5-11)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+5-11)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp (+40)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp (+41)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp (+44)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp (+69)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp (+49)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp (+37)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp (+42)
  • (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp (+34)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (-1)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..8a26828240de4 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -526,7 +526,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_senders``                                      *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_smart_ptr_owner_equality``                     *unimplemented*
+    ``__cpp_lib_smart_ptr_owner_equality``                     ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_span_at``                                      ``202311L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..1dee9843c9c55 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..98135f198110c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -12,7 +12,7 @@
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
-"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
+"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","|Complete|","23","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
 "`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..0770f2a8e383d 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -18,6 +18,7 @@
 #include <__cstddef/ptrdiff_t.h>
 #include <__exception/exception.h>
 #include <__functional/binary_function.h>
+#include <__functional/hash.h>
 #include <__functional/operations.h>
 #include <__functional/reference_wrapper.h>
 #include <__fwd/ostream.h>
@@ -555,6 +556,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
 
   _LIBCPP_HIDE_FROM_ABI bool __owner_equivalent(const shared_ptr& __p) const { return __cntrl_ == __p.__cntrl_; }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
 #if _LIBCPP_STD_VER >= 17
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __add_lvalue_reference_t<element_type> operator[](ptrdiff_t __i) const {
     static_assert(is_array<_Tp>::value, "std::shared_ptr<T>::operator[] is only valid when T is an array type.");
@@ -1244,6 +1261,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr {
     return __cntrl_ < __r.__cntrl_;
   }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
   template <class _Up>
   friend class weak_ptr;
   template <class _Up>
@@ -1317,6 +1350,44 @@ struct owner_less<void> {
 };
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+struct owner_hash {
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(shared_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(weak_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  typedef void is_transparent;
+};
+
+struct owner_equal {
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  typedef void is_transparent;
+};
+#endif
+
 template <class _Tp>
 class enable_shared_from_this {
   mutable weak_ptr<_Tp> __weak_this_;
diff --git a/libcxx/include/memory b/libcxx/include/memory
index d5d48ed9a9e6d..03636ddb839b0 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -633,6 +633,9 @@ public:
     explicit operator bool() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -771,6 +774,9 @@ public:
     shared_ptr<T> lock() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -817,6 +823,32 @@ struct owner_less<void>
     typedef void is_transparent;
 };
 
+// class owner_hash:                                                          // C++26
+struct owner_hash
+{
+    template <class _Tp>
+    size_t operator()(shared_ptr<_Tp> const& __p) const noexcept;
+    template <class _Tp>
+    size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;
+
+    typedef void is_transparent;
+};
+
+// class owner_equal:                                                         // C++26
+struct owner_equal
+{
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+
+    typedef void is_transparent;
+};
+
 template<class T>
 class enable_shared_from_this
 {
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..31b5cc4048e18 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -628,7 +628,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
-// # define __cpp_lib_smart_ptr_owner_equality             202306L
+# define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
 # define __cpp_lib_sstream_from_string_view             202306L
 # define __cpp_lib_string_subview                       202506L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index c58357a463ffd..dc14ff766ac5c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -648,17 +648,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_to_address
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 f2d74cc99d864..11b181a66c518 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
@@ -8024,17 +8024,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_source_location
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
new file mode 100644
index 0000000000000..0f8c6935ef811
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::shared_ptr<void> empty1;
+  const std::shared_ptr<long> empty2;
+
+  assert(p1.owner_equal(p2));
+  assert(p2.owner_equal(p1));
+  assert(!p1.owner_equal(p3));
+  assert(!p3.owner_equal(p1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!p1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(p2));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
new file mode 100644
index 0000000000000..6ac9f57e07a05
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+  const std::shared_ptr<void> empty_sp;
+  const std::weak_ptr<long> empty_wp;
+
+  assert(p1.owner_equal(w1));
+  assert(p2.owner_equal(w1));
+  assert(!p1.owner_equal(w3));
+  assert(!p3.owner_equal(w1));
+
+  assert(empty_sp.owner_equal(empty_wp));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(w1)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(w1));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..57967b9369fe3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// shared_ptr
+
+// size_t owner_hash() const noexcept;
+
+#include <memory>
+#include <cassert>
+#include <cstddef>
+#include "test_macros.h"
+
+struct Pair {
+  int a;
+  int b;
+};
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::weak_ptr<int> w1(p1);
+
+  assert(p1.owner_hash() == p2.owner_hash());
+  assert(p1.owner_hash() == w1.owner_hash());
+
+  const std::shared_ptr<Pair> sp(new Pair{1, 2});
+  const std::shared_ptr<int> alias(sp, &sp->b);
+  assert(static_cast<void*>(sp.get()) != static_cast<void*>(alias.get()));
+  assert(sp.owner_hash() == alias.owner_hash());
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_hash()), std::size_t);
+  ASSERT_NOEXCEPT(p1.owner_hash());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
new file mode 100644
index 0000000000000..1c6c9e8bd5d67
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// struct owner_equal
+// {
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//
+//     typedef unspecified is_transparent;
+// };
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+#include <unordered_set>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+
+  std::owner_equal oe;
+
+  assert(oe(p1, p2));
+  assert(oe(p1, w1));
+  assert(oe(w1, p1));
+  assert(oe(w1, w1));
+
+  assert(!oe(p1, p3));
+  assert(!oe(p1, w3));
+  assert(!oe(w1, p3));
+  assert(!oe(w1, w3));
+
+  ASSERT_SAME_TYPE(decltype(oe(p1, p2)), bool);
+  ASSERT_NOEXCEPT(oe(p1, p2));
+  ASSERT_NOEXCEPT(oe(p1, w1));
+  ASSERT_NOEXCEPT(oe(w1, p1));
+  ASSERT_NOEXCEPT(oe(w1, w1));
+
+  static_assert(std::is_same<std::owner_equal::is_transparent, void>::value, "");
+
+  {
+    std::unordered_set<std::weak_p...
[truncated]

@firedog1234

firedog1234 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@frederick-vs-ja Would you be able to review this implementation? Thanks.

Comment thread libcxx/docs/ReleaseNotes/23.rst Outdated
- P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
- P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
- P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
- P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should be added to 24.rst because we won't backport this to LLVM 23.

Comment thread libcxx/docs/Status/Cxx26Papers.csv Outdated
Comment thread libcxx/include/__memory/shared_ptr.h Outdated
Comment thread libcxx/include/__memory/shared_ptr.h Outdated
Comment thread libcxx/include/memory Outdated
template <class _Tp>
size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;

typedef void is_transparent;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
typedef void is_transparent;
using is_transparent = unspecified;

The standard doesn't specify the actual type. See [util.smartptr.owner.hash] and [util.smartptr.owner.equal].

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The synopsis should be copied from the standard preferably.

@frederick-vs-ja

Copy link
Copy Markdown
Contributor

Ah, we should also modify libcxx/modules/std/memory.inc to make the new functors usable in standard library modules.

@firedog1234

Copy link
Copy Markdown
Contributor Author

Got it. Thanks for the review! i'll add updates.

Comment thread libcxx/include/__memory/shared_ptr.h Outdated
Comment thread libcxx/include/memory Outdated
Comment on lines +636 to +638
size_t owner_hash() const noexcept; // C++26
template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept; // C++26

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
size_t owner_hash() const noexcept; // C++26
template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept; // C++26
size_t owner_hash() const noexcept; // C++26
template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept; // C++26

Nit: please ident. Also bellow.

Comment thread libcxx/include/memory Outdated
template <class _Tp>
size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;

typedef void is_transparent;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The synopsis should be copied from the standard preferably.

Comment on lines +17 to +19
#include <memory>
#include <cassert>
#include "test_macros.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <memory>
#include <cassert>
#include "test_macros.h"
#include <cassert>
#include <memory>
#include "test_macros.h"

#include's should be sorted and grouped manually. Here and bellow.

Comment on lines +28 to +37
assert(p1.owner_equal(p2));
assert(p2.owner_equal(p1));
assert(!p1.owner_equal(p3));
assert(!p3.owner_equal(p1));

assert(empty1.owner_equal(empty2));
assert(!p1.owner_equal(empty1));

ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
ASSERT_NOEXCEPT(p1.owner_equal(p2));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The above is correct but in newer test when possible we prefer to use the non-macro versions. For example the following patterns:

Suggested change
assert(p1.owner_equal(p2));
assert(p2.owner_equal(p1));
assert(!p1.owner_equal(p3));
assert(!p3.owner_equal(p1));
assert(empty1.owner_equal(empty2));
assert(!p1.owner_equal(empty1));
ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
ASSERT_NOEXCEPT(p1.owner_equal(p2));
std::same_as<bool> decltype(auto) result = p1.owner(p2);
assert(result));
static_assert(noexcept(p1.owner_equal(p2)));
...etc...

There is no specific order required but you can order them for easier reading.

@firedog1234

Copy link
Copy Markdown
Contributor Author

Thanks guys for the review! I pushed updates in a new commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P1901R2: Enabling the Use of weak_ptr as Keys in Unordered Associative Containers

3 participants