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|","12.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 @@ -266,6 +266,8 @@ ------------------------------------------------- ----------------- ``__cpp_lib_semaphore`` ``201907L`` ------------------------------------------------- ----------------- + ``__cpp_lib_shared_ptr_arrays`` ``201707L`` + ------------------------------------------------- ----------------- ``__cpp_lib_shift`` *unimplemented* ------------------------------------------------- ----------------- ``__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 @@ -2660,6 +2680,101 @@ _Storage __storage_; }; + +#if _LIBCPP_STD_VER > 17 +template +struct __shared_ptr_array + : __shared_weak_count +{ + template + __shared_ptr_array(_Alloc const&__alloc, size_t __size, _Et const&__element) + : __alloc(__alloc), __size(__size) { + if constexpr (is_same_v<_Et, false_type>) + __construct_elements(__get_elem_begin(), __size); + else + __construct_elements(__get_elem_begin(), __size, __element); + } + + _LIBCPP_HIDE_FROM_ABI + _Tp* __get_elem_begin() _NOEXCEPT { return reinterpret_cast<_Tp*>(&__data); } + +private: + // 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 __destroy_elements(_ValueType *__first, int __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>) { + __destroy_elements(__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 __construct_elements(_ValueType *__first, size_t __n, _Args&& ...__args) { + using _ValueAlloc = typename __allocator_traits_rebind<_Alloc, _ValueType>::type; + auto __current = __first; + _ValueAlloc __value_alloc(__alloc); + + #ifndef _LIBCPP_NO_EXCEPTIONS + try { + #endif + for (; __n > 0; (void)++__current, --__n) { + if constexpr (is_array_v<_ValueType>) { + __construct_elements(_VSTD::begin(*__current), extent_v<_ValueType>, __args...); + } else { + allocator_traits<_ValueAlloc>::construct(__value_alloc, __current, __args...); + } + } + #ifndef _LIBCPP_NO_EXCEPTIONS + } catch (...) { + __destroy_elements(__first, __n); + throw; + } + #endif + } + + virtual void __on_zero_shared() _NOEXCEPT { + __destroy_elements(__get_elem_begin(), __size); + } + + virtual void __on_zero_shared_weak() _NOEXCEPT { + using _CharAlloc = typename __allocator_traits_rebind<_Alloc, char>::type; + _CharAlloc __tmp(__alloc); + size_t __full_size = __size * sizeof(_Tp) + sizeof(__shared_ptr_array); + allocator_traits<_CharAlloc>::deallocate(__tmp, reinterpret_cast(this), __full_size); + } + + [[no_unique_address]] _Alloc __alloc; + size_t __size; + using _DataBlock = typename std::aligned_storage::type; + _DataBlock __data[0]; +}; + +#endif // _LIBCPP_STD_VER > 17 + struct __shared_ptr_dummy_rebind_allocator_type; template <> class _LIBCPP_TEMPLATE_VIS allocator<__shared_ptr_dummy_rebind_allocator_type> @@ -3362,13 +3477,61 @@ return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem(), _VSTD::addressof(*__control_block)); } -template::value> > +#if _LIBCPP_STD_VER > 17 + +template +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> __allocate_shared_array(const _Alloc& __a, size_t __n, const _Et& __u) +{ + using _ElemT = remove_extent_t<_Tp>; + using _ControlBlock = __shared_ptr_array<_ElemT, _Alloc>; + using _CharAllocator = typename __allocator_traits_rebind<_Alloc, char>::type; + + size_t __size = __n * sizeof(_ElemT) + sizeof(_ControlBlock); + __allocation_guard<_CharAllocator> __guard(__a, __size); + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__a, __n, __u); + 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::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a) +{ + return __allocate_shared_array<_Tp>(__a, extent_v<_Tp>, false_type()); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) +{ + return __allocate_shared_array<_Tp>(__a, extent_v<_Tp>, __u); +} + +template::value> > +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) +{ + return __allocate_shared_array<_Tp>(__a, __n, false_type()); +} + +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<_Tp>(__a, __n, __u); +} + +#endif // _LIBCPP_STD_VER > 17 + +template _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&& ...__args) { return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...); } + template inline _LIBCPP_INLINE_VISIBILITY bool diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -129,7 +129,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 @@ -339,6 +340,8 @@ # if !defined(_LIBCPP_HAS_NO_THREADS) # 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 @@ -26,6 +26,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] @@ -388,8 +389,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 @@ -544,8 +545,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 @@ -118,6 +118,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] @@ -3046,8 +3047,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 @@ -4267,8 +4268,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,293 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, 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) {} + ~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) {} + ~C() { assert(count-- == value); } +}; + +int C::count = 0; + +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; +}; + +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); + } + + { + 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)); + } + + { + 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()); + } + + // make sure alignment works + { + using T = B[8]; + + std::shared_ptr ptr = std::make_shared(); + assert(reinterpret_cast(ptr.get()) % alignof(B) == 0); + } +} 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 @@ -507,7 +507,7 @@ "internal_depends": "!defined(_LIBCPP_HAS_NO_THREADS)", }, { "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",