//===----------------------------------------------------------------------===//
//
// 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
// <memory>
//
// namespace ranges {
// template<nothrow-input-iterator InputIterator, nothrow-sentinel-for<InputIterator> Sentinel>
// requires destructible<iter_value_t<InputIterator>>
// constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20
// template<nothrow-input-range InputRange>
// requires destructible<range_value_t<InputRange>>
// constexpr borrowed_iterator_t<InputRange> destroy(InputRange&& range) noexcept; // since C++20
// }
#include <cassert>
#include <memory>
#include <ranges>
#include <type_traits>
#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<decltype(std::ranges::destroy)>);
struct NotNothrowDtrable {
~NotNothrowDtrable() noexcept(false) {}
};
static_assert(!std::is_invocable_v<decltype(std::ranges::destroy), NotNothrowDtrable*, NotNothrowDtrable*>);
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 <class Iterator>
constexpr void test() {
// (iterator + sentinel) overload.
{
constexpr int N = 5;
std::allocator<Counted> alloc;
using Traits = std::allocator_traits<decltype(alloc)>;
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<Counted> alloc;
using Traits = std::allocator_traits<decltype(alloc)>;
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<Counted*>();
test<forward_iterator<Counted*>>();
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<Array> alloc;
using Traits = std::allocator_traits<decltype(alloc)>;
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<Array> alloc;
using Traits = std::allocator_traits<decltype(alloc)>;
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<Array> alloc;
using Traits = std::allocator_traits<decltype(alloc)>;
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<Array> alloc;
using Traits = std::allocator_traits<decltype(alloc)>;
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;
}