diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -94,7 +94,7 @@ Uninitialised memory,uninitialized_default_construct_n,Konstantin Varlamov,`D115315 `_,✅ Uninitialised memory,uninitialized_value_construct,Konstantin Varlamov,`D115626 `_,✅ Uninitialised memory,uninitialized_value_construct_n,Konstantin Varlamov,`D115626 `_,✅ -Uninitialised memory,destroy,Konstantin Varlamov,n/a,Not started -Uninitialised memory,destroy_n,Konstantin Varlamov,n/a,Not started -Uninitialised memory,destroy_at,Konstantin Varlamov,n/a,Not started -Uninitialised memory,construct_at,Konstantin Varlamov,n/a,Not started +Uninitialised memory,destroy,Konstantin Varlamov,`D116078 `_,✅ +Uninitialised memory,destroy_n,Konstantin Varlamov,`D116078 `_,✅ +Uninitialised memory,destroy_at,Konstantin Varlamov,`D116078 `_,✅ +Uninitialised memory,construct_at,Konstantin Varlamov,`D116078 `_,✅ diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -28,10 +28,10 @@ | `ranges::uninitialized_move_n `_ | `ranges::uninitialized_fill `_ | `ranges::uninitialized_fill_n `_ -| ranges::construct_at -| ranges::destroy -| ranges::destroy_at -| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,In progress +| `ranges::construct_at `_ +| `ranges::destroy `_ +| `ranges::destroy_at `_ +| `ranges::destroy_n `_",[special.mem.concepts],Konstantin Varlamov,✅ [strings],Adds begin/end and updates const_iterator.,[iterator.concepts],Unassigned,Not started [views.span],Same as [strings],[iterator.concepts],Unassigned,Not started `[iterator.cust.move] `_,`ranges::iter_move `_,,Zoe Carver,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -249,6 +249,7 @@ __memory/concepts.h __memory/construct_at.h __memory/pointer_traits.h + __memory/ranges_construct_at.h __memory/ranges_uninitialized_algorithms.h __memory/raw_storage_iterator.h __memory/shared_ptr.h 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 @@ -14,6 +14,7 @@ #include <__debug> #include <__iterator/access.h> #include <__memory/addressof.h> +#include <__memory/voidify.h> #include <__utility/forward.h> #include #include @@ -31,10 +32,10 @@ template()) _Tp(declval<_Args>()...) )> -_LIBCPP_INLINE_VISIBILITY +_LIBCPP_HIDE_FROM_ABI constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) { _LIBCPP_ASSERT(__location, "null pointer given to construct_at"); - return ::new ((void*)__location) _Tp(_VSTD::forward<_Args>(__args)...); + return ::new (_VSTD::__voidify(*__location)) _Tp(_VSTD::forward<_Args>(__args)...); } #endif @@ -46,7 +47,7 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 -void __destroy(_ForwardIterator, _ForwardIterator); +_ForwardIterator __destroy(_ForwardIterator, _ForwardIterator); template ::value, int>::type = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 @@ -66,9 +67,10 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 -void __destroy(_ForwardIterator __first, _ForwardIterator __last) { +_ForwardIterator __destroy(_ForwardIterator __first, _ForwardIterator __last) { for (; __first != __last; ++__first) _VSTD::__destroy_at(_VSTD::addressof(*__first)); + return __first; } #if _LIBCPP_STD_VER > 14 @@ -90,7 +92,7 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 void destroy(_ForwardIterator __first, _ForwardIterator __last) { - _VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last)); + (void)_VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last)); } template diff --git a/libcxx/include/__memory/ranges_construct_at.h b/libcxx/include/__memory/ranges_construct_at.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__memory/ranges_construct_at.h @@ -0,0 +1,144 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 _LIBCPP___MEMORY_RANGES_CONSTRUCT_AT_H +#define _LIBCPP___MEMORY_RANGES_CONSTRUCT_AT_H + +#include <__concepts/destructible.h> +#include <__config> +#include <__function_like.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/readable_traits.h> +#include <__memory/concepts.h> +#include <__memory/construct_at.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) +namespace ranges { + +// construct_at + +namespace __construct_at { + +struct __fn final : private __function_like { + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + template()) _Tp(declval<_Args>()...) + )> + _LIBCPP_HIDE_FROM_ABI + constexpr _Tp* operator()(_Tp* __location, _Args&& ...__args) const { + return _VSTD::construct_at(__location, _VSTD::forward<_Args>(__args)...); + } + +}; + +} // namespace __construct_at + +inline namespace __cpo { +inline constexpr auto construct_at = __construct_at::__fn(__function_like::__tag()); +} // namespace __cpo + +// destroy_at + +namespace __destroy_at { + +struct __fn final : private __function_like { + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + template + _LIBCPP_HIDE_FROM_ABI + constexpr void operator()(_Tp* __location) const noexcept { + _VSTD::destroy_at(__location); + } + +}; + +} // namespace __destroy_at + +inline namespace __cpo { +inline constexpr auto destroy_at = __destroy_at::__fn(__function_like::__tag()); +} // namespace __cpo + +// destroy + +namespace __destroy { + +struct __fn final : private __function_like { + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + template <__nothrow_input_iterator _InputIterator, __nothrow_sentinel_for<_InputIterator> _Sentinel> + requires destructible> + _LIBCPP_HIDE_FROM_ABI + constexpr _InputIterator operator()(_InputIterator __first, _Sentinel __last) const noexcept { + return _VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last)); + } + + template <__nothrow_input_range _InputRange> + requires destructible> + _LIBCPP_HIDE_FROM_ABI + constexpr borrowed_iterator_t<_InputRange> operator()(_InputRange&& __range) const noexcept { + return (*this)(ranges::begin(__range), ranges::end(__range)); + } + +}; + +} // namespace __destroy + +inline namespace __cpo { +inline constexpr auto destroy = __destroy::__fn(__function_like::__tag()); +} // namespace __cpo + +// destroy_n + +namespace __destroy_n { + +struct __fn final : private __function_like { + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {} + + template <__nothrow_input_iterator _InputIterator> + requires destructible> + _LIBCPP_HIDE_FROM_ABI + constexpr _InputIterator operator()(_InputIterator __first, iter_difference_t<_InputIterator> __n) const noexcept { + return _VSTD::destroy_n(_VSTD::move(__first), __n); + } + +}; + +} // namespace __destroy_n + +inline namespace __cpo { +inline constexpr auto destroy_n = __destroy_n::__fn(__function_like::__tag()); +} // namespace __cpo + +} // namespace ranges +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___MEMORY_RANGES_CONSTRUCT_AT_H diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -244,15 +244,40 @@ template constexpr T* construct_at(T* location, Args&& ...args); // since C++20 +namespace ranges { + template + constexpr T* construct_at(T* location, Args&&... args); // since C++20 +} + template void destroy_at(T* location); // constexpr in C++20 +namespace ranges { + template + constexpr void destroy_at(T* location) noexcept; // since C++20 +} + template void destroy(ForwardIterator first, ForwardIterator last); // constexpr in C++20 +namespace ranges { + template Sentinel> + requires destructible> + constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20 + template + requires destructible> + constexpr borrowed_iterator_t destroy(InputRange&& range) noexcept; // since C++20 +} + template ForwardIterator destroy_n(ForwardIterator first, Size n); // constexpr in C++20 +namespace ranges { + template + requires destructible> + constexpr InputIterator destroy_n(InputIterator first, iter_difference_t n) noexcept; // since C++20 +} + template ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result); @@ -790,6 +815,7 @@ #include <__memory/concepts.h> #include <__memory/construct_at.h> #include <__memory/pointer_traits.h> +#include <__memory/ranges_construct_at.h> #include <__memory/ranges_uninitialized_algorithms.h> #include <__memory/raw_storage_iterator.h> #include <__memory/shared_ptr.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -661,6 +661,10 @@ module concepts { private header "__memory/concepts.h" } module construct_at { private header "__memory/construct_at.h" } module pointer_traits { private header "__memory/pointer_traits.h" } + module ranges_construct_at { + private header "__memory/ranges_construct_at.h" + export __function_like + } module ranges_uninitialized_algorithms { private header "__memory/ranges_uninitialized_algorithms.h" export __function_like diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__memory/ranges_construct_at.h'}} +#include <__memory/ranges_construct_at.h> diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp @@ -18,7 +18,9 @@ #include #include +#include +#include "test_iterators.h" struct Foo { int a; @@ -97,15 +99,23 @@ return true; } -// Make sure std::construct_at SFINAEs out based on the validity of calling -// the constructor, instead of hard-erroring. -template -constexpr bool test_sfinae(int) { return false; } -template -constexpr bool test_sfinae(...) { return true; } -static_assert(test_sfinae(int())); +template ()...))> +constexpr bool can_construct_at(Args&&...) { return true; } + +template +constexpr bool can_construct_at(...) { return false; } + +// Check that SFINAE works. +static_assert( can_construct_at((int*)nullptr, 42)); +static_assert( can_construct_at((Foo*)nullptr, 1, '2', 3.0)); +static_assert(!can_construct_at((Foo*)nullptr, 1, '2')); +static_assert(!can_construct_at((Foo*)nullptr, 1, '2', 3.0, 4)); +static_assert(!can_construct_at(nullptr, 1, '2', 3.0)); +static_assert(!can_construct_at((int*)nullptr, 1, '2', 3.0)); +static_assert(!can_construct_at(contiguous_iterator(), 1, '2', 3.0)); +// Can't construct function pointers. +static_assert(!can_construct_at((int(*)())nullptr)); +static_assert(!can_construct_at((int(*)())nullptr, nullptr)); int main(int, char**) { diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges + +// +// +// namespace ranges { +// template +// constexpr T* construct_at(T* location, Args&&... args); // since C++20 +// } + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +// TODO(varconst): consolidate the ADL checks into a single file. +// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, +// implementations are allowed to use a different mechanism to achieve this effect, so this check is +// libc++-specific. +LIBCPP_STATIC_ASSERT(std::is_class_v); + +struct Foo { + int x = 0; + int y = 0; + + constexpr Foo() = default; + constexpr explicit Foo(int set_x, int set_y) : x(set_x), y(set_y) {} + constexpr Foo(std::initializer_list); + + void operator&() const = delete; + void operator,(auto&&) const = delete; +}; + +ASSERT_SAME_TYPE(decltype(std::ranges::construct_at((int*)nullptr)), int*); +ASSERT_SAME_TYPE(decltype(std::ranges::construct_at((Foo*)nullptr)), Foo*); + +struct Counted { + int& count; + + constexpr Counted(int& count_ref) : count(count_ref) { ++count; } + constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; } + constexpr ~Counted() { --count; } +}; + +constexpr bool test() { + // Value initialization. + { + int x = 1; + + int* result = std::ranges::construct_at(&x); + assert(result == &x); + assert(x == 0); + } + + // Copy initialization. + { + int x = 1; + + int* result = std::ranges::construct_at(&x, 42); + assert(result == &x); + assert(x == 42); + } + + // Explicit multiargument constructor; also checks that the initializer list constructor is not invoked. + { + Foo f; + + Foo* result = std::ranges::construct_at(std::addressof(f), 42, 123); + assert(result == std::addressof(f)); + assert(f.x == 42); + assert(f.y == 123); + } + + // Works with buffers of uninitialized memory. + { + std::allocator alloc; + Counted* out = alloc.allocate(2); + int count = 0; + + Counted* result = std::ranges::construct_at(out, count); + assert(result == out); + assert(count == 1); + + result = std::ranges::construct_at(out + 1, count); + assert(result == out + 1); + assert(count == 2); + + std::destroy(out, out + 1); + alloc.deallocate(out, 2); + } + + // Works with const pointers. + { + int x = 1; + const int* ptr = &x; + + const int* result = std::ranges::construct_at(ptr, 42); + assert(result == ptr); + assert(x == 42); + } + + return true; +} + +constexpr bool can_construct_at(auto&&... args) + requires requires { std::ranges::construct_at(decltype(args)(args)...); } + { return true; } + +constexpr bool can_construct_at(auto&&...) { return false; } + +// Check that SFINAE works. +static_assert( can_construct_at((Foo*)nullptr, 1, 2)); +static_assert(!can_construct_at((Foo*)nullptr, 1)); +static_assert(!can_construct_at((Foo*)nullptr, 1, 2, 3)); +static_assert(!can_construct_at(nullptr, 1, 2)); +static_assert(!can_construct_at((int*)nullptr, 1, 2)); +static_assert(!can_construct_at(contiguous_iterator(), 1, 2)); +// Can't construct function pointers. +static_assert(!can_construct_at((int(*)())nullptr)); +static_assert(!can_construct_at((int(*)())nullptr, nullptr)); +// TODO(varconst): check that array types work once D114649 implementing LWG3639 lands. + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} 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 @@ -109,7 +109,7 @@ test_arrays(); static_assert(tests()); // TODO: Until std::construct_at has support for arrays, it's impossible to test this - // in a constexpr context. + // in a constexpr context (see https://reviews.llvm.org/D114903). // static_assert(test_arrays()); #endif return 0; 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 @@ -30,13 +30,12 @@ int* counter_; TEST_CONSTEXPR VirtualCounted(int* counter) : counter_(counter) { ++*counter_; } TEST_CONSTEXPR_CXX20 virtual ~VirtualCounted() { --*counter_; } - friend void operator&(VirtualCounted) = delete; + void operator&() const = delete; }; struct DerivedCounted : VirtualCounted { TEST_CONSTEXPR DerivedCounted(int* counter) : VirtualCounted(counter) { } TEST_CONSTEXPR_CXX20 ~DerivedCounted() override { } - friend void operator&(DerivedCounted) = delete; }; #if TEST_STD_VER > 17 @@ -135,7 +134,7 @@ test_arrays(); static_assert(test()); // TODO: Until std::construct_at has support for arrays, it's impossible to test this - // in a constexpr context. + // in a constexpr context (see https://reviews.llvm.org/D114903). // static_assert(test_arrays()); #endif return 0; 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 @@ -112,7 +112,7 @@ test_arrays(); static_assert(tests()); // TODO: Until std::construct_at has support for arrays, it's impossible to test this - // in a constexpr context. + // in a constexpr context (see https://reviews.llvm.org/D114903). // static_assert(test_arrays()); #endif return 0; diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp @@ -0,0 +1,222 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges + +// +// +// namespace ranges { +// template Sentinel> +// requires destructible> +// constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20 +// template +// requires destructible> +// constexpr borrowed_iterator_t destroy(InputRange&& range) noexcept; // since C++20 +// } + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +// TODO(varconst): consolidate the ADL checks into a single file. +// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, +// implementations are allowed to use a different mechanism to achieve this effect, so this check is +// libc++-specific. +LIBCPP_STATIC_ASSERT(std::is_class_v); + +struct NotNothrowDtrable { + ~NotNothrowDtrable() noexcept(false) {} +}; +static_assert(!std::is_invocable_v); + +struct Counted { + int& count; + + constexpr Counted(int& count_ref) : count(count_ref) { ++count; } + constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; } + constexpr ~Counted() { --count; } + + friend void operator&(Counted) = delete; +}; + +template +constexpr void test() { + // (iterator + sentinel) overload. + { + constexpr int N = 5; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Counted* out = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Traits::construct(alloc, out + i, counter); + } + assert(counter == N); + + std::ranges::destroy(Iterator(out), Iterator(out + N)); + assert(counter == 0); + + Traits::deallocate(alloc, out, N); + } + + // (range) overload. + { + constexpr int N = 5; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Counted* out = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Traits::construct(alloc, out + i, counter); + } + assert(counter == N); + + auto range = std::ranges::subrange(Iterator(out), Iterator(out + N)); + std::ranges::destroy(range); + assert(counter == 0); + + Traits::deallocate(alloc, out, N); + } +} + +constexpr bool tests() { + test(); + test>(); + + return true; +} + +constexpr bool test_arrays() { + // One-dimensional array, (iterator + sentinel) overload. + { + constexpr int N = 5; + constexpr int M = 3; + + using Array = Counted[M]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* buffer = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Array& array_ref = *(buffer + i); + for (int j = 0; j != M; ++j) { + Traits::construct(alloc, std::addressof(array_ref[j]), counter); + } + } + assert(counter == N * M); + + std::ranges::destroy(buffer, buffer + N); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, N); + } + + // One-dimensional array, (range) overload. + { + constexpr int N = 5; + constexpr int A = 3; + + using Array = Counted[A]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* buffer = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Array& array_ref = *(buffer + i); + for (int j = 0; j != A; ++j) { + Traits::construct(alloc, std::addressof(array_ref[j]), counter); + } + } + assert(counter == N * A); + + auto range = std::ranges::subrange(buffer, buffer + N); + std::ranges::destroy(range); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, N); + } + + // Multidimensional array, (iterator + sentinel ) overload. + { + constexpr int N = 5; + constexpr int A = 3; + constexpr int B = 3; + + using Array = Counted[A][B]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* buffer = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Array& array_ref = *(buffer + i); + for (int j = 0; j != A; ++j) { + for (int k = 0; k != B; ++k) { + Traits::construct(alloc, std::addressof(array_ref[j][k]), counter); + } + } + } + assert(counter == N * A * B); + + std::ranges::destroy(buffer, buffer + N); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, N); + } + + // Multidimensional array, (range) overload. + { + constexpr int N = 5; + constexpr int A = 3; + constexpr int B = 3; + + using Array = Counted[A][B]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* buffer = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Array& array_ref = *(buffer + i); + for (int j = 0; j != A; ++j) { + for (int k = 0; k != B; ++k) { + Traits::construct(alloc, std::addressof(array_ref[j][k]), counter); + } + } + } + assert(counter == N * A * B); + + std::ranges::destroy(buffer, buffer + N); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, N); + } + + return true; +} + +int main(int, char**) { + tests(); + test_arrays(); + + static_assert(tests()); + // TODO: Until std::construct_at has support for arrays, it's impossible to test this + // in a constexpr context (see https://reviews.llvm.org/D114903). + // static_assert(test_arrays()); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges + +// +// +// namespace ranges { +// template +// constexpr void destroy_at(T* location) noexcept; // since C++20 +// } + +#include +#include +#include + +#include "test_macros.h" + +// TODO(varconst): consolidate the ADL checks into a single file. +// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, +// implementations are allowed to use a different mechanism to achieve this effect, so this check is +// libc++-specific. +LIBCPP_STATIC_ASSERT(std::is_class_v); + +struct NotNothrowDtrable { + ~NotNothrowDtrable() noexcept(false) {} +}; +static_assert(!std::is_invocable_v); + +struct Counted { + int& count; + + constexpr Counted(int& count_ref) : count(count_ref) { ++count; } + constexpr ~Counted() { --count; } + + friend void operator&(Counted) = delete; +}; + +struct VirtualCountedBase { + int& count; + + constexpr VirtualCountedBase(int& count_ref) : count(count_ref) { ++count; } + constexpr virtual ~VirtualCountedBase() { --count; } + + void operator&() const = delete; +}; + +struct VirtualCountedDerived : VirtualCountedBase { + constexpr VirtualCountedDerived(int& count_ref) : VirtualCountedBase(count_ref) {} + + // Without a definition, GCC gives an error when the destructor is invoked in a constexpr context (see + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93413). + constexpr ~VirtualCountedDerived() override {} +}; + +constexpr bool test() { + // Destroying a "trivial" object. + { + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Counted* buffer = Traits::allocate(alloc, 2); + Traits::construct(alloc, buffer, counter); + Traits::construct(alloc, buffer + 1, counter); + assert(counter == 2); + + std::ranges::destroy_at(buffer); + assert(counter == 1); + std::ranges::destroy_at(buffer + 1); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, 2); + } + + // Destroying a derived object with a virtual destructor. + { + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + VirtualCountedDerived* buffer = Traits::allocate(alloc, 2); + Traits::construct(alloc, buffer, counter); + Traits::construct(alloc, buffer + 1, counter); + assert(counter == 2); + + std::ranges::destroy_at(buffer); + assert(counter == 1); + std::ranges::destroy_at(buffer + 1); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, 2); + } + + return true; +} + +constexpr bool test_arrays() { + // Pointer to an array. + { + using Array = Counted[3]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* array = Traits::allocate(alloc, 1); + Array& array_ref = *array; + for (int i = 0; i != 3; ++i) { + Traits::construct(alloc, std::addressof(array_ref[i]), counter); + } + assert(counter == 3); + + std::ranges::destroy_at(array); + assert(counter == 0); + + Traits::deallocate(alloc, array, 1); + } + + // Pointer to a two-dimensional array. + { + using Array = Counted[3][2]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* array = Traits::allocate(alloc, 1); + Array& array_ref = *array; + for (int i = 0; i != 3; ++i) { + for (int j = 0; j != 2; ++j) { + Traits::construct(alloc, std::addressof(array_ref[i][j]), counter); + } + } + assert(counter == 3 * 2); + + std::ranges::destroy_at(array); + assert(counter == 0); + + Traits::deallocate(alloc, array, 1); + } + + return true; +} + +int main(int, char**) { + test(); + test_arrays(); + + static_assert(test()); + // TODO: Until std::construct_at has support for arrays, it's impossible to test this + // in a constexpr context (see https://reviews.llvm.org/D114903). + // static_assert(test_arrays()); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges + +// +// +// namespace ranges { +// template +// requires destructible> +// constexpr InputIterator destroy_n(InputIterator first, iter_difference_t n) noexcept; // since C++20 +// } + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +// TODO(varconst): consolidate the ADL checks into a single file. +// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, +// implementations are allowed to use a different mechanism to achieve this effect, so this check is +// libc++-specific. +LIBCPP_STATIC_ASSERT(std::is_class_v); + +struct NotNothrowDtrable { + ~NotNothrowDtrable() noexcept(false) {} +}; +static_assert(!std::is_invocable_v); + +struct Counted { + int& count; + + constexpr Counted(int& count_ref) : count(count_ref) { ++count; } + constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; } + constexpr ~Counted() { --count; } + + friend void operator&(Counted) = delete; +}; + +template +constexpr void test() { + { + constexpr int N = 5; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Counted* out = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Traits::construct(alloc, out + i, counter); + } + assert(counter == N); + + std::ranges::destroy_n(Iterator(out), N); + assert(counter == 0); + + Traits::deallocate(alloc, out, N); + } +} + +constexpr bool tests() { + test(); + test>(); + + return true; +} + +constexpr bool test_arrays() { + // One-dimensional array. + { + constexpr int N = 5; + constexpr int M = 3; + + using Array = Counted[M]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* buffer = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Array& array_ref = *(buffer + i); + for (int j = 0; j != M; ++j) { + Traits::construct(alloc, std::addressof(array_ref[j]), counter); + } + } + assert(counter == N * M); + + std::ranges::destroy_n(buffer, N); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, N); + } + + // Multidimensional array. + { + constexpr int N = 5; + constexpr int A = 3; + constexpr int B = 3; + + using Array = Counted[A][B]; + std::allocator alloc; + using Traits = std::allocator_traits; + int counter = 0; + + Array* buffer = Traits::allocate(alloc, N); + for (int i = 0; i != N; ++i) { + Array& array_ref = *(buffer + i); + for (int j = 0; j != A; ++j) { + for (int k = 0; k != B; ++k) { + Traits::construct(alloc, std::addressof(array_ref[j][k]), counter); + } + } + } + assert(counter == N * A * B); + + std::ranges::destroy_n(buffer, N); + assert(counter == 0); + + Traits::deallocate(alloc, buffer, N); + } + + return true; +} + +int main(int, char**) { + tests(); + test_arrays(); + + static_assert(tests()); + // TODO: Until std::construct_at has support for arrays, it's impossible to test this + // in a constexpr context (see https://reviews.llvm.org/D114903). + // static_assert(test_arrays()); + + return 0; +}