[libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting allocators with explicit conversions (PR #208439)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 9 04:59:32 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Allocators are only required to be explicitly convertible between different types. `shared_ptr` currently requires implicit conversions, however.


---
Full diff: https://github.com/llvm/llvm-project/pull/208439.diff


2 Files Affected:

- (modified) libcxx/include/__memory/shared_ptr.h (+6-5) 
- (modified) libcxx/test/support/min_allocator.h (+6-6) 


``````````diff
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..4112c1175b9e1 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -658,8 +658,9 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __all
 
 template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
-  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> >;
+  using _ControlBlock   = __shared_ptr_emplace<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a), std::forward<_Args>(__args)...);
 }
 
 template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
@@ -671,9 +672,9 @@ template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> =
 
 template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ControlBlock =
-      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, remove_cv_t<_Tp>>;
+  using _ControlBlock   = __shared_ptr_emplace_for_overwrite<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a));
 }
 
 template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
diff --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h
index 6beffc1d2eb10..07603425f0668 100644
--- a/libcxx/test/support/min_allocator.h
+++ b/libcxx/test/support/min_allocator.h
@@ -29,7 +29,7 @@ class bare_allocator {
   bare_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
+  explicit bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
 
@@ -59,7 +59,7 @@ class no_default_allocator {
   typedef T value_type;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -102,7 +102,7 @@ class malloc_allocator : public malloc_allocator_base {
   malloc_allocator() TEST_NOEXCEPT { assert(!disable_default_constructor); }
 
   template <class U>
-  malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
+  explicit malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) {
     const std::size_t nbytes = n * sizeof(T);
@@ -410,7 +410,7 @@ class complete_type_allocator {
   TEST_CONSTEXPR_CXX20 complete_type_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -466,7 +466,7 @@ class safe_allocator {
   TEST_CONSTEXPR_CXX20 safe_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     T* memory = std::allocator<T>().allocate(n);
@@ -499,7 +499,7 @@ struct tiny_size_allocator {
   tiny_size_allocator() = default;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
+  TEST_CONSTEXPR_CXX20 explicit tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     assert(n <= MaxSize);

``````````

</details>


https://github.com/llvm/llvm-project/pull/208439


More information about the libcxx-commits mailing list