[libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_COMPRESSED_PAIR/TRIPLE from shared_ptr (PR #200401)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Fri May 29 07:02:45 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/200401
>From eeee53e97186f145b482898f0381c894bfe0a0b5 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 26 May 2026 08:47:26 +0200
Subject: [PATCH] [libc++] Remove _LIBCPP_COMPRESSED_PAIR/TRIPLE from
shared_ptr
---
libcxx/docs/ABIGuarantees.rst | 2 +-
libcxx/include/__configuration/attributes.h | 2 +
libcxx/include/__memory/compressed_pair.h | 25 --
libcxx/include/__memory/shared_ptr.h | 134 +++++-----
.../libcxx.control_block_layout.pass.cpp | 232 ------------------
.../allocate_shared.lwg2070.pass.cpp | 25 +-
6 files changed, 77 insertions(+), 343 deletions(-)
delete mode 100644 libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp
diff --git a/libcxx/docs/ABIGuarantees.rst b/libcxx/docs/ABIGuarantees.rst
index 4bd9737542a43..87ba69401c7ad 100644
--- a/libcxx/docs/ABIGuarantees.rst
+++ b/libcxx/docs/ABIGuarantees.rst
@@ -67,7 +67,7 @@ removes these workarounds for platforms that don't care about ABI compatibility.
``_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING``
------------------------------------------
-This removes artificial padding from ``_LIBCPP_COMPRESSED_PAIR`` and ``_LIBCPP_COMPRESSED_TRIPLE``.
+This removes artificial padding from ``_LIBCPP_COMPRESSED_PAIR``.
These macros are used inside the associative and unordered containers, ``deque``, ``forward_list``, ``future``,
``list``, ``basic_string``, ``function``, ``shared_ptr``, ``unique_ptr``, and ``vector`` to stay ABI compatible with the
diff --git a/libcxx/include/__configuration/attributes.h b/libcxx/include/__configuration/attributes.h
index cc828466482fd..71ab098e20744 100644
--- a/libcxx/include/__configuration/attributes.h
+++ b/libcxx/include/__configuration/attributes.h
@@ -215,6 +215,8 @@
#endif
#define _LIBCPP_HIDE_FROM_ABI_VIRTUAL _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION
+#define _LIBCPP_HIDE_STRUCT_FROM_ABI [[__gnu__::__abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE))]]
+
// Optional attributes
// -------------------
diff --git a/libcxx/include/__memory/compressed_pair.h b/libcxx/include/__memory/compressed_pair.h
index f1f1c920453cf..bb745620b9f85 100644
--- a/libcxx/include/__memory/compressed_pair.h
+++ b/libcxx/include/__memory/compressed_pair.h
@@ -94,16 +94,6 @@ class __compressed_pair_padding<_ToPad, true> {};
_LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T1> _LIBCPP_CONCAT3(__padding1_, __LINE__, _); \
_LIBCPP_NO_UNIQUE_ADDRESS T2 Initializer2; \
_LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T2> _LIBCPP_CONCAT3(__padding2_, __LINE__, _)
-
-# define _LIBCPP_COMPRESSED_TRIPLE(T1, Initializer1, T2, Initializer2, T3, Initializer3) \
- _LIBCPP_NO_UNIQUE_ADDRESS \
- __attribute__((__aligned__(::std::__compressed_pair_alignment<T2>), \
- __aligned__(::std::__compressed_pair_alignment<T3>))) T1 Initializer1; \
- _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T1> _LIBCPP_CONCAT3(__padding1_, __LINE__, _); \
- _LIBCPP_NO_UNIQUE_ADDRESS T2 Initializer2; \
- _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T2> _LIBCPP_CONCAT3(__padding2_, __LINE__, _); \
- _LIBCPP_NO_UNIQUE_ADDRESS T3 Initializer3; \
- _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T3> _LIBCPP_CONCAT3(__padding3_, __LINE__, _)
# else
# define _LIBCPP_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
struct { \
@@ -112,16 +102,6 @@ class __compressed_pair_padding<_ToPad, true> {};
_LIBCPP_NO_UNIQUE_ADDRESS T2 Initializer2; \
_LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T2> _LIBCPP_CONCAT3(__padding2_, __LINE__, _); \
}
-
-# define _LIBCPP_COMPRESSED_TRIPLE(T1, Initializer1, T2, Initializer2, T3, Initializer3) \
- struct { \
- _LIBCPP_NO_UNIQUE_ADDRESS T1 Initializer1; \
- _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T1> _LIBCPP_CONCAT3(__padding1_, __LINE__, _); \
- _LIBCPP_NO_UNIQUE_ADDRESS T2 Initializer2; \
- _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T2> _LIBCPP_CONCAT3(__padding2_, __LINE__, _); \
- _LIBCPP_NO_UNIQUE_ADDRESS T3 Initializer3; \
- _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T3> _LIBCPP_CONCAT3(__padding3_, __LINE__, _); \
- }
# endif
#else
@@ -130,11 +110,6 @@ class __compressed_pair_padding<_ToPad, true> {};
# define _LIBCPP_COMPRESSED_PAIR(T1, Name1, T2, Name2) \
_LIBCPP_NO_UNIQUE_ADDRESS T1 Name1; \
_LIBCPP_NO_UNIQUE_ADDRESS T2 Name2
-
-# define _LIBCPP_COMPRESSED_TRIPLE(T1, Name1, T2, Name2, T3, Name3) \
- _LIBCPP_NO_UNIQUE_ADDRESS T1 Name1; \
- _LIBCPP_NO_UNIQUE_ADDRESS T2 Name2; \
- _LIBCPP_NO_UNIQUE_ADDRESS T3 Name3
#endif // _LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 66442c8868005..a77dcdbb4392c 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -27,7 +27,6 @@
#include <__memory/allocator_destructor.h>
#include <__memory/allocator_traits.h>
#include <__memory/auto_ptr.h>
-#include <__memory/compressed_pair.h>
#include <__memory/construct_at.h>
#include <__memory/destroy.h>
#include <__memory/pointer_traits.h>
@@ -94,12 +93,16 @@ template <class _Tp>
class weak_ptr;
template <class _Tp, class _Dp, class _Alloc>
-class __shared_ptr_pointer : public __shared_weak_count {
- _LIBCPP_COMPRESSED_TRIPLE(_Tp, __ptr_, _Dp, __deleter_, _Alloc, __alloc_);
+class _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_pointer final : public __shared_weak_count {
+ using __alloc_t = __allocator_traits_rebind_t<_Alloc, __shared_ptr_pointer>;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
+ _LIBCPP_NO_UNIQUE_ADDRESS _Dp __deleter_;
+ _LIBCPP_NO_UNIQUE_ADDRESS _Tp __ptr_;
public:
_LIBCPP_HIDE_FROM_ABI __shared_ptr_pointer(_Tp __p, _Dp __d, _Alloc __a)
- : __ptr_(__p), __deleter_(std::move(__d)), __alloc_(std::move(__a)) {}
+ : __alloc_(std::move(__a)), __deleter_(std::move(__d)), __ptr_(__p) {}
#if _LIBCPP_HAS_RTTI
_LIBCPP_HIDE_FROM_ABI_VIRTUAL const void* __get_deleter(const type_info& __t) const _NOEXCEPT override {
@@ -110,17 +113,14 @@ class __shared_ptr_pointer : public __shared_weak_count {
private:
_LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override {
__deleter_(__ptr_);
+ __ptr_.~_Tp();
__deleter_.~_Dp();
}
_LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
- typedef typename __allocator_traits_rebind<_Alloc, __shared_ptr_pointer>::type _Al;
- typedef allocator_traits<_Al> _ATraits;
- typedef pointer_traits<typename _ATraits::pointer> _PTraits;
-
- _Al __a(__alloc_);
- __alloc_.~_Alloc();
- __a.deallocate(_PTraits::pointer_to(*this), 1);
+ __alloc_t __a(__alloc_);
+ __alloc_.~__alloc_t();
+ __a.deallocate(pointer_traits<typename allocator_traits<__alloc_t>::pointer>::pointer_to(*this), 1);
}
};
@@ -130,79 +130,53 @@ class __shared_ptr_pointer : public __shared_weak_count {
struct __for_overwrite_tag {};
template <class _Tp, class _Alloc>
-struct __shared_ptr_emplace : __shared_weak_count {
+struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace : __shared_weak_count {
+ using __alloc_t _LIBCPP_NODEBUG = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace>;
+ using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<__alloc_t>;
using __value_type _LIBCPP_NODEBUG = __remove_cv_t<_Tp>;
- template <class... _Args,
- class _Allocator = _Alloc,
- __enable_if_t<is_same<typename _Allocator::value_type, __for_overwrite_tag>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&...) : __storage_(std::move(__a)) {
- static_assert(
- sizeof...(_Args) == 0, "No argument should be provided to the control block when using _for_overwrite");
- ::new (static_cast<void*>(__get_elem())) __value_type;
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __alloc_(std::move(__a)) {
+ __alloc_traits::construct(__alloc_, __get_elem(), std::forward<_Args>(__args)...);
}
- template <class... _Args,
- class _Allocator = _Alloc,
- __enable_if_t<!is_same<typename _Allocator::value_type, __for_overwrite_tag>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __storage_(std::move(__a)) {
- using _TpAlloc = typename __allocator_traits_rebind<_Alloc, __value_type>::type;
- _TpAlloc __tmp(*__get_alloc());
- allocator_traits<_TpAlloc>::construct(__tmp, __get_elem(), std::forward<_Args>(__args)...);
- }
-
- _LIBCPP_HIDE_FROM_ABI _Alloc* __get_alloc() _NOEXCEPT { return __storage_.__get_alloc(); }
-
- _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return __storage_.__get_elem(); }
+ _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return reinterpret_cast<__value_type*>(__buffer_); }
private:
- template <class _Allocator = _Alloc,
- __enable_if_t<is_same<typename _Allocator::value_type, __for_overwrite_tag>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI void __on_zero_shared_impl() _NOEXCEPT {
- __get_elem()->~__value_type();
- }
-
- template <class _Allocator = _Alloc,
- __enable_if_t<!is_same<typename _Allocator::value_type, __for_overwrite_tag>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI void __on_zero_shared_impl() _NOEXCEPT {
- using _TpAlloc = typename __allocator_traits_rebind<_Allocator, __remove_cv_t<_Tp> >::type;
- _TpAlloc __tmp(*__get_alloc());
- allocator_traits<_TpAlloc>::destroy(__tmp, __get_elem());
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override {
+ __alloc_traits::destroy(__alloc_, __get_elem());
}
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override { __on_zero_shared_impl(); }
-
_LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
- using _ControlBlockAlloc = typename __allocator_traits_rebind<_Alloc, __shared_ptr_emplace>::type;
- using _ControlBlockPointer = typename allocator_traits<_ControlBlockAlloc>::pointer;
- _ControlBlockAlloc __tmp(*__get_alloc());
- __storage_.~_Storage();
- allocator_traits<_ControlBlockAlloc>::deallocate(__tmp, pointer_traits<_ControlBlockPointer>::pointer_to(*this), 1);
+ __alloc_t __tmp(__alloc_);
+ __alloc_.~__alloc_t();
+ __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
}
- // TODO: It should be possible to refactor this to remove `_Storage` entirely.
- // This class implements the control block for non-array shared pointers created
- // through `std::allocate_shared` and `std::make_shared`.
- struct _Storage {
- struct _Data {
- _LIBCPP_COMPRESSED_PAIR(_Alloc, __alloc_, __value_type, __elem_);
- };
+ _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
+ _ALIGNAS_TYPE(__value_type) char __buffer_[sizeof(__value_type)];
+};
- _ALIGNAS_TYPE(_Data) char __buffer_[sizeof(_Data)];
+template <class _Tp, class _Alloc>
+struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace_for_overwrite : __shared_weak_count {
+ using __alloc_t _LIBCPP_NODEBUG = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace_for_overwrite>;
+ using __value_type _LIBCPP_NODEBUG = __remove_cv_t<_Tp>;
- _LIBCPP_HIDE_FROM_ABI explicit _Storage(_Alloc&& __a) { ::new ((void*)__get_alloc()) _Alloc(std::move(__a)); }
- _LIBCPP_HIDE_FROM_ABI ~_Storage() { __get_alloc()->~_Alloc(); }
+ explicit __shared_ptr_emplace_for_overwrite(_Alloc __a) : __alloc_(std::move(__a)) {}
- _LIBCPP_HIDE_FROM_ABI _Alloc* __get_alloc() _NOEXCEPT {
- return std::addressof(reinterpret_cast<_Data*>(__buffer_)->__alloc_);
- }
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override { __value_.~__value_type(); }
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
+ __alloc_t __tmp(__alloc_);
+ __alloc_.~__alloc_t();
+ using __alloc_traits = allocator_traits<__alloc_t>;
+ __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
+ }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_CFI __value_type* __get_elem() _NOEXCEPT {
- return std::addressof(reinterpret_cast<_Data*>(__buffer_)->__elem_);
- }
- };
+ __value_type* __get_elem() _NOEXCEPT { return std::addressof(__value_); }
- _Storage __storage_;
+private:
+ _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __value_type __value_;
};
struct __shared_ptr_dummy_rebind_allocator_type;
@@ -654,15 +628,21 @@ shared_ptr(unique_ptr<_Tp, _Dp>) -> shared_ptr<_Tp>;
//
// std::allocate_shared and std::make_shared
//
-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, _Alloc>;
+
+template <class _ControlBlock, class _Tp, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __alloc, _Args&&... __args) {
using _ControlBlockAllocator = typename __allocator_traits_rebind<_Alloc, _ControlBlock>::type;
- __allocation_guard<_ControlBlockAllocator> __guard(__a, 1);
- ::new ((void*)std::addressof(*__guard.__get())) _ControlBlock(__a, std::forward<_Args>(__args)...);
+ __allocation_guard<_ControlBlockAllocator> __guard(__alloc, 1);
+ ::new ((void*)std::addressof(*__guard.__get())) _ControlBlock(__alloc, std::forward<_Args>(__args)...);
auto __control_block = __guard.__release_ptr();
return shared_ptr<_Tp>::__create_with_control_block(
- (*__control_block).__get_elem(), std::addressof(*__control_block));
+ __control_block->__get_elem(), std::__to_address(__control_block));
+}
+
+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, __args...);
}
template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
@@ -674,9 +654,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 _ForOverwriteAllocator = __allocator_traits_rebind_t<_Alloc, __for_overwrite_tag>;
- _ForOverwriteAllocator __alloc(__a);
- return std::allocate_shared<_Tp>(__alloc);
+ 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);
}
template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
diff --git a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp
deleted file mode 100644
index 9cb5b2ffbae97..0000000000000
--- a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_layout.pass.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: libcpp-abi-no-compressed-pair-padding
-
-// This test makes sure that the control block implementation used for non-array
-// types in std::make_shared and std::allocate_shared is ABI compatible with the
-// original implementation.
-//
-// This test is relevant because the implementation of that control block is
-// different starting in C++20, a change that was required to implement P0674.
-
-#include <cassert>
-#include <cstddef>
-#include <memory>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include <string>
-#include <vector>
-
-#include "test_macros.h"
-
-struct value_init_tag {};
-
-template <class T, int _Idx, bool CanBeEmptyBase = std::is_empty<T>::value && !std::__is_final_v<T>>
-struct compressed_pair_elem {
- explicit compressed_pair_elem(value_init_tag) : value_() {}
-
- template <class U>
- explicit compressed_pair_elem(U&& u) : value_(std::forward<U>(u)) {}
-
- T& get() { return value_; }
-
-private:
- T value_;
-};
-
-template <class T, int _Idx>
-struct compressed_pair_elem<T, _Idx, true> : private T {
- explicit compressed_pair_elem(value_init_tag) : T() {}
-
- template <class U>
- explicit compressed_pair_elem(U&& u) : T(std::forward<U>(u)) {}
-
- T& get() { return *this; }
-};
-
-template <class T1, class T2>
-class compressed_pair : private compressed_pair_elem<T1, 0>, private compressed_pair_elem<T2, 1> {
-public:
- using Base1 = compressed_pair_elem<T1, 0>;
- using Base2 = compressed_pair_elem<T2, 1>;
-
- template <class U1, class U2>
- explicit compressed_pair(U1&& t1, U2&& t2) : Base1(std::forward<U1>(t1)), Base2(std::forward<U2>(t2)) {}
-
- T1& first() { return static_cast<Base1&>(*this).get(); }
- T2& second() { return static_cast<Base2&>(*this).get(); }
-};
-
-// This is the pre-C++20 implementation of the control block used by non-array
-// std::allocate_shared and std::make_shared. We keep it here so that we can
-// make sure our implementation is backwards compatible with it forever.
-//
-// Of course, the class and its methods were renamed, but the size and layout
-// of the class should remain the same as the original implementation.
-template <class T, class Alloc>
-struct OldEmplaceControlBlock : std::__shared_weak_count {
- explicit OldEmplaceControlBlock(Alloc a) : data_(std::move(a), value_init_tag()) {}
- T* get_elem() noexcept { return std::addressof(data_.second()); }
- Alloc* get_alloc() noexcept { return std::addressof(data_.first()); }
-
-private:
- virtual void __on_zero_shared() noexcept {
- // Not implemented
- }
-
- virtual void __on_zero_shared_weak() noexcept {
- // Not implemented
- }
-
- compressed_pair<Alloc, T> data_;
-};
-
-template <class T, template <class> class Alloc>
-void test() {
- using Old = OldEmplaceControlBlock<T, Alloc<T>>;
- using New = std::__shared_ptr_emplace<T, Alloc<T>>;
-
- static_assert(sizeof(New) == sizeof(Old), "");
- static_assert(alignof(New) == alignof(Old), "");
-
- // Also make sure each member is at the same offset
- Alloc<T> a;
- Old old(a);
- New new_(a);
-
- // 1. Check the stored object
- {
- char const* old_elem = reinterpret_cast<char const*>(old.get_elem());
- char const* new_elem = reinterpret_cast<char const*>(new_.__get_elem());
- std::ptrdiff_t old_offset = old_elem - reinterpret_cast<char const*>(&old);
- std::ptrdiff_t new_offset = new_elem - reinterpret_cast<char const*>(&new_);
- assert(new_offset == old_offset && "offset of stored element changed");
- }
-
- // 2. Check the allocator
- {
- char const* old_alloc = reinterpret_cast<char const*>(old.get_alloc());
- char const* new_alloc = reinterpret_cast<char const*>(new_.__get_alloc());
- std::ptrdiff_t old_offset = old_alloc - reinterpret_cast<char const*>(&old);
- std::ptrdiff_t new_offset = new_alloc - reinterpret_cast<char const*>(&new_);
- assert(new_offset == old_offset && "offset of allocator changed");
- }
-
- // Make sure both types have the same triviality (that has ABI impact since
- // it determined how objects are passed). Both should be non-trivial.
- static_assert(std::is_trivially_copyable<New>::value == std::is_trivially_copyable<Old>::value, "");
- static_assert(
- std::is_trivially_default_constructible<New>::value == std::is_trivially_default_constructible<Old>::value, "");
-}
-
-// Object types to store in the control block
-struct TrivialEmptyType {};
-
-struct alignas(32) OveralignedEmptyType {};
-
-struct TrivialNonEmptyType {
- char c[11];
-};
-
-struct FinalEmptyType final {};
-
-struct NonTrivialType {
- char c[22];
- NonTrivialType() : c{'x'} {}
-};
-
-struct VirtualFunctionType {
- virtual ~VirtualFunctionType() {}
-};
-
-// Allocator types
-template <class T>
-struct TrivialEmptyAlloc {
- using value_type = T;
- TrivialEmptyAlloc() = default;
- template <class U>
- TrivialEmptyAlloc(TrivialEmptyAlloc<U>) {}
- T* allocate(std::size_t) { return nullptr; }
- void deallocate(T*, std::size_t) {}
-};
-
-template <class T>
-struct TrivialNonEmptyAlloc {
- char storage[77];
- using value_type = T;
- TrivialNonEmptyAlloc() = default;
- template <class U>
- TrivialNonEmptyAlloc(TrivialNonEmptyAlloc<U>) {}
- T* allocate(std::size_t) { return nullptr; }
- void deallocate(T*, std::size_t) {}
-};
-
-template <class T>
-struct FinalEmptyAlloc final {
- using value_type = T;
- FinalEmptyAlloc() = default;
- template <class U>
- FinalEmptyAlloc(FinalEmptyAlloc<U>) {}
- T* allocate(std::size_t) { return nullptr; }
- void deallocate(T*, std::size_t) {}
-};
-
-template <class T>
-struct NonTrivialAlloc {
- char storage[88];
- using value_type = T;
- NonTrivialAlloc() {}
- template <class U>
- NonTrivialAlloc(NonTrivialAlloc<U>) {}
- T* allocate(std::size_t) { return nullptr; }
- void deallocate(T*, std::size_t) {}
-};
-
-int main(int, char**) {
- test<TrivialEmptyType, TrivialEmptyAlloc>();
- test<TrivialEmptyType, TrivialNonEmptyAlloc>();
- test<TrivialEmptyType, FinalEmptyAlloc>();
- test<TrivialEmptyType, NonTrivialAlloc>();
-
-#if !defined(TEST_HAS_NO_ALIGNED_ALLOCATION)
- test<OveralignedEmptyType, TrivialEmptyAlloc>();
- test<OveralignedEmptyType, TrivialNonEmptyAlloc>();
- test<OveralignedEmptyType, FinalEmptyAlloc>();
- test<OveralignedEmptyType, NonTrivialAlloc>();
-#endif
-
- test<TrivialNonEmptyType, TrivialEmptyAlloc>();
- test<TrivialNonEmptyType, TrivialNonEmptyAlloc>();
- test<TrivialNonEmptyType, FinalEmptyAlloc>();
- test<TrivialNonEmptyType, NonTrivialAlloc>();
-
- test<FinalEmptyType, TrivialEmptyAlloc>();
- // FinalEmptyType combined with TrivialNonEmptyAlloc, FinalEmptyAlloc or NonTrivialAlloc is known to have an ABI break
- // between LLVM 19 and LLVM 20. It's been deemed not severe enough to cause actual breakage.
-
- test<NonTrivialType, TrivialEmptyAlloc>();
- test<NonTrivialType, TrivialNonEmptyAlloc>();
- test<NonTrivialType, FinalEmptyAlloc>();
- test<NonTrivialType, NonTrivialAlloc>();
-
- test<VirtualFunctionType, TrivialEmptyAlloc>();
- test<VirtualFunctionType, TrivialNonEmptyAlloc>();
- test<VirtualFunctionType, FinalEmptyAlloc>();
- test<VirtualFunctionType, NonTrivialAlloc>();
-
- // Test a few real world types just to make sure we didn't mess up badly somehow
- test<std::string, std::allocator>();
- test<int, std::allocator>();
- test<std::vector<int>, std::allocator>();
-
- return 0;
-}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.lwg2070.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.lwg2070.pass.cpp
index 222a552e58378..9dc4dd0dc7574 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.lwg2070.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.lwg2070.pass.cpp
@@ -51,19 +51,20 @@ struct MyAllocator {
void deallocate(pointer p, std::ptrdiff_t) { return ::operator delete(p); }
- template <typename ...Args>
- void construct(T* p, Args&& ...args) {
+ template <class U, typename... Args>
+ void construct(U* p, Args&&... args) {
construct_called = true;
destroy_called = false;
allocator_id = id;
- ::new (p) T(std::forward<Args>(args)...);
+ ::new (p) U(std::forward<Args>(args)...);
}
- void destroy(T* p) {
+ template <class U>
+ void destroy(U* p) {
construct_called = false;
destroy_called = true;
allocator_id = id;
- p->~T();
+ p->~U();
}
};
@@ -81,7 +82,8 @@ struct Private {
int id;
private:
- friend FactoryAllocator<Private>;
+ template <class>
+ friend struct FactoryAllocator;
Private(int i) : id(i) {}
~Private() {}
};
@@ -98,8 +100,15 @@ struct FactoryAllocator : std::allocator<T> {
typedef FactoryAllocator<T1> other;
};
- void construct(void* p, int id) { ::new (p) Private(id); }
- void destroy(Private* p) { p->~Private(); }
+ template <class U>
+ void construct(U* p, int id) {
+ ::new (p) U(id);
+ }
+
+ template <class U>
+ void destroy(U* p) {
+ p->~U();
+ }
};
std::shared_ptr<Private> Factory::allocate() {
More information about the libcxx-commits
mailing list