[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