[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