diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -1,6 +1,6 @@ "Paper #","Group","Paper Name","Meeting","Status","First released version" "`P0463R1 `__","LWG","Endian just Endian","Toronto","|Complete|","7.0" -"`P0674R1 `__","LWG","Extending make_shared to Support Arrays","Toronto","","" +"`P0674R1 `__","LWG","Extending make_shared to Support Arrays","Toronto","|Complete|","13.0" "","","","","","" "`P0020R6 `__","LWG","Floating Point Atomic","Albuquerque","","" "`P0053R7 `__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","","" diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -268,6 +268,8 @@ ------------------------------------------------- ----------------- ``__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/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -514,9 +514,29 @@ template D* get_deleter(shared_ptr const& p) noexcept; template - shared_ptr make_shared(Args&&... args); + shared_ptr make_shared(Args&&... args); // T is not array template - shared_ptr allocate_shared(const A& a, Args&&... args); + shared_ptr allocate_shared(const A& a, Args&&... args); // T is not array + +template + shared_ptr make_shared(size_t N); // T is U[] (since C++20) +template + shared_ptr allocate_shared(const A& a, size_t N); // T is U[] (since C++20) + +template + shared_ptr make_shared(); // T is U[N] (since C++20) +template + shared_ptr allocate_shared(const A& a); // T is U[N] (since C++20) + +template + shared_ptr make_shared(size_t N, const remove_extent_t& u); // T is U[] (since C++20) +template + shared_ptr allocate_shared(const A& a, size_t N, const remove_extent_t& u); // T is U[] (since C++20) + +template shared_ptr + make_shared(const remove_extent_t& u); // T is U[N] (since C++20) +template + shared_ptr allocate_shared(const A& a, const remove_extent_t& u); // T is U[N] (since C++20) template class weak_ptr @@ -2598,6 +2618,148 @@ _Storage __storage_; }; + +#if _LIBCPP_STD_VER > 17 + +// Destroys every element in the range [first, first + n) FROM RIGHT TO LEFT +// using allocator destruction by rebinding this::alloc to the correct allocator +// type. If elements are themselves C-style arrays, they are recursively +// destroyed in the same manner. +// +// This function assumes that destructors do not throw. +template +_LIBCPP_HIDE_FROM_ABI +void __shared_ptr_array_destroy_elements(_Alloc const& __alloc, _ValueType *__first, long __n) noexcept { + using _ValueAlloc = typename __allocator_traits_rebind<_Alloc, _ValueType>::type; + _ValueAlloc __value_alloc(__alloc); + for (--__n; __n >= 0; --__n) { + if constexpr (is_array_v<_ValueType>) { + __shared_ptr_array_destroy_elements(__alloc, __first[__n], extent_v<_ValueType>); + } else { + allocator_traits<_ValueAlloc>::destroy(__value_alloc, __first + __n); + } + } +} + +// Given a range delimited by [first, first + n), initializes +// the range [first, first + n) from left to right using the construct method +// of this::alloc rebound to the correct allocator type. +// +// Each element is initialized using allocator_traits construction, and the given +// arguments are passed to construction. The arguments are not perfect-forwarded +// because that could set us up for double-moves. If the elements of the range +// are themselves C-style arrays, they are recursively constructed in the same +// manner. +// +// If an exception is thrown, the initialized elements are destroyed in reverse +// order of initialization using allocator_traits destruction. +template +_LIBCPP_HIDE_FROM_ABI +void __shared_ptr_array_construct_elements(_Alloc const& __alloc, _ValueType *__first, size_t __n, _Args&& ...__args) { + using _ValueAlloc = typename __allocator_traits_rebind<_Alloc, _ValueType>::type; + _ValueAlloc __value_alloc(__alloc); + size_t __i = 0; + +#ifndef _LIBCPP_NO_EXCEPTIONS + try { +#endif // _LIBCPP_NO_EXCEPTIONS + for (; __i < __n; ++__i) { + if constexpr (is_array_v<_ValueType>) { + __shared_ptr_array_construct_elements(__alloc, _VSTD::begin(__first[__i]), extent_v<_ValueType>, __args...); + } else { + allocator_traits<_ValueAlloc>::construct(__value_alloc, __first + __i, __args...); + } + } +#ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + // When _ValueType is NOT an array: destroy the half-open range + // [__first, __first + __i + 1) which includes the last object that was fully + // construct, but not the object that was partially constructed. + // + // When _ValueType is an array: only destroy [__first, __first + __i), because the + // one-deeper level of recursion (the function that propagated the exception), has + // already destroyed all constructed elements in the array at __first + __i, as + // described above. + // + // NOTE: because elements are destroyed right to left, the second argument to + // __destroy_elements should always be one greater than the number of elements to be + // destroyed. + if constexpr (is_array_v<_ValueType>) + __shared_ptr_array_destroy_elements(__alloc, __first, __i); + else + __shared_ptr_array_destroy_elements(__alloc, __first, __i + 1); + throw; + } +#endif // _LIBCPP_NO_EXCEPTIONS +} + +template +struct __shared_ptr_array_unbounded : __shared_weak_count +{ + _LIBCPP_HIDE_FROM_ABI + _Tp* __get_elem_begin() noexcept { return reinterpret_cast<_Tp*>(&__data_); } + + template + _LIBCPP_HIDE_FROM_ABI + explicit __shared_ptr_array_unbounded(_Alloc const&__alloc, size_t __count, _Args&&... __args) + : __alloc_(__alloc), __count_(__count) { + __shared_ptr_array_construct_elements(__alloc, __get_elem_begin(), __count, _VSTD::forward<_Args>(__args)...); + } + +private: + virtual void __on_zero_shared() _NOEXCEPT { + __shared_ptr_array_destroy_elements(__alloc_, __get_elem_begin(), __count_); + } + + virtual void __on_zero_shared_weak() _NOEXCEPT { + using _CharAlloc = typename __allocator_traits_rebind<_Alloc, char>::type; + _CharAlloc __tmp(__alloc_); + __alloc_.~_Alloc(); + size_t __size = __count_ == 0 + ? sizeof(__shared_ptr_array_unbounded) + : (__count_ - 1) * sizeof(_Tp) + sizeof(__shared_ptr_array_unbounded); + allocator_traits<_CharAlloc>::deallocate(__tmp, reinterpret_cast(this), __size); + } + + [[no_unique_address]] _Alloc __alloc_; + size_t __count_; + using _DataBlock = typename std::aligned_storage::type; + _DataBlock __data_[1]; +}; + +template +struct __shared_ptr_array_bounded : __shared_weak_count +{ + _LIBCPP_HIDE_FROM_ABI + _Tp* __get_elem_begin() noexcept { return reinterpret_cast<_Tp*>(&__data_); } + + template + _LIBCPP_HIDE_FROM_ABI + explicit __shared_ptr_array_bounded(_Alloc const&__alloc, _Args&&... __args) + : __alloc_(__alloc) { + __shared_ptr_array_construct_elements(__alloc, __get_elem_begin(), _Count, _VSTD::forward<_Args>(__args)...); + } + +private: + virtual void __on_zero_shared() _NOEXCEPT { + __shared_ptr_array_destroy_elements(__alloc_, __get_elem_begin(), _Count); + } + + virtual void __on_zero_shared_weak() _NOEXCEPT { + using _CharAlloc = typename __allocator_traits_rebind<_Alloc, char>::type; + _CharAlloc __tmp(__alloc_); + __alloc_.~_Alloc(); + allocator_traits<_CharAlloc>::deallocate( + __tmp, reinterpret_cast(this), sizeof(__shared_ptr_array_bounded)); + } + + [[no_unique_address]] _Alloc __alloc_; + using _DataBlock = typename std::aligned_storage::type; + _DataBlock __data_[_Count]; +}; + +#endif // _LIBCPP_STD_VER > 17 + struct __shared_ptr_dummy_rebind_allocator_type; template <> class _LIBCPP_TEMPLATE_VIS allocator<__shared_ptr_dummy_rebind_allocator_type> @@ -3343,6 +3505,95 @@ return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...); } +#if _LIBCPP_STD_VER > 17 + +template +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> __allocate_shared_array_unbound(const _Alloc& __a, size_t __n, _Args&& ...__args) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array_unbounded<_ElemT, _Alloc>; + using _CharAllocator = typename __allocator_traits_rebind<_Alloc, char>::type; + + size_t __size = __n == 0 ? sizeof(_ControlBlock) : (__n - 1) * sizeof(_ElemT) + sizeof(_ControlBlock); + __allocation_guard<_CharAllocator> __guard(__a, __size); + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, __n, _VSTD::forward<_Args>(__args)...); + auto __control_block = reinterpret_cast<_ControlBlock*>(__guard.__release_ptr()); + return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem_begin(), _VSTD::addressof(*__control_block)); +} + +template +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> __allocate_shared_array_bound(const _Alloc& __a, _Args&& ...__args) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array_bounded<_ElemT, _Alloc, extent_v<_Tp>>; + using _ControlBlockAllocator = typename __allocator_traits_rebind<_Alloc, _ControlBlock>::type; + + __allocation_guard<_ControlBlockAllocator> __guard(__a, sizeof(_ControlBlock)); + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, _VSTD::forward<_Args>(__args)...); + return shared_ptr<_Tp>::__create_with_control_block(__guard.__get()->__get_elem_begin(), __guard.__release_ptr()); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a) +{ + return __allocate_shared_array_bound<_Tp>(__a); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) +{ + return __allocate_shared_array_bound<_Tp>(__a, __u); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) +{ + return __allocate_shared_array_unbound<_Tp>(__a, __n); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) +{ + return __allocate_shared_array_unbound<_Tp>(__a, __n, __u); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared() +{ + return __allocate_shared_array_bound<_Tp>(allocator<_Tp>()); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared(const remove_extent_t<_Tp>& __u) +{ + return __allocate_shared_array_bound<_Tp>(allocator<_Tp>(), __u); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared(size_t __n) +{ + return __allocate_shared_array_unbound<_Tp>(allocator<_Tp>(), __n); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u) +{ + return __allocate_shared_array_unbound<_Tp>(allocator<_Tp>(), __n, __u); +} + +#endif // _LIBCPP_STD_VER > 17 + + template inline _LIBCPP_INLINE_VISIBILITY bool diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -130,7 +130,8 @@ __cpp_lib_scoped_lock 201703L __cpp_lib_semaphore 201907L __cpp_lib_shared_mutex 201505L -__cpp_lib_shared_ptr_arrays 201611L +__cpp_lib_shared_ptr_arrays 201707L + 201611L // C++17 __cpp_lib_shared_ptr_weak_type 201606L __cpp_lib_shared_timed_mutex 201402L __cpp_lib_shift 201806L @@ -345,6 +346,8 @@ # 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/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 --- 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 @@ -28,6 +28,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] @@ -384,8 +385,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 @@ -534,8 +535,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 --- 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 @@ -121,6 +121,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] @@ -3074,8 +3075,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 @@ -4290,8 +4291,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/create_array.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/create_array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/create_array.pass.cpp @@ -0,0 +1,552 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// shared_ptr + +// template shared_ptr make_shared(size_t N); // T is U[] +// template +// shared_ptr allocate_shared(const A& a, size_t N); // T is U[] + +// template shared_ptr make_shared(); // T is U[N] +// template +// shared_ptr allocate_shared(const A& a); // T is U[N] + +// template +// shared_ptr make_shared(size_t N, const remove_extent_t& u); // T is U[] +// template +// shared_ptr allocate_shared(const A& a, size_t N, +// const remove_extent_t& u); // T is U[] + +// template shared_ptr +// make_shared(const remove_extent_t& u); // T is U[N] +// template +// shared_ptr allocate_shared(const A& a, +// const remove_extent_t& u); // T is U[N] + +#include +#include + +#include "count_new.h" +#include "test_macros.h" + +struct A { + static int count; + + int value; + A() : value(++count) {} + A(const A&) : value(++count) {} + // Ensure that we destroy these objects in the reverse order as they were created. + ~A() { assert(count-- == value); } +}; + +int A::count = 0; + +struct B { + std::max_align_t val; +}; + +struct C { + static int count; + + int value; + C() : value(++count) {} + // Ensure that we destroy these objects in the reverse order as they were created. + ~C() { assert(count-- == value); } +}; + +int C::count = 0; + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct D { + static int count; + static int total; + static int throwAt; + + int value; + D() : value(++count) { + ++total; + if (value == throwAt) + throw 0; + } + ~D() { assert(count-- == value); } +}; + +int D::count = 0; +int D::total = 0; +int D::throwAt = 0; + +template +struct ThrowsOnConstructAllocator : std::allocator { + ThrowsOnConstructAllocator() = default; + + template + ThrowsOnConstructAllocator(const ThrowsOnConstructAllocator& that) + : std::allocator(that) {} + + void construct(T*) { throw 0; } +}; +#endif // TEST_HAS_NO_EXCEPTIONS + +struct BigType { + char buff[128]; + + BigType(char value = 1) { + for (auto& e : buff) + e = value; + } + + long sum() { + long out = 0; + for (auto e : buff) + out += e; + return out; + } +}; + +struct NoCopyMove { + NoCopyMove() = default; + NoCopyMove(NoCopyMove const&) = delete; + NoCopyMove(NoCopyMove&&) = delete; +}; + +struct MoveOnly { + MoveOnly() = default; + MoveOnly(MoveOnly const&) = delete; + MoveOnly(MoveOnly&&) = default; +}; + +struct CopyCounter { + static int count; + CopyCounter() = default; + CopyCounter(const CopyCounter&) { ++count; } +}; + +int CopyCounter::count = 0; + +template +struct is_make_shared_invokable { + template + static auto test(int) + -> decltype((void)std::make_shared(std::declval()...), + std::true_type()); + + template + static std::false_type test(...); + + static constexpr bool value = decltype(test(0))::value; +}; + +template +struct is_allocate_shared_invokable { + template + static auto test(int) + -> decltype((void)std::allocate_shared(std::declval()...), + std::true_type()); + + template + static std::false_type test(...); + + static constexpr bool value = decltype(test(0))::value; +}; + +static int liveAllocCount = 0; + +template +struct SetsGlobalInDtor : std::allocator { + template + SetsGlobalInDtor(const SetsGlobalInDtor& that) + : std::allocator(that) { liveAllocCount++; } + SetsGlobalInDtor(const SetsGlobalInDtor& that) + : std::allocator(that) { liveAllocCount++; } + SetsGlobalInDtor() { liveAllocCount++; } + + ~SetsGlobalInDtor() { liveAllocCount--; } +}; + +// max_align_t * 3 + 1 means that we round up to max_align_t * 4, but then remove one byte for the +// id ("value"). +constexpr size_t overAlignedBufferSize = (alignof(std::max_align_t) * 4) - 1; + +// Size = (alignof(std::max_align_t) * 3) + 1. +struct alignas(alignof(std::max_align_t) * 2) OverAlignedType { + static int count; + char value; // Note: we can never have more than 255 of these. + char buff[alignof(std::max_align_t) * 3]; + + OverAlignedType() : value(++count) { + // Set every element in the buffer AND the padding after the buffer to count. + for (size_t i = 0; i < overAlignedBufferSize; ++i) { + buff[i] = count; + } + } + OverAlignedType(const OverAlignedType& that) : value(++count) { + memcpy(buff, that.buff, overAlignedBufferSize); + } + // Ensure that we destroy these objects in the reverse order as they were created. + ~OverAlignedType() { assert(count-- == value); } +}; + +int OverAlignedType::count = 0; + +int main() { + { + using Unbound = A[]; + { + std::shared_ptr ptr0 = std::make_shared(8); + assert(A::count == 8); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8); + assert(A::count == 8); + } + assert(A::count == 0); + + using Unbound1 = A[][2]; + { + std::shared_ptr ptr0 = std::make_shared(8); + assert(A::count == 16); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8); + assert(A::count == 16); + } + assert(A::count == 0); + + using Unbound2 = A[][2][2]; + { + std::shared_ptr ptr0 = std::make_shared(8); + assert(A::count == 32); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8); + assert(A::count == 32); + } + assert(A::count == 0); + } + + { + using Bound = A[8]; + { + std::shared_ptr ptr0 = std::make_shared(); + assert(A::count == 8); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + assert(A::count == 8); + } + assert(A::count == 0); + + using Bound1 = A[8][2]; + { + std::shared_ptr ptr0 = std::make_shared(); + assert(A::count == 16); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + assert(A::count == 16); + } + assert(A::count == 0); + + using Bound2 = A[8][2][2]; + { + std::shared_ptr ptr0 = std::make_shared(); + assert(A::count == 32); + } + assert(A::count == 0); + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + assert(A::count == 32); + } + assert(A::count == 0); + } + + { + using T = int[]; + std::shared_ptr ptr0 = std::make_shared(8, 42); + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator(), 8, 42); + + for (unsigned i = 0; i < 8; ++i) { + assert(ptr0[i] == 42); + assert(ptr1[i] == 42); + } + } + + { + using T = C[][2]; + std::shared_ptr ptr0 = std::make_shared(3); + std::shared_ptr ptr1 = std::allocate_shared(std::allocator(), 3); + assert(ptr0[0][0].value == 1); + assert(ptr0[0][1].value == 2); + assert(ptr0[1][0].value == 3); + assert(ptr0[1][1].value == 4); + assert(ptr0[2][0].value == 5); + assert(ptr0[2][1].value == 6); + assert(ptr1[0][0].value == 7); + assert(ptr1[0][1].value == 8); + assert(ptr1[1][0].value == 9); + assert(ptr1[1][1].value == 10); + assert(ptr1[2][0].value == 11); + assert(ptr1[2][1].value == 12); + } + + { + using T = int[8]; + std::shared_ptr ptr0 = std::make_shared(42); + std::shared_ptr ptr1 = std::allocate_shared(std::allocator(), 42); + + for (unsigned i = 0; i < 8; ++i) { + assert(ptr0[i] == 42); + assert(ptr1[i] == 42); + } + } + + { + using T = C[3][2]; + std::shared_ptr ptr0 = std::make_shared(); + std::shared_ptr ptr1 = std::allocate_shared(std::allocator()); + assert(ptr0[0][0].value == 1); + assert(ptr0[0][1].value == 2); + assert(ptr0[1][0].value == 3); + assert(ptr0[1][1].value == 4); + assert(ptr0[2][0].value == 5); + assert(ptr0[2][1].value == 6); + assert(ptr1[0][0].value == 7); + assert(ptr1[0][1].value == 8); + assert(ptr1[1][0].value == 9); + assert(ptr1[1][1].value == 10); + assert(ptr1[2][0].value == 11); + assert(ptr1[2][1].value == 12); + } + + { + using T = BigType[8]; + std::shared_ptr ptr = std::make_shared(); + for (unsigned i = 0; i < 8; ++i) + assert(ptr[i].sum() == 128); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using T = A[8]; + A::count = 0; + globalMemCounter.reset(); + globalMemCounter.throw_after = 0; + try { + std::shared_ptr ptr = std::make_shared(); + assert(false && "We should have thrown and recovered."); + } catch (...) { + assert(A::count == 0); + } + assert(globalMemCounter.checkOutstandingNewEq(0)); + } + + { + using T = A[8]; + A::count = 0; + globalMemCounter.reset(); + globalMemCounter.throw_after = 0; + try { + std::shared_ptr ptr = std::allocate_shared(std::allocator()); + assert(false && "We should have thrown and recovered."); + } catch (...) { + assert(A::count == 0); + } + assert(globalMemCounter.checkOutstandingNewEq(0)); + } + + { + using T = D[][2][2][2][4][4]; + D::count = 0; + D::throwAt = 512; + + try { + std::shared_ptr ptr = std::allocate_shared(std::allocator(), 8); + assert(false && "We should have thrown and recovered."); + } catch (...) { + } + assert(D::total == 512); + assert(D::count == 0); + } + + { + using T = int[8]; + try { + std::shared_ptr ptr = + std::allocate_shared(ThrowsOnConstructAllocator()); + assert(false && "We should have thrown and recovered."); + } catch (...) { + } + } +#endif // TEST_HAS_NO_EXCEPTIONS + + { + using T = int[8]; + assert(liveAllocCount == 0); + { + std::shared_ptr ptr = std::allocate_shared(SetsGlobalInDtor()); + assert(liveAllocCount != 0); + } + assert(liveAllocCount == 0); + } + + { + std::shared_ptr ptr1 = + std::allocate_shared(std::allocator()); + std::shared_ptr ptr2 = + std::allocate_shared(std::allocator(), 8); + std::shared_ptr ptr3 = + std::allocate_shared(std::allocator()); + std::shared_ptr ptr4 = + std::allocate_shared(std::allocator(), 8); + assert(ptr1.get()); + assert(ptr2.get()); + assert(ptr3.get()); + assert(ptr4.get()); + } + + { + using T = CopyCounter[]; + CopyCounter::count = 0; + std::shared_ptr ptr = std::make_shared(4, CopyCounter()); + assert(CopyCounter::count == 4); + } + + { + using T = CopyCounter[4]; + CopyCounter::count = 0; + std::shared_ptr ptr = std::make_shared(CopyCounter()); + assert(CopyCounter::count == 4); + } + // Zero-size array. + { + assert(A::count == 0); + using T = A[]; + std::shared_ptr ptr = std::make_shared(0); + assert(ptr.get() != nullptr); + // Make sure we don't construct any objects of type A. + assert(A::count == 0); + } + { + assert(A::count == 0); + using T = A[]; + std::shared_ptr ptr = std::allocate_shared(std::allocator(), 0); + assert(ptr.get() != nullptr); + // Make sure we don't construct any objects of type A. + assert(A::count == 0); + } + + // Over aligned types. + { + using T = OverAlignedType[4]; + std::shared_ptr ptr = std::make_shared(); + assert(ptr.get()); + assert(reinterpret_cast(ptr.get()) % alignof(OverAlignedType) == + 0); + assert(OverAlignedType::count == 4); + for (size_t i = 0; i < 4; i++) { + for (size_t buffIndex = 0; buffIndex < overAlignedBufferSize; + ++buffIndex) { + assert(static_cast(ptr[i].buff[buffIndex]) == i + 1); + } + } + } + assert(OverAlignedType::count == 0); + { + using T = OverAlignedType[]; + std::shared_ptr ptr = std::make_shared(4); + assert(ptr.get()); + assert(reinterpret_cast(ptr.get()) % alignof(OverAlignedType) == + 0); + assert(OverAlignedType::count == 4); + for (size_t i = 0; i < 4; i++) { + for (size_t buffIndex = 0; buffIndex < overAlignedBufferSize; + ++buffIndex) { + assert(static_cast(ptr[i].buff[buffIndex]) == i + 1); + } + } + } + assert(OverAlignedType::count == 0); + + // Make sure alignment works. + { + using T = B[8]; + std::shared_ptr ptr = std::make_shared(); + assert(reinterpret_cast(ptr.get()) % alignof(B) == 0); + } + { + using T = B[]; + std::shared_ptr ptr = std::make_shared(8); + assert(reinterpret_cast(ptr.get()) % alignof(B) == 0); + } + + // Test SFINAE. + { + static_assert(is_make_shared_invokable::value); + static_assert(is_allocate_shared_invokable, + size_t>::value); + + static_assert(is_make_shared_invokable::value); + static_assert( + is_allocate_shared_invokable >::value); + + static_assert(is_make_shared_invokable::value); + static_assert(is_allocate_shared_invokable, + size_t, int>::value); + + static_assert(is_make_shared_invokable::value); + static_assert( + is_allocate_shared_invokable, int>::value); + + static_assert(is_make_shared_invokable::value); + static_assert( + is_allocate_shared_invokable, size_t>::value); + + static_assert(is_make_shared_invokable::value); + static_assert( + is_allocate_shared_invokable >::value); + + static_assert(is_make_shared_invokable::value); + static_assert( + is_allocate_shared_invokable, size_t, A>::value); + + static_assert(is_make_shared_invokable::value); + static_assert( + is_allocate_shared_invokable, A>::value); + + // No size argument: + static_assert(!is_make_shared_invokable::value); + static_assert(!is_allocate_shared_invokable, size_t, + A>::value); + + // Too many args: + static_assert(!is_make_shared_invokable::value); + static_assert(!is_allocate_shared_invokable, + size_t, int, int>::value); + + // Ctor arg instead of element type: + static_assert(!is_make_shared_invokable::value); + static_assert( + !is_allocate_shared_invokable, int>::value); + } +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -511,7 +511,7 @@ "internal_depends": "!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",