[libcxx-commits] [libcxx] e27a122 - [libc++] Support arrays in make_shared and allocate_shared (P0674R1)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Apr 6 05:43:04 PDT 2022
Author: Louis Dionne
Date: 2022-04-06T08:42:55-04:00
New Revision: e27a122b3abc5000e99239f3df6e6d4e32b7dc81
URL: https://github.com/llvm/llvm-project/commit/e27a122b3abc5000e99239f3df6e6d4e32b7dc81
DIFF: https://github.com/llvm/llvm-project/commit/e27a122b3abc5000e99239f3df6e6d4e32b7dc81.diff
LOG: [libc++] Support arrays in make_shared and allocate_shared (P0674R1)
This patch implements P0674R1, i.e. support for arrays in std::make_shared
and std::allocate_shared.
Co-authored-by: Zoe Carver <z.zoelec2 at gmail.com>
Differential Revision: https://reviews.llvm.org/D62641
Added:
libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.zero_size.compile.fail.cpp
libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.zero_size.compile.fail.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.bounded.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.unbounded.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.bounded.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.unbounded.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/types.h
Modified:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/ReleaseNotes.rst
libcxx/docs/Status/Cxx20Papers.csv
libcxx/include/__memory/shared_ptr.h
libcxx/include/__memory/uninitialized_algorithms.h
libcxx/include/memory
libcxx/include/version
libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.private.compile.fail.cpp
libcxx/utils/generate_feature_test_macro_components.py
Removed:
################################################################################
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8877ec0a48bc6..e305a78e0c456 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -268,6 +268,8 @@ Status
------------------------------------------------- -----------------
``__cpp_lib_semaphore`` ``201907L``
------------------------------------------------- -----------------
+ ``__cpp_lib_shared_ptr_arrays`` ``201707L``
+ ------------------------------------------------- -----------------
``__cpp_lib_shift`` ``201806L``
------------------------------------------------- -----------------
``__cpp_lib_smart_ptr_for_overwrite`` *unimplemented*
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index ea42b56f29d1a..dc86c58844a41 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -42,6 +42,8 @@ New Features
- Implemented P1165R1 (Make stateful allocator propagation more consistent for ``operator+(basic_string)``)
+- Implemented P0674R1 (Support arrays in make_shared and allocate_shared)
+
- `pop_heap` now uses an algorithm known as "bottom-up heapsort" or
"heapsort with bounce" to reduce the number of comparisons, and rearranges
elements using move-assignment instead of `swap`.
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 438e1f07b896c..de07254a48425 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -1,6 +1,6 @@
"Paper #","Group","Paper Name","Meeting","Status","First released version"
"`P0463R1 <https://wg21.link/P0463R1>`__","LWG","Endian just Endian","Toronto","|Complete|","7.0"
-"`P0674R1 <https://wg21.link/P0674R1>`__","LWG","Extending make_shared to Support Arrays","Toronto","",""
+"`P0674R1 <https://wg21.link/P0674R1>`__","LWG","Extending make_shared to Support Arrays","Toronto","|Complete|","15.0"
"","","","","",""
"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","",""
"`P0053R7 <https://wg21.link/P0053R7>`__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","",""
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 8560d3079758a..397da8f8fa1cf 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -15,12 +15,15 @@
#include <__functional/binary_function.h>
#include <__functional/operations.h>
#include <__functional/reference_wrapper.h>
+#include <__iterator/access.h>
#include <__memory/addressof.h>
#include <__memory/allocation_guard.h>
#include <__memory/allocator.h>
#include <__memory/allocator_traits.h>
#include <__memory/compressed_pair.h>
+#include <__memory/construct_at.h>
#include <__memory/pointer_traits.h>
+#include <__memory/uninitialized_algorithms.h>
#include <__memory/unique_ptr.h>
#include <__utility/forward.h>
#include <__utility/move.h>
@@ -963,6 +966,216 @@ shared_ptr<_Tp> make_shared(_Args&& ...__args)
return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...);
}
+#if _LIBCPP_STD_VER > 17
+
+template <size_t _Alignment>
+struct __sp_aligned_storage {
+ alignas(_Alignment) char __storage[_Alignment];
+};
+
+template <class _Tp, class _Alloc>
+struct __unbounded_array_control_block;
+
+template <class _Tp, class _Alloc>
+struct __unbounded_array_control_block<_Tp[], _Alloc> : __shared_weak_count
+{
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ _Tp* __get_data() noexcept { return __data_; }
+
+ _LIBCPP_HIDE_FROM_ABI
+ explicit __unbounded_array_control_block(_Alloc const& __alloc, size_t __count, _Tp const& __arg)
+ : __alloc_(__alloc), __count_(__count)
+ {
+ std::__uninitialized_allocator_fill_n(__alloc_, std::begin(__data_), __count_, __arg);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ explicit __unbounded_array_control_block(_Alloc const& __alloc, size_t __count)
+ : __alloc_(__alloc), __count_(__count)
+ {
+ std::__uninitialized_allocator_value_construct_n(__alloc_, std::begin(__data_), __count_);
+ }
+
+ // Returns the number of bytes required to store a control block followed by the given number
+ // of elements of _Tp, with the whole storage being aligned to a multiple of _Tp's alignment.
+ _LIBCPP_HIDE_FROM_ABI
+ static constexpr size_t __bytes_for(size_t __elements) {
+ // When there's 0 elements, the control block alone is enough since it holds one element.
+ // Otherwise, we allocate one fewer element than requested because the control block already
+ // holds one. Also, we use the bitwise formula below to ensure that we allocate enough bytes
+ // for the whole allocation to be a multiple of _Tp's alignment. That formula is taken from [1].
+ //
+ // [1]: https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding
+ size_t __bytes = __elements == 0 ? sizeof(__unbounded_array_control_block)
+ : (__elements - 1) * sizeof(_Tp) + sizeof(__unbounded_array_control_block);
+ constexpr size_t __align = alignof(_Tp);
+ return (__bytes + __align - 1) & ~(__align - 1);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ ~__unbounded_array_control_block() override { } // can't be `= default` because of the sometimes-non-trivial union member __data_
+
+private:
+ void __on_zero_shared() _NOEXCEPT override {
+ __allocator_traits_rebind_t<_Alloc, _Tp> __value_alloc(__alloc_);
+ std::__allocator_destroy_multidimensional(__value_alloc, __data_, __data_ + __count_);
+ }
+
+ void __on_zero_shared_weak() _NOEXCEPT override {
+ using _AlignedStorage = __sp_aligned_storage<alignof(__unbounded_array_control_block)>;
+ using _StorageAlloc = __allocator_traits_rebind_t<_Alloc, _AlignedStorage>;
+ using _PointerTraits = pointer_traits<typename allocator_traits<_StorageAlloc>::pointer>;
+
+ _StorageAlloc __tmp(__alloc_);
+ __alloc_.~_Alloc();
+ size_t __size = __unbounded_array_control_block::__bytes_for(__count_);
+ _AlignedStorage* __storage = reinterpret_cast<_AlignedStorage*>(this);
+ allocator_traits<_StorageAlloc>::deallocate(__tmp, _PointerTraits::pointer_to(*__storage), __size);
+ }
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
+ size_t __count_;
+ union {
+ _Tp __data_[1];
+ };
+};
+
+template<class _Array, class _Alloc, class... _Arg>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Array> __allocate_shared_unbounded_array(const _Alloc& __a, size_t __n, _Arg&& ...__arg)
+{
+ static_assert(is_unbounded_array_v<_Array>);
+ // We compute the number of bytes necessary to hold the control block and the
+ // array elements. Then, we allocate an array of properly-aligned dummy structs
+ // large enough to hold the control block and array. This allows shifting the
+ // burden of aligning memory properly from us to the allocator.
+ using _ControlBlock = __unbounded_array_control_block<_Array, _Alloc>;
+ using _AlignedStorage = __sp_aligned_storage<alignof(_ControlBlock)>;
+ using _StorageAlloc = __allocator_traits_rebind_t<_Alloc, _AlignedStorage>;
+ __allocation_guard<_StorageAlloc> __guard(__a, _ControlBlock::__bytes_for(__n) / sizeof(_AlignedStorage));
+ _ControlBlock* __control_block = reinterpret_cast<_ControlBlock*>(std::addressof(*__guard.__get()));
+ std::construct_at(__control_block, __a, __n, std::forward<_Arg>(__arg)...);
+ __guard.__release_ptr();
+ return shared_ptr<_Array>::__create_with_control_block(__control_block->__get_data(), __control_block);
+}
+
+template <class _Tp, class _Alloc>
+struct __bounded_array_control_block;
+
+template <class _Tp, size_t _Count, class _Alloc>
+struct __bounded_array_control_block<_Tp[_Count], _Alloc>
+ : __shared_weak_count
+{
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ _Tp* __get_data() noexcept { return __data_; }
+
+ _LIBCPP_HIDE_FROM_ABI
+ explicit __bounded_array_control_block(_Alloc const& __alloc, _Tp const& __arg) : __alloc_(__alloc) {
+ std::__uninitialized_allocator_fill_n(__alloc_, std::addressof(__data_[0]), _Count, __arg);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ explicit __bounded_array_control_block(_Alloc const& __alloc) : __alloc_(__alloc) {
+ std::__uninitialized_allocator_value_construct_n(__alloc_, std::addressof(__data_[0]), _Count);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ ~__bounded_array_control_block() override { } // can't be `= default` because of the sometimes-non-trivial union member __data_
+
+private:
+ void __on_zero_shared() _NOEXCEPT override {
+ __allocator_traits_rebind_t<_Alloc, _Tp> __value_alloc(__alloc_);
+ std::__allocator_destroy_multidimensional(__value_alloc, __data_, __data_ + _Count);
+ }
+
+ void __on_zero_shared_weak() _NOEXCEPT override {
+ using _ControlBlockAlloc = __allocator_traits_rebind_t<_Alloc, __bounded_array_control_block>;
+ using _PointerTraits = pointer_traits<typename allocator_traits<_ControlBlockAlloc>::pointer>;
+
+ _ControlBlockAlloc __tmp(__alloc_);
+ __alloc_.~_Alloc();
+ allocator_traits<_ControlBlockAlloc>::deallocate(__tmp, _PointerTraits::pointer_to(*this), sizeof(*this));
+ }
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
+ union {
+ _Tp __data_[_Count];
+ };
+};
+
+template<class _Array, class _Alloc, class... _Arg>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Array> __allocate_shared_bounded_array(const _Alloc& __a, _Arg&& ...__arg)
+{
+ static_assert(is_bounded_array_v<_Array>);
+ using _ControlBlock = __bounded_array_control_block<_Array, _Alloc>;
+ using _ControlBlockAlloc = __allocator_traits_rebind_t<_Alloc, _ControlBlock>;
+
+ __allocation_guard<_ControlBlockAlloc> __guard(__a, 1);
+ _ControlBlock* __control_block = reinterpret_cast<_ControlBlock*>(std::addressof(*__guard.__get()));
+ std::construct_at(__control_block, __a, std::forward<_Arg>(__arg)...);
+ __guard.__release_ptr();
+ return shared_ptr<_Array>::__create_with_control_block(__control_block->__get_data(), __control_block);
+}
+
+template<class _Tp, class _Alloc, class = __enable_if_t<is_bounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> allocate_shared(const _Alloc& __a)
+{
+ return std::__allocate_shared_bounded_array<_Tp>(__a);
+}
+
+template<class _Tp, class _Alloc, class = __enable_if_t<is_bounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u)
+{
+ return std::__allocate_shared_bounded_array<_Tp>(__a, __u);
+}
+
+template<class _Tp, class _Alloc, class = __enable_if_t<is_unbounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n)
+{
+ return std::__allocate_shared_unbounded_array<_Tp>(__a, __n);
+}
+
+template<class _Tp, class _Alloc, class = __enable_if_t<is_unbounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u)
+{
+ return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u);
+}
+
+template<class _Tp, class = __enable_if_t<is_bounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> make_shared()
+{
+ return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>());
+}
+
+template<class _Tp, class = __enable_if_t<is_bounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> make_shared(const remove_extent_t<_Tp>& __u)
+{
+ return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __u);
+}
+
+template<class _Tp, class = __enable_if_t<is_unbounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> make_shared(size_t __n)
+{
+ return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n);
+}
+
+template<class _Tp, class = __enable_if_t<is_unbounded_array<_Tp>::value>>
+_LIBCPP_HIDE_FROM_ABI
+shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u)
+{
+ return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u);
+}
+
+#endif // _LIBCPP_STD_VER > 17
+
template<class _Tp, class _Up>
inline _LIBCPP_INLINE_VISIBILITY
bool
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index fc55b49aef2eb..dfdfdf4eddf1d 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -13,10 +13,13 @@
#include <__config>
#include <__iterator/iterator_traits.h>
#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
#include <__memory/construct_at.h>
#include <__memory/voidify.h>
#include <__utility/move.h>
#include <__utility/pair.h>
+#include <__utility/transaction.h>
+#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -346,6 +349,153 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
#endif // _LIBCPP_STD_VER > 14
+#if _LIBCPP_STD_VER > 17
+
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template<class _Alloc, class _BidirIter, class = __enable_if_t<
+ __is_cpp17_bidirectional_iterator<_BidirIter>::value
+>>
+_LIBCPP_HIDE_FROM_ABI
+constexpr void __allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+ using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+ static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+ "The allocator should already be rebound to the correct type");
+
+ if (__first == __last)
+ return;
+
+ if constexpr (is_array_v<_ValueType>) {
+ static_assert(!is_unbounded_array_v<_ValueType>,
+ "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+ using _Element = remove_extent_t<_ValueType>;
+ __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+ do {
+ --__last;
+ decltype(auto) __array = *__last;
+ std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+ } while (__last != __first);
+ } else {
+ do {
+ --__last;
+ allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+ } while (__last != __first);
+ }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template<class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI
+constexpr void __allocator_construct_at(_Alloc& __alloc, _Tp* __loc) {
+ static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+ "The allocator should already be rebound to the correct type");
+
+ if constexpr (is_array_v<_Tp>) {
+ using _Element = remove_extent_t<_Tp>;
+ __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+ size_t __i = 0;
+ _Tp& __array = *__loc;
+
+ // If an exception is thrown, destroy what we have constructed so far in reverse order.
+ __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); });
+ for (; __i != extent_v<_Tp>; ++__i) {
+ std::__allocator_construct_at(__elem_alloc, std::addressof(__array[__i]));
+ }
+ __guard.__complete();
+ } else {
+ allocator_traits<_Alloc>::construct(__alloc, __loc);
+ }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template<class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI
+constexpr void __allocator_construct_at(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+ static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+ "The allocator should already be rebound to the correct type");
+
+ if constexpr (is_array_v<_Tp>) {
+ static_assert(is_array_v<_Arg>,
+ "Provided non-array initialization argument to __allocator_construct_at when "
+ "trying to construct an array.");
+
+ using _Element = remove_extent_t<_Tp>;
+ __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+ size_t __i = 0;
+ _Tp& __array = *__loc;
+
+ // If an exception is thrown, destroy what we have constructed so far in reverse order.
+ __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); });
+ for (; __i != extent_v<_Tp>; ++__i) {
+ std::__allocator_construct_at(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+ }
+ __guard.__complete();
+ } else {
+ allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+ }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template<class _Alloc, class _BidirIter, class _Tp, class _Size = typename iterator_traits<_BidirIter>::
diff erence_type>
+_LIBCPP_HIDE_FROM_ABI
+constexpr void __uninitialized_allocator_fill_n(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+ using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+ __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+ _BidirIter __begin = __it;
+
+ // If an exception is thrown, destroy what we have constructed so far in reverse order.
+ __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+ for (; __n != 0; --__n, ++__it) {
+ std::__allocator_construct_at(__value_alloc, std::addressof(*__it), __value);
+ }
+ __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template<class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::
diff erence_type>
+_LIBCPP_HIDE_FROM_ABI
+constexpr void __uninitialized_allocator_value_construct_n(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+ using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+ __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+ _BidirIter __begin = __it;
+
+ // If an exception is thrown, destroy what we have constructed so far in reverse order.
+ __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+ for (; __n != 0; --__n, ++__it) {
+ std::__allocator_construct_at(__value_alloc, std::addressof(*__it));
+ }
+ __guard.__complete();
+}
+
+#endif // _LIBCPP_STD_VER > 17
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index 53172c58c3b4a..8d577245999d4 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -661,9 +661,29 @@ template<class E, class T, class Y>
template<class D, class T> D* get_deleter(shared_ptr<T> const& p) noexcept;
template<class T, class... Args>
- shared_ptr<T> make_shared(Args&&... args);
+ shared_ptr<T> make_shared(Args&&... args); // T is not an array
template<class T, class A, class... Args>
- shared_ptr<T> allocate_shared(const A& a, Args&&... args);
+ shared_ptr<T> allocate_shared(const A& a, Args&&... args); // T is not an array
+
+template<class T>
+ shared_ptr<T> make_shared(size_t N); // T is U[] (since C++20)
+template<class T, class A>
+ shared_ptr<T> allocate_shared(const A& a, size_t N); // T is U[] (since C++20)
+
+template<class T>
+ shared_ptr<T> make_shared(); // T is U[N] (since C++20)
+template<class T, class A>
+ shared_ptr<T> allocate_shared(const A& a); // T is U[N] (since C++20)
+
+template<class T>
+ shared_ptr<T> make_shared(size_t N, const remove_extent_t<T>& u); // T is U[] (since C++20)
+template<class T, class A>
+ shared_ptr<T> allocate_shared(const A& a, size_t N, const remove_extent_t<T>& u); // T is U[] (since C++20)
+
+template<class T> shared_ptr<T>
+ make_shared(const remove_extent_t<T>& u); // T is U[N] (since C++20)
+template<class T, class A>
+ shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& u); // T is U[N] (since C++20)
template<class T>
class weak_ptr
diff --git a/libcxx/include/version b/libcxx/include/version
index 003a42cf59e6a..258adedffffa4 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -154,7 +154,8 @@ __cpp_lib_sample 201603L <algorithm>
__cpp_lib_scoped_lock 201703L <mutex>
__cpp_lib_semaphore 201907L <semaphore>
__cpp_lib_shared_mutex 201505L <shared_mutex>
-__cpp_lib_shared_ptr_arrays 201611L <memory>
+__cpp_lib_shared_ptr_arrays 201707L <memory>
+ 201611L // C++17
__cpp_lib_shared_ptr_weak_type 201606L <memory>
__cpp_lib_shared_timed_mutex 201402L <shared_mutex>
__cpp_lib_shift 201806L <algorithm>
@@ -353,6 +354,8 @@ __cpp_lib_void_t 201411L <type_traits>
# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_semaphore)
# define __cpp_lib_semaphore 201907L
# endif
+# undef __cpp_lib_shared_ptr_arrays
+# define __cpp_lib_shared_ptr_arrays 201707L
# define __cpp_lib_shift 201806L
// # define __cpp_lib_smart_ptr_for_overwrite 202002L
// # define __cpp_lib_source_location 201907L
diff --git a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.zero_size.compile.fail.cpp b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.zero_size.compile.fail.cpp
new file mode 100644
index 0000000000000..3c678d0e4d67d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.zero_size.compile.fail.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Make sure that std::allocate_shared<T[0]>(...) fails at compile-time.
+// While Clang and GCC appear to support T[0] as a language extension, that support is
+// unreliable (for example T[0] doesn't match a T[N] partial specialization on Clang as
+// of writing this). So instead, we make sure that this doesn't work at all with our
+// implementation.
+
+#include <memory>
+
+void f() {
+ auto p = std::allocate_shared<int[0]>(std::allocator<int[0]>());
+ (void)p;
+}
diff --git a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.zero_size.compile.fail.cpp b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.zero_size.compile.fail.cpp
new file mode 100644
index 0000000000000..35964a52f6de2
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.zero_size.compile.fail.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Make sure that std::make_shared<T[0]>(...) fails at compile-time.
+// While Clang and GCC appear to support T[0] as a language extension, that support is
+// unreliable (for example T[0] doesn't match a T[N] partial specialization on Clang as
+// of writing this). So instead, we make sure that this doesn't work at all with our
+// implementation.
+
+#include <memory>
+
+void f() {
+ auto p = std::make_shared<int[0]>();
+ (void)p;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
index 20fe2514b4eff..cd9d4fa7a09d2 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
@@ -29,6 +29,7 @@
__cpp_lib_ranges 201811L [C++20]
__cpp_lib_raw_memory_algorithms 201606L [C++17]
__cpp_lib_shared_ptr_arrays 201611L [C++17]
+ 201707L [C++20]
__cpp_lib_shared_ptr_weak_type 201606L [C++17]
__cpp_lib_smart_ptr_for_overwrite 202002L [C++20]
__cpp_lib_to_address 201711L [C++20]
@@ -374,8 +375,8 @@
# ifndef __cpp_lib_shared_ptr_arrays
# error "__cpp_lib_shared_ptr_arrays should be defined in c++20"
# endif
-# if __cpp_lib_shared_ptr_arrays != 201611L
-# error "__cpp_lib_shared_ptr_arrays should have the value 201611L in c++20"
+# if __cpp_lib_shared_ptr_arrays != 201707L
+# error "__cpp_lib_shared_ptr_arrays should have the value 201707L in c++20"
# endif
# ifndef __cpp_lib_shared_ptr_weak_type
@@ -525,8 +526,8 @@
# ifndef __cpp_lib_shared_ptr_arrays
# error "__cpp_lib_shared_ptr_arrays should be defined in c++2b"
# endif
-# if __cpp_lib_shared_ptr_arrays != 201611L
-# error "__cpp_lib_shared_ptr_arrays should have the value 201611L in c++2b"
+# if __cpp_lib_shared_ptr_arrays != 201707L
+# error "__cpp_lib_shared_ptr_arrays should have the value 201707L in c++2b"
# endif
# ifndef __cpp_lib_shared_ptr_weak_type
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
index a46e4ea11434a..8a01b07713add 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
@@ -141,6 +141,7 @@
__cpp_lib_semaphore 201907L [C++20]
__cpp_lib_shared_mutex 201505L [C++17]
__cpp_lib_shared_ptr_arrays 201611L [C++17]
+ 201707L [C++20]
__cpp_lib_shared_ptr_weak_type 201606L [C++17]
__cpp_lib_shared_timed_mutex 201402L [C++14]
__cpp_lib_shift 201806L [C++20]
@@ -3347,8 +3348,8 @@
# ifndef __cpp_lib_shared_ptr_arrays
# error "__cpp_lib_shared_ptr_arrays should be defined in c++20"
# endif
-# if __cpp_lib_shared_ptr_arrays != 201611L
-# error "__cpp_lib_shared_ptr_arrays should have the value 201611L in c++20"
+# if __cpp_lib_shared_ptr_arrays != 201707L
+# error "__cpp_lib_shared_ptr_arrays should have the value 201707L in c++20"
# endif
# ifndef __cpp_lib_shared_ptr_weak_type
@@ -4720,8 +4721,8 @@
# ifndef __cpp_lib_shared_ptr_arrays
# error "__cpp_lib_shared_ptr_arrays should be defined in c++2b"
# endif
-# if __cpp_lib_shared_ptr_arrays != 201611L
-# error "__cpp_lib_shared_ptr_arrays should have the value 201611L in c++2b"
+# if __cpp_lib_shared_ptr_arrays != 201707L
+# error "__cpp_lib_shared_ptr_arrays should have the value 201707L in c++2b"
# endif
# ifndef __cpp_lib_shared_ptr_weak_type
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.bounded.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.bounded.pass.cpp
new file mode 100644
index 0000000000000..06e767fd17831
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.bounded.pass.cpp
@@ -0,0 +1,418 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Aligned deallocation isn't provided before macOS 10.14, and some tests for overaligned types
+// below require that feature.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13}}
+
+// <memory>
+
+// shared_ptr
+
+// template<class T, class A>
+// shared_ptr<T> allocate_shared(const A& a); // T is U[N]
+//
+// template<class T, class A>
+// shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& u); // T is U[N]
+
+#include <cassert>
+#include <concepts>
+#include <cstdint> // std::uintptr_t
+#include <memory>
+#include <utility>
+
+#include "min_allocator.h"
+#include "operator_hijacker.h"
+#include "types.h"
+
+template <class T, class ...Args>
+concept CanAllocateShared = requires(Args&& ...args) {
+ { std::allocate_shared<T>(std::forward<Args>(args)...) } -> std::same_as<std::shared_ptr<T>>;
+};
+
+int main(int, char**) {
+ // Make sure we initialize elements correctly
+ {
+ // Without passing an initial value
+ {
+ using Array = int[8];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == 0);
+ }
+ }
+ {
+ using Array = int[8][3];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 0);
+ assert(ptr[i][1] == 0);
+ assert(ptr[i][2] == 0);
+ }
+ }
+ {
+ using Array = int[8][3][2];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 0);
+ assert(ptr[i][0][1] == 0);
+ assert(ptr[i][1][0] == 0);
+ assert(ptr[i][1][1] == 0);
+ assert(ptr[i][2][0] == 0);
+ assert(ptr[i][2][1] == 0);
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[8];
+ int init = 42;
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == init);
+ }
+ }
+ {
+ using Array = int[8][3];
+ int init[3] = {42, 43, 44};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 42);
+ assert(ptr[i][1] == 43);
+ assert(ptr[i][2] == 44);
+ }
+ }
+ {
+ using Array = int[8][3][2];
+ int init[3][2] = {{31, 32}, {41, 42}, {51, 52}};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 31);
+ assert(ptr[i][0][1] == 32);
+ assert(ptr[i][1][0] == 41);
+ assert(ptr[i][1][1] == 42);
+ assert(ptr[i][2][0] == 51);
+ assert(ptr[i][2][1] == 52);
+ }
+ }
+ }
+
+ // Make sure array elements are destroyed in reverse order
+ {
+ // Without passing an initial value
+ {
+ using Array = DestroyInReverseOrder[8];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(DestroyInReverseOrder::alive() == 8);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(DestroyInReverseOrder::alive() == 8 * 3);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3][2];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(DestroyInReverseOrder::alive() == 8 * 3 * 2);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = DestroyInReverseOrder[8];
+ int count = 0;
+ DestroyInReverseOrder init(&count);
+ int init_count = 1;
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(count == 8 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3];
+ int count = 0;
+ DestroyInReverseOrder init[3] = {&count, &count, &count};
+ int init_count = 3;
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(count == 8 * 3 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3][2];
+ int count = 0;
+ DestroyInReverseOrder init[3][2] = {{&count, &count}, {&count, &count}, {&count, &count}};
+ int init_count = 3 * 2;
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(count == 8 * 3 * 2 + init_count);
+ }
+ assert(count == init_count);
+ }
+ }
+
+ // Count the number of copies being made
+ {
+ // Without passing an initial value
+ {
+ using Array = CountCopies[8];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[8][3];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());;
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[8][3][2];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());;
+ assert(CountCopies::copies() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = CountCopies[8];
+ int copies = 0;
+ CountCopies init(&copies);
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(copies == 8);
+ }
+ {
+ using Array = CountCopies[8][3];
+ int copies = 0;
+ CountCopies init[3] = {&copies, &copies, &copies};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(copies == 8 * 3);
+ }
+ {
+ using Array = CountCopies[8][3][2];
+ int copies = 0;
+ CountCopies init[3][2] = {{&copies, &copies}, {&copies, &copies}, {&copies, &copies}};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(copies == 8 * 3 * 2);
+ }
+ }
+
+ // Make sure array elements are aligned properly when the array contains an overaligned type.
+ //
+ // Here, we don't need to test both the with-initial-value and without-initial-value code paths,
+ // since we're just checking the alignment and both are going to use the same code path unless
+ // the implementation is completely crazy.
+ {
+ auto check_alignment = []<class T> {
+ {
+ using Array = T[8];
+ std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ for (int i = 0; i < 8; ++i) {
+ T* p = std::addressof(ptr[i]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ {
+ using Array = T[8][3];
+ std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ T* p = std::addressof(ptr[i][j]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ {
+ using Array = T[8][3][2];
+ std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 2; ++k) {
+ T* p = std::addressof(ptr[i][j][k]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ }
+ };
+
+ struct Empty { };
+ check_alignment.operator()<Empty>();
+ check_alignment.operator()<OverAligned>();
+ check_alignment.operator()<MaxAligned>();
+
+ // test non corner cases as well while we're at it
+ struct Foo { int i; char c; };
+ check_alignment.operator()<int>();
+ check_alignment.operator()<Foo>();
+ }
+
+ // Make sure that we destroy all the elements constructed so far when an exception
+ // is thrown. Also make sure that we do it in reverse order of construction.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ {
+ struct Sentinel : ThrowOnConstruction, DestroyInReverseOrder { };
+
+ // Without passing an initial value
+ {
+ using Array = Sentinel[8];
+ for (int i = 0; i < 8; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = Sentinel[8];
+ for (int i = 0; i < 8; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init;
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 1);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3][2] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3 * 2);
+ }
+ }
+ }
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+ // Test with another allocator that's not std::allocator
+ {
+ // Without passing an initial value
+ {
+ using Array = int[8][3];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(min_allocator<Array>());
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 0);
+ assert(ptr[i][1] == 0);
+ assert(ptr[i][2] == 0);
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[8][3];
+ int init[3] = {42, 43, 44};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(min_allocator<Array>(), init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 42);
+ assert(ptr[i][1] == 43);
+ assert(ptr[i][2] == 44);
+ }
+ }
+ }
+
+ // Make sure the version without an initialization argument works even for non-movable types
+ {
+ using Array = NonMovable[8][3];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>());
+ (void)ptr;
+ }
+
+ // Make sure std::allocate_shared handles badly-behaved types properly
+ {
+ using Array = operator_hijacker[3];
+ std::shared_ptr<Array> p1 = std::allocate_shared<Array>(std::allocator<Array>());
+ std::shared_ptr<Array> p2 = std::allocate_shared<Array>(std::allocator<Array>(), operator_hijacker());
+ assert(p1 != nullptr);
+ assert(p2 != nullptr);
+ }
+
+ // Check that we SFINAE-away for invalid arguments
+ {
+ struct T { };
+ static_assert( CanAllocateShared<T[8], std::allocator<T[8]>>);
+ static_assert( CanAllocateShared<T[8], std::allocator<T[8]>, T>);
+ static_assert(!CanAllocateShared<T[8], std::allocator<T[8]>, T, int>); // too many arguments
+ static_assert(!CanAllocateShared<T[8], std::allocator<T[8]>, int>); // T is not constructible from int
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.unbounded.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.unbounded.pass.cpp
new file mode 100644
index 0000000000000..5842c478361b0
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.unbounded.pass.cpp
@@ -0,0 +1,460 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Aligned deallocation isn't provided before macOS 10.14, and some tests for overaligned types
+// below require that feature.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13}}
+
+// <memory>
+
+// shared_ptr
+
+// template<class T, class A>
+// shared_ptr<T> allocate_shared(const A& a, size_t N); // T is U[]
+//
+// template<class T, class A>
+// shared_ptr<T> allocate_shared(const A& a, size_t N, const remove_extent_t<T>& u); // T is U[]
+
+// Ignore error about requesting a large alignment not being ABI compatible with older AIX systems.
+#ifdef _AIX
+# pragma clang diagnostic ignored "-Waix-compat"
+#endif
+
+#include <cassert>
+#include <concepts>
+#include <cstdint> // std::uintptr_t
+#include <memory>
+#include <utility>
+
+#include "min_allocator.h"
+#include "operator_hijacker.h"
+#include "test_macros.h"
+#include "types.h"
+
+template <class T, class ...Args>
+concept CanAllocateShared = requires(Args&& ...args) {
+ { std::allocate_shared<T>(std::forward<Args>(args)...) } -> std::same_as<std::shared_ptr<T>>;
+};
+
+int main(int, char**) {
+ // Check behavior for a zero-sized array
+ {
+ // Without passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 0);
+ assert(ptr != nullptr);
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 0, 42);
+ assert(ptr != nullptr);
+ }
+ }
+
+ // Check behavior for a 1-sized array
+ {
+ // Without passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 1);
+ assert(ptr != nullptr);
+ assert(ptr[0] == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 1, 42);
+ assert(ptr != nullptr);
+ assert(ptr[0] == 42);
+ }
+ }
+
+ // Make sure we initialize elements correctly
+ {
+ // Without passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == 0);
+ }
+ }
+ {
+ using Array = int[][3];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 0);
+ assert(ptr[i][1] == 0);
+ assert(ptr[i][2] == 0);
+ }
+ }
+ {
+ using Array = int[][3][2];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 0);
+ assert(ptr[i][0][1] == 0);
+ assert(ptr[i][1][0] == 0);
+ assert(ptr[i][1][1] == 0);
+ assert(ptr[i][2][0] == 0);
+ assert(ptr[i][2][1] == 0);
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[];
+ int init = 42;
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == init);
+ }
+ }
+ {
+ using Array = int[][3];
+ int init[3] = {42, 43, 44};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 42);
+ assert(ptr[i][1] == 43);
+ assert(ptr[i][2] == 44);
+ }
+ }
+ {
+ using Array = int[][3][2];
+ int init[3][2] = {{31, 32}, {41, 42}, {51, 52}};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 31);
+ assert(ptr[i][0][1] == 32);
+ assert(ptr[i][1][0] == 41);
+ assert(ptr[i][1][1] == 42);
+ assert(ptr[i][2][0] == 51);
+ assert(ptr[i][2][1] == 52);
+ }
+ }
+ }
+
+ // Make sure array elements are destroyed in reverse order
+ {
+ // Without passing an initial value
+ {
+ using Array = DestroyInReverseOrder[];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(DestroyInReverseOrder::alive() == 8);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(DestroyInReverseOrder::alive() == 8 * 3);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3][2];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(DestroyInReverseOrder::alive() == 8 * 3 * 2);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = DestroyInReverseOrder[];
+ int count = 0;
+ DestroyInReverseOrder init(&count);
+ int init_count = 1;
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(count == 8 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3];
+ int count = 0;
+ DestroyInReverseOrder init[3] = {&count, &count, &count};
+ int init_count = 3;
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(count == 8 * 3 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3][2];
+ int count = 0;
+ DestroyInReverseOrder init[3][2] = {{&count, &count}, {&count, &count}, {&count, &count}};
+ int init_count = 3 * 2;
+ {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(count == 8 * 3 * 2 + init_count);
+ }
+ assert(count == init_count);
+ }
+ }
+
+ // Count the number of copies being made
+ {
+ // Without passing an initial value
+ {
+ using Array = CountCopies[];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[][3];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[][3][2];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(CountCopies::copies() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = CountCopies[];
+ int copies = 0;
+ CountCopies init(&copies);
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(copies == 8);
+ }
+ {
+ using Array = CountCopies[][3];
+ int copies = 0;
+ CountCopies init[3] = {&copies, &copies, &copies};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(copies == 8 * 3);
+ }
+ {
+ using Array = CountCopies[][3][2];
+ int copies = 0;
+ CountCopies init[3][2] = {{&copies, &copies}, {&copies, &copies}, {&copies, &copies}};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(copies == 8 * 3 * 2);
+ }
+ }
+
+ // Make sure array elements are aligned properly when the array contains an overaligned type.
+ //
+ // Here, we don't need to test both the with-initial-value and without-initial-value code paths,
+ // since we're just checking the alignment and both are going to use the same code path unless
+ // the implementation is completely crazy.
+ {
+ auto check_alignment = []<class T> {
+ {
+ using Array = T[];
+ std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ for (int i = 0; i < 8; ++i) {
+ T* p = std::addressof(ptr[i]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ {
+ using Array = T[][3];
+ std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ T* p = std::addressof(ptr[i][j]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ {
+ using Array = T[][3][2];
+ std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 2; ++k) {
+ T* p = std::addressof(ptr[i][j][k]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ }
+ };
+
+ struct Empty { };
+ check_alignment.operator()<Empty>();
+ check_alignment.operator()<OverAligned>();
+ check_alignment.operator()<MaxAligned>();
+
+ // test non corner cases as well while we're at it
+ struct Foo { int i; char c; };
+ check_alignment.operator()<int>();
+ check_alignment.operator()<Foo>();
+ }
+
+ // Make sure that we destroy all the elements constructed so far when an exception
+ // is thrown. Also make sure that we do it in reverse order of construction.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ {
+ struct Sentinel : ThrowOnConstruction, DestroyInReverseOrder { };
+
+ // Without passing an initial value
+ {
+ using Array = Sentinel[];
+ for (int i = 0; i < 8; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = Sentinel[];
+ for (int i = 0; i < 8; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init;
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 1);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3][2] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3 * 2);
+ }
+ }
+ }
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+ // Test with another allocator that's not std::allocator
+ {
+ // Without passing an initial value
+ {
+ using Array = int[][3];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(min_allocator<Array>(), 8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 0);
+ assert(ptr[i][1] == 0);
+ assert(ptr[i][2] == 0);
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[][3];
+ int init[3] = {42, 43, 44};
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(min_allocator<Array>(), 8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 42);
+ assert(ptr[i][1] == 43);
+ assert(ptr[i][2] == 44);
+ }
+ }
+ }
+
+ // Make sure the version without an initialization argument works even for non-movable types
+ {
+ using Array = NonMovable[][3];
+ std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8);
+ (void)ptr;
+ }
+
+ // Make sure std::allocate_shared handles badly-behaved types properly
+ {
+ using Array = operator_hijacker[];
+ std::shared_ptr<Array> p1 = std::allocate_shared<Array>(std::allocator<Array>(), 3);
+ std::shared_ptr<Array> p2 = std::allocate_shared<Array>(std::allocator<Array>(), 3, operator_hijacker());
+ assert(p1 != nullptr);
+ assert(p2 != nullptr);
+ }
+
+ // Check that we SFINAE-away for invalid arguments
+ {
+ struct T { };
+ static_assert( CanAllocateShared<T[], std::allocator<T[]>, std::size_t>);
+ static_assert( CanAllocateShared<T[], std::allocator<T[]>, std::size_t, T>);
+ static_assert(!CanAllocateShared<T[], std::allocator<T[]>, std::size_t, T, int>); // too many arguments
+ static_assert(!CanAllocateShared<T[], std::allocator<T[]>, std::size_t, int>); // T not constructible from int
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.pass.cpp
index 472cb3d0a7d7c..ac7a9913b0e91 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.pass.cpp
@@ -11,15 +11,17 @@
// shared_ptr
// template<class T, class A, class... Args>
-// shared_ptr<T> allocate_shared(const A& a, Args&&... args);
+// shared_ptr<T> allocate_shared(const A& a, Args&&... args); // T is not an array
#include <memory>
#include <new>
#include <cstdlib>
#include <cassert>
-#include "test_macros.h"
-#include "test_allocator.h"
+
#include "min_allocator.h"
+#include "operator_hijacker.h"
+#include "test_allocator.h"
+#include "test_macros.h"
int new_count = 0;
@@ -172,6 +174,14 @@ int main(int, char**)
}
assert(A::count == 0);
+ // Make sure std::allocate_shared handles badly-behaved types properly
+ {
+ std::shared_ptr<operator_hijacker> p1 = std::allocate_shared<operator_hijacker>(min_allocator<operator_hijacker>());
+ std::shared_ptr<operator_hijacker> p2 = std::allocate_shared<operator_hijacker>(min_allocator<operator_hijacker>(), operator_hijacker());
+ assert(p1 != nullptr);
+ assert(p2 != nullptr);
+ }
+
// Test that we don't call construct before C++20.
#if TEST_STD_VER < 20
{
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp
index d45d6e124b871..908aa7f100cd5 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp
@@ -13,7 +13,7 @@
// shared_ptr
// template<class T, class A, class... Args>
-// shared_ptr<T> allocate_shared(const A& a, Args&&... args);
+// shared_ptr<T> allocate_shared(const A& a, Args&&... args);
// This test checks that allocator_traits::construct is used in allocate_shared
// as requested in C++20 (via P0674R1).
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.bounded.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.bounded.pass.cpp
new file mode 100644
index 0000000000000..20fa508fa77af
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.bounded.pass.cpp
@@ -0,0 +1,390 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Aligned deallocation isn't provided before macOS 10.14, and some tests for overaligned types
+// below require that feature.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13}}
+
+// <memory>
+
+// shared_ptr
+
+// template<class T>
+// shared_ptr<T> make_shared(); // T is U[N]
+//
+// template<class T>
+// shared_ptr<T> make_shared(const remove_extent_t<T>& u); // T is U[N]
+
+#include <cassert>
+#include <concepts>
+#include <cstdint> // std::uintptr_t
+#include <memory>
+#include <utility>
+
+#include "operator_hijacker.h"
+#include "types.h"
+
+template <class T, class ...Args>
+concept CanMakeShared = requires(Args&& ...args) {
+ { std::make_shared<T>(std::forward<Args>(args)...) } -> std::same_as<std::shared_ptr<T>>;
+};
+
+int main(int, char**) {
+ // Make sure we initialize elements correctly
+ {
+ // Without passing an initial value
+ {
+ using Array = int[8];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == 0);
+ }
+ }
+ {
+ using Array = int[8][3];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 0);
+ assert(ptr[i][1] == 0);
+ assert(ptr[i][2] == 0);
+ }
+ }
+ {
+ using Array = int[8][3][2];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 0);
+ assert(ptr[i][0][1] == 0);
+ assert(ptr[i][1][0] == 0);
+ assert(ptr[i][1][1] == 0);
+ assert(ptr[i][2][0] == 0);
+ assert(ptr[i][2][1] == 0);
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[8];
+ int init = 42;
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == init);
+ }
+ }
+ {
+ using Array = int[8][3];
+ int init[3] = {42, 43, 44};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 42);
+ assert(ptr[i][1] == 43);
+ assert(ptr[i][2] == 44);
+ }
+ }
+ {
+ using Array = int[8][3][2];
+ int init[3][2] = {{31, 32}, {41, 42}, {51, 52}};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 31);
+ assert(ptr[i][0][1] == 32);
+ assert(ptr[i][1][0] == 41);
+ assert(ptr[i][1][1] == 42);
+ assert(ptr[i][2][0] == 51);
+ assert(ptr[i][2][1] == 52);
+ }
+ }
+ }
+
+ // Make sure array elements are destroyed in reverse order
+ {
+ // Without passing an initial value
+ {
+ using Array = DestroyInReverseOrder[8];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(DestroyInReverseOrder::alive() == 8);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(DestroyInReverseOrder::alive() == 8 * 3);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3][2];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(DestroyInReverseOrder::alive() == 8 * 3 * 2);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = DestroyInReverseOrder[8];
+ int count = 0;
+ DestroyInReverseOrder init(&count);
+ int init_count = 1;
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(count == 8 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3];
+ int count = 0;
+ DestroyInReverseOrder init[3] = {&count, &count, &count};
+ int init_count = 3;
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(count == 8 * 3 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[8][3][2];
+ int count = 0;
+ DestroyInReverseOrder init[3][2] = {{&count, &count}, {&count, &count}, {&count, &count}};
+ int init_count = 3 * 2;
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(count == 8 * 3 * 2 + init_count);
+ }
+ assert(count == init_count);
+ }
+ }
+
+ // Count the number of copies being made
+ {
+ // Without passing an initial value
+ {
+ using Array = CountCopies[8];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[8][3];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[8][3][2];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(CountCopies::copies() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = CountCopies[8];
+ int copies = 0;
+ CountCopies init(&copies);
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(copies == 8);
+ }
+ {
+ using Array = CountCopies[8][3];
+ int copies = 0;
+ CountCopies init[3] = {&copies, &copies, &copies};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(copies == 8 * 3);
+ }
+ {
+ using Array = CountCopies[8][3][2];
+ int copies = 0;
+ CountCopies init[3][2] = {{&copies, &copies}, {&copies, &copies}, {&copies, &copies}};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(copies == 8 * 3 * 2);
+ }
+ }
+
+ // Make sure array elements are aligned properly when the array contains an overaligned type.
+ //
+ // Here, we don't need to test both the with-initial-value and without-initial-value code paths,
+ // since we're just checking the alignment and both are going to use the same code path unless
+ // the implementation is completely crazy.
+ {
+ auto check_alignment = []<class T> {
+ {
+ using Array = T[8];
+ std::shared_ptr ptr = std::make_shared<Array>();
+ for (int i = 0; i < 8; ++i) {
+ T* p = std::addressof(ptr[i]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ {
+ using Array = T[8][3];
+ std::shared_ptr ptr = std::make_shared<Array>();
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ T* p = std::addressof(ptr[i][j]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ {
+ using Array = T[8][3][2];
+ std::shared_ptr ptr = std::make_shared<Array>();
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 2; ++k) {
+ T* p = std::addressof(ptr[i][j][k]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ }
+ };
+
+ struct Empty { };
+ check_alignment.operator()<Empty>();
+ check_alignment.operator()<OverAligned>();
+ check_alignment.operator()<MaxAligned>();
+
+ // test non corner cases as well while we're at it
+ struct Foo { int i; char c; };
+ check_alignment.operator()<int>();
+ check_alignment.operator()<Foo>();
+ }
+
+ // Make sure that we destroy all the elements constructed so far when an exception
+ // is thrown. Also make sure that we do it in reverse order of construction.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ {
+ struct Sentinel : ThrowOnConstruction, DestroyInReverseOrder { };
+
+ // Without passing an initial value
+ {
+ using Array = Sentinel[8];
+ for (int i = 0; i < 8; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = Sentinel[8];
+ for (int i = 0; i < 8; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init;
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 1);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[8][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3][2] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3 * 2);
+ }
+ }
+ }
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+ // Make sure the version without an initialization argument works even for non-movable types
+ {
+ using Array = NonMovable[8][3];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>();
+ (void)ptr;
+ }
+
+ // Make sure std::make_shared handles badly-behaved types properly
+ {
+ using Array = operator_hijacker[3];
+ std::shared_ptr<Array> p1 = std::make_shared<Array>();
+ std::shared_ptr<Array> p2 = std::make_shared<Array>(operator_hijacker());
+ assert(p1 != nullptr);
+ assert(p2 != nullptr);
+ }
+
+ // Check that we SFINAE-away for invalid arguments
+ {
+ struct T { };
+ static_assert( CanMakeShared<T[8]>);
+ static_assert( CanMakeShared<T[8], T>);
+ static_assert(!CanMakeShared<T[8], T, int>); // too many arguments
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.unbounded.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.unbounded.pass.cpp
new file mode 100644
index 0000000000000..44365b0bf4f6e
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.array.unbounded.pass.cpp
@@ -0,0 +1,431 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Aligned deallocation isn't provided before macOS 10.14, and some tests for overaligned types
+// below require that feature.
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13}}
+
+// <memory>
+
+// shared_ptr
+
+// template<class T> shared_ptr<T> make_shared(size_t N); // T is U[]
+//
+// template<class T>
+// shared_ptr<T> make_shared(size_t N, const remove_extent_t<T>& u); // T is U[]
+
+// Ignore error about requesting a large alignment not being ABI compatible with older AIX systems.
+#ifdef _AIX
+# pragma clang diagnostic ignored "-Waix-compat"
+#endif
+
+#include <cassert>
+#include <concepts>
+#include <cstdint> // std::uintptr_t
+#include <memory>
+#include <utility>
+
+#include "operator_hijacker.h"
+#include "test_macros.h"
+#include "types.h"
+
+template <class T, class ...Args>
+concept CanMakeShared = requires(Args&& ...args) {
+ { std::make_shared<T>(std::forward<Args>(args)...) } -> std::same_as<std::shared_ptr<T>>;
+};
+
+int main(int, char**) {
+ // Check behavior for a zero-sized array
+ {
+ // Without passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(0);
+ assert(ptr != nullptr);
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(0, 42);
+ assert(ptr != nullptr);
+ }
+ }
+
+ // Check behavior for a 1-sized array
+ {
+ // Without passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(1);
+ assert(ptr != nullptr);
+ assert(ptr[0] == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(1, 42);
+ assert(ptr != nullptr);
+ assert(ptr[0] == 42);
+ }
+ }
+
+ // Make sure we initialize elements correctly
+ {
+ // Without passing an initial value
+ {
+ using Array = int[];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == 0);
+ }
+ }
+ {
+ using Array = int[][3];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 0);
+ assert(ptr[i][1] == 0);
+ assert(ptr[i][2] == 0);
+ }
+ }
+ {
+ using Array = int[][3][2];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 0);
+ assert(ptr[i][0][1] == 0);
+ assert(ptr[i][1][0] == 0);
+ assert(ptr[i][1][1] == 0);
+ assert(ptr[i][2][0] == 0);
+ assert(ptr[i][2][1] == 0);
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = int[];
+ int init = 42;
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i] == init);
+ }
+ }
+ {
+ using Array = int[][3];
+ int init[3] = {42, 43, 44};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0] == 42);
+ assert(ptr[i][1] == 43);
+ assert(ptr[i][2] == 44);
+ }
+ }
+ {
+ using Array = int[][3][2];
+ int init[3][2] = {{31, 32}, {41, 42}, {51, 52}};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ for (unsigned i = 0; i < 8; ++i) {
+ assert(ptr[i][0][0] == 31);
+ assert(ptr[i][0][1] == 32);
+ assert(ptr[i][1][0] == 41);
+ assert(ptr[i][1][1] == 42);
+ assert(ptr[i][2][0] == 51);
+ assert(ptr[i][2][1] == 52);
+ }
+ }
+ }
+
+ // Make sure array elements are destroyed in reverse order
+ {
+ // Without passing an initial value
+ {
+ using Array = DestroyInReverseOrder[];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(DestroyInReverseOrder::alive() == 8);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(DestroyInReverseOrder::alive() == 8 * 3);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3][2];
+ DestroyInReverseOrder::reset();
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(DestroyInReverseOrder::alive() == 8 * 3 * 2);
+ }
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = DestroyInReverseOrder[];
+ int count = 0;
+ DestroyInReverseOrder init(&count);
+ int init_count = 1;
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(count == 8 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3];
+ int count = 0;
+ DestroyInReverseOrder init[3] = {&count, &count, &count};
+ int init_count = 3;
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(count == 8 * 3 + init_count);
+ }
+ assert(count == init_count);
+ }
+ {
+ using Array = DestroyInReverseOrder[][3][2];
+ int count = 0;
+ DestroyInReverseOrder init[3][2] = {{&count, &count}, {&count, &count}, {&count, &count}};
+ int init_count = 3 * 2;
+ {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(count == 8 * 3 * 2 + init_count);
+ }
+ assert(count == init_count);
+ }
+ }
+
+ // Count the number of copies being made
+ {
+ // Without passing an initial value
+ {
+ using Array = CountCopies[];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[][3];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(CountCopies::copies() == 0);
+ }
+ {
+ using Array = CountCopies[][3][2];
+ CountCopies::reset();
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(CountCopies::copies() == 0);
+ }
+
+ // Passing an initial value
+ {
+ using Array = CountCopies[];
+ int copies = 0;
+ CountCopies init(&copies);
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(copies == 8);
+ }
+ {
+ using Array = CountCopies[][3];
+ int copies = 0;
+ CountCopies init[3] = {&copies, &copies, &copies};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(copies == 8 * 3);
+ }
+ {
+ using Array = CountCopies[][3][2];
+ int copies = 0;
+ CountCopies init[3][2] = {{&copies, &copies}, {&copies, &copies}, {&copies, &copies}};
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(copies == 8 * 3 * 2);
+ }
+ }
+
+ // Make sure array elements are aligned properly when the array contains an overaligned type.
+ //
+ // Here, we don't need to test both the with-initial-value and without-initial-value code paths,
+ // since we're just checking the alignment and both are going to use the same code path unless
+ // the implementation is completely crazy.
+ {
+ auto check_alignment = []<class T> {
+ {
+ using Array = T[];
+ std::shared_ptr ptr = std::make_shared<Array>(8);
+ for (int i = 0; i < 8; ++i) {
+ T* p = std::addressof(ptr[i]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ {
+ using Array = T[][3];
+ std::shared_ptr ptr = std::make_shared<Array>(8);
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ T* p = std::addressof(ptr[i][j]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ {
+ using Array = T[][3][2];
+ std::shared_ptr ptr = std::make_shared<Array>(8);
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 2; ++k) {
+ T* p = std::addressof(ptr[i][j][k]);
+ assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
+ }
+ }
+ }
+ }
+ };
+
+ struct Empty { };
+ check_alignment.operator()<Empty>();
+ check_alignment.operator()<OverAligned>();
+ check_alignment.operator()<MaxAligned>();
+
+ // test non corner cases as well while we're at it
+ struct Foo { int i; char c; };
+ check_alignment.operator()<int>();
+ check_alignment.operator()<Foo>();
+ }
+
+ // Make sure that we destroy all the elements constructed so far when an exception
+ // is thrown. Also make sure that we do it in reverse order of construction.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ {
+ struct Sentinel : ThrowOnConstruction, DestroyInReverseOrder { };
+
+ // Without passing an initial value
+ {
+ using Array = Sentinel[];
+ for (int i = 0; i < 8; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ ThrowOnConstruction::throw_after(i);
+ DestroyInReverseOrder::reset();
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 0);
+ }
+ }
+ }
+
+ // Passing an initial value
+ {
+ using Array = Sentinel[];
+ for (int i = 0; i < 8; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init;
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 1);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3];
+ for (int i = 0; i < 8 * 3; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3);
+ }
+ }
+ }
+ {
+ using Array = Sentinel[][3][2];
+ for (int i = 0; i < 8 * 3 * 2; ++i) {
+ DestroyInReverseOrder::reset();
+ ThrowOnConstruction::reset();
+ Sentinel init[3][2] = {};
+ ThrowOnConstruction::throw_after(i);
+ try {
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8, init);
+ assert(false);
+ } catch (ThrowOnConstruction::exception const&) {
+ assert(DestroyInReverseOrder::alive() == 3 * 2);
+ }
+ }
+ }
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+ // Make sure the version without an initialization argument works even for non-movable types
+ {
+ using Array = NonMovable[][3];
+ std::shared_ptr<Array> ptr = std::make_shared<Array>(8);
+ (void)ptr;
+ }
+
+ // Make sure std::make_shared handles badly-behaved types properly
+ {
+ using Array = operator_hijacker[];
+ std::shared_ptr<Array> p1 = std::make_shared<Array>(3);
+ std::shared_ptr<Array> p2 = std::make_shared<Array>(3, operator_hijacker());
+ assert(p1 != nullptr);
+ assert(p2 != nullptr);
+ }
+
+ // Check that we SFINAE-away for invalid arguments
+ {
+ struct T { };
+ static_assert( CanMakeShared<T[], std::size_t>);
+ static_assert( CanMakeShared<T[], std::size_t, T>);
+ static_assert(!CanMakeShared<T[], std::size_t, T, int>); // too many arguments
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.pass.cpp
index e835b6263efac..23a904f8ae11f 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.pass.cpp
@@ -10,13 +10,15 @@
// shared_ptr
-// template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
+// template<class T, class... Args>
+// shared_ptr<T> make_shared(Args&&... args); // T is not an array
#include <memory>
#include <cassert>
-#include "test_macros.h"
#include "count_new.h"
+#include "operator_hijacker.h"
+#include "test_macros.h"
struct A
{
@@ -122,6 +124,14 @@ int main(int, char**)
#endif
assert(A::count == 0);
+ // Make sure std::make_shared handles badly-behaved types properly
+ {
+ std::shared_ptr<operator_hijacker> p1 = std::make_shared<operator_hijacker>();
+ std::shared_ptr<operator_hijacker> p2 = std::make_shared<operator_hijacker>(operator_hijacker());
+ assert(p1 != nullptr);
+ assert(p2 != nullptr);
+ }
+
test<bool>(true);
test<int>(3);
test<double>(5.0);
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.private.compile.fail.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.private.compile.fail.cpp
index e17d7424be444..fec160a9854db 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.private.compile.fail.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.private.compile.fail.cpp
@@ -10,7 +10,8 @@
// shared_ptr
-// template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
+// template<class T, class... Args>
+// shared_ptr<T> make_shared(Args&&... args);
#include <memory>
#include <cassert>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/types.h b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/types.h
new file mode 100644
index 0000000000000..7cd65231404fa
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/types.h
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_SHARED_CREATE_TYPES_H
+#define TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_SHARED_CREATE_TYPES_H
+
+#include <cassert>
+#include <cstddef>
+
+#include "test_macros.h"
+
+struct DestroyInReverseOrder {
+ static void reset() { global_count_ = 0; }
+ static int alive() { return global_count_; }
+
+ DestroyInReverseOrder()
+ : DestroyInReverseOrder(&global_count_)
+ { }
+
+ constexpr DestroyInReverseOrder(int* count)
+ : count_(count), value_(*count)
+ { ++*count_; }
+
+ constexpr DestroyInReverseOrder(DestroyInReverseOrder const& other)
+ : count_(other.count_), value_(*other.count_)
+ { ++*count_; }
+
+ constexpr int value() const { return value_; }
+
+ // Ensure that we destroy these objects in the reverse order as they were created.
+ constexpr ~DestroyInReverseOrder() {
+ --*count_;
+ assert(*count_ == value_);
+ }
+
+private:
+ int* count_;
+ int value_;
+ static int global_count_;
+};
+
+int DestroyInReverseOrder::global_count_ = 0;
+
+struct NonMovable {
+ NonMovable() = default;
+ NonMovable(NonMovable&&) = delete;
+};
+
+struct CountCopies {
+ static void reset() { global_count_ = 0; }
+ static int copies() { return global_count_; }
+
+ constexpr CountCopies() : copies_(&global_count_) { }
+ constexpr CountCopies(int* counter) : copies_(counter) { }
+ constexpr CountCopies(CountCopies const& other) : copies_(other.copies_) { ++*copies_; }
+
+private:
+ int* copies_;
+ static int global_count_;
+};
+
+int CountCopies::global_count_ = 0;
+
+struct alignas(alignof(std::max_align_t) * 2) OverAligned { };
+
+struct MaxAligned {
+ std::max_align_t foo;
+};
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+struct ThrowOnConstruction {
+ struct exception : std::exception { };
+
+ ThrowOnConstruction() { on_construct(); }
+ ThrowOnConstruction(ThrowOnConstruction const&) { on_construct(); }
+
+ static void reset() { throw_after_ = -1; }
+ static void throw_after(int n) { throw_after_ = n; }
+
+private:
+ static int throw_after_;
+ void on_construct() {
+ if (throw_after_ == 0)
+ throw exception{};
+
+ if (throw_after_ != -1)
+ --throw_after_;
+ }
+};
+
+int ThrowOnConstruction::throw_after_ = -1;
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+#endif // TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_SHARED_CREATE_TYPES_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 191d0ee01cc9e..61cee0dd2d332 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -608,7 +608,7 @@ def add_version_header(tc):
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_shared_mutex)",
}, {
"name": "__cpp_lib_shared_ptr_arrays",
- "values": { "c++17": 201611 },
+ "values": { "c++17": 201611, "c++20": 201707 },
"headers": ["memory"],
}, {
"name": "__cpp_lib_shared_ptr_weak_type",
More information about the libcxx-commits
mailing list