diff --git a/libcxx/include/__memory/construct_at.h b/libcxx/include/__memory/construct_at.h --- a/libcxx/include/__memory/construct_at.h +++ b/libcxx/include/__memory/construct_at.h @@ -12,7 +12,10 @@ #include <__config> #include <__debug> +#include <__iterator/access.h> +#include <__memory/addressof.h> #include <__utility/forward.h> +#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -43,13 +46,41 @@ #if _LIBCPP_STD_VER > 14 -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +void destroy(_ForwardIterator, _ForwardIterator); + +template >> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 void destroy_at(_Tp* __loc) { _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at"); __loc->~_Tp(); } +#if _LIBCPP_STD_VER > 17 +template >> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +void destroy_at(_Tp* __loc) { + _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at"); + _VSTD::destroy(_VSTD::begin(*__loc), _VSTD::end(*__loc)); +} +#endif + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +void destroy(_ForwardIterator __first, _ForwardIterator __last) { + for (; __first != __last; ++__first) + _VSTD::destroy_at(_VSTD::addressof(*__first)); +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +_ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) { + for (; __n > 0; (void)++__first, --__n) + _VSTD::destroy_at(_VSTD::addressof(*__first)); + return __first; +} + #endif _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -122,21 +122,6 @@ #if _LIBCPP_STD_VER > 14 -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -void destroy(_ForwardIterator __first, _ForwardIterator __last) { - for (; __first != __last; ++__first) - _VSTD::destroy_at(_VSTD::addressof(*__first)); -} - -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -_ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) { - for (; __n > 0; (void)++__first, --__n) - _VSTD::destroy_at(_VSTD::addressof(*__first)); - return __first; -} - template inline _LIBCPP_INLINE_VISIBILITY void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) { diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -33,23 +34,98 @@ TEST_CONSTEXPR_CXX20 bool test() { - using Alloc = std::allocator; - int counter = 0; - int const N = 5; - Alloc alloc; - Counted* pool = std::allocator_traits::allocate(alloc, N); + // Basic support + { + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Counted* pool = std::allocator_traits::allocate(alloc, 5); - for (Counted* p = pool; p != pool + N; ++p) - std::allocator_traits::construct(alloc, p, &counter); - assert(counter == 5); + for (Counted* p = pool; p != pool + 5; ++p) + std::allocator_traits::construct(alloc, p, &counter); + assert(counter == 5); - std::destroy(pool, pool + 1); - assert(counter == 4); + std::destroy(pool, pool + 1); + static_assert(std::is_same_v); + assert(counter == 4); - std::destroy(forward_iterator(pool + 1), forward_iterator(pool + 5)); - assert(counter == 0); + std::destroy(pool + 1, pool + 5); + assert(counter == 0); - std::allocator_traits::deallocate(alloc, pool, N); + std::allocator_traits::deallocate(alloc, pool, 5); + } + + // Make sure it works with a bare forward_iterator + { + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Counted* pool = std::allocator_traits::allocate(alloc, 5); + + for (Counted* p = pool; p != pool + 5; ++p) + std::allocator_traits::construct(alloc, p, &counter); + assert(counter == 5); + + using It = forward_iterator; + std::destroy(It(pool), It(pool + 5)); + static_assert(std::is_same_v); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + + // Test support for array types +#if TEST_STD_VER > 17 + // TODO: Until std::construct_at has support for arrays, it's impossible to test this + // in a constexpr context. + if (std::is_constant_evaluated()) + return true; + + { + using Array = Counted[3]; + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + std::allocator_traits::construct(alloc, std::addressof(arr[i]), &counter); + } + } + assert(counter == 5 * 3); + + std::destroy(pool, pool + 5); + static_assert(std::is_same_v); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + { + using Array = Counted[3][2]; + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + for (int j = 0; j != 2; ++j) { + std::allocator_traits::construct(alloc, std::addressof(arr[i][j]), &counter); + } + } + } + assert(counter == 5 * 3 * 2); + + std::destroy(pool, pool + 5); + static_assert(std::is_same_v); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } +#endif return true; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "test_macros.h" @@ -56,6 +57,7 @@ assert(counter == 2); std::destroy_at(ptr1); + static_assert(std::is_same_v); assert(counter == 1); std::destroy_at(ptr2); @@ -76,6 +78,7 @@ assert(counter == 2); std::destroy_at(ptr1); + static_assert(std::is_same_v); assert(counter == 1); std::destroy_at(ptr2); @@ -85,6 +88,52 @@ std::allocator_traits::deallocate(alloc, ptr2, 1); } + // Test support for array types +#if TEST_STD_VER > 17 + // TODO: Until std::construct_at has support for arrays, it's impossible to test this + // in a constexpr context. + if (std::is_constant_evaluated()) + return true; + + { + using Array = Counted[3]; + using Alloc = std::allocator; + Alloc alloc; + Array* ptr = std::allocator_traits::allocate(alloc, 1); + Array& arr = *ptr; + + int counter = 0; + for (int i = 0; i != 3; ++i) + std::allocator_traits::construct(alloc, std::addressof(arr[i]), &counter); + assert(counter == 3); + + std::destroy_at(ptr); + static_assert(std::is_same_v); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, ptr, 1); + } + { + using Array = Counted[3][2]; + using Alloc = std::allocator; + Alloc alloc; + Array* ptr = std::allocator_traits::allocate(alloc, 1); + Array& arr = *ptr; + + int counter = 0; + for (int i = 0; i != 3; ++i) + for (int j = 0; j != 2; ++j) + std::allocator_traits::construct(alloc, std::addressof(arr[i][j]), &counter); + assert(counter == 3 * 2); + + std::destroy_at(ptr); + static_assert(std::is_same_v); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, ptr, 1); + } +#endif + return true; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -33,25 +34,101 @@ TEST_CONSTEXPR_CXX20 bool test() { - using Alloc = std::allocator; - int counter = 0; - int const N = 5; - Alloc alloc; - Counted* pool = std::allocator_traits::allocate(alloc, N); + // Basic support + { + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Counted* pool = std::allocator_traits::allocate(alloc, 5); - for (Counted* p = pool; p != pool + N; ++p) - std::allocator_traits::construct(alloc, p, &counter); - assert(counter == 5); + for (Counted* p = pool; p != pool + 5; ++p) + std::allocator_traits::construct(alloc, p, &counter); + assert(counter == 5); - Counted* np = std::destroy_n(pool, 1); - assert(np == pool + 1); - assert(counter == 4); + assert(std::destroy_n(pool, 1) == pool + 1); + static_assert(std::is_same_v); + assert(counter == 4); - forward_iterator it = std::destroy_n(forward_iterator(pool + 1), 4); - assert(it == forward_iterator(pool + 5)); - assert(counter == 0); + assert(std::destroy_n(pool + 1, 4) == pool + 5); + assert(counter == 0); - std::allocator_traits::deallocate(alloc, pool, N); + std::allocator_traits::deallocate(alloc, pool, 5); + } + + // Make sure it works with a bare forward_iterator + { + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Counted* pool = std::allocator_traits::allocate(alloc, 5); + + for (Counted* p = pool; p != pool + 5; ++p) + std::allocator_traits::construct(alloc, p, &counter); + assert(counter == 5); + + using It = forward_iterator; + It it = std::destroy_n(It(pool), 5); + static_assert(std::is_same_v); + assert(it == It(pool + 5)); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + + // Test support for array types +#if TEST_STD_VER > 17 + // TODO: Until std::construct_at has support for arrays, it's impossible to test this + // in a constexpr context. + if (std::is_constant_evaluated()) + return true; + + { + using Array = Counted[3]; + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + std::allocator_traits::construct(alloc, std::addressof(arr[i]), &counter); + } + } + assert(counter == 5 * 3); + + Array* p = std::destroy_n(pool, 5); + static_assert(std::is_same_v); + assert(p == pool + 5); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } + { + using Array = Counted[3][2]; + using Alloc = std::allocator; + int counter = 0; + Alloc alloc; + Array* pool = std::allocator_traits::allocate(alloc, 5); + + for (Array* p = pool; p != pool + 5; ++p) { + Array& arr = *p; + for (int i = 0; i != 3; ++i) { + for (int j = 0; j != 2; ++j) { + std::allocator_traits::construct(alloc, std::addressof(arr[i][j]), &counter); + } + } + } + assert(counter == 5 * 3 * 2); + + Array* p = std::destroy_n(pool, 5); + static_assert(std::is_same_v); + assert(p == pool + 5); + assert(counter == 0); + + std::allocator_traits::deallocate(alloc, pool, 5); + } +#endif return true; }