1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10
11 // <memory>
12 //
13 // namespace ranges {
14 // template<nothrow-input-iterator InputIterator>
15 // requires destructible<iter_value_t<InputIterator>>
16 // constexpr InputIterator destroy_n(InputIterator first, iter_difference_t<InputIterator> n) noexcept; // since C++20
17 // }
18
19 #include <cassert>
20 #include <memory>
21 #include <ranges>
22 #include <type_traits>
23
24 #include "test_iterators.h"
25 #include "test_macros.h"
26
27 // TODO(varconst): consolidate the ADL checks into a single file.
28 // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
29 // implementations are allowed to use a different mechanism to achieve this effect, so this check is
30 // libc++-specific.
31 LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy_n)>);
32
33 struct NotNothrowDtrable {
~NotNothrowDtrableNotNothrowDtrable34 ~NotNothrowDtrable() noexcept(false) {}
35 };
36 static_assert(!std::is_invocable_v<decltype(std::ranges::destroy_n), NotNothrowDtrable*, int>);
37
38 struct Counted {
39 int& count;
40
CountedCounted41 constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
CountedCounted42 constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
~CountedCounted43 constexpr ~Counted() { --count; }
44
45 friend void operator&(Counted) = delete;
46 };
47
48 template <class Iterator>
test()49 constexpr void test() {
50 {
51 constexpr int N = 5;
52 std::allocator<Counted> alloc;
53 using Traits = std::allocator_traits<decltype(alloc)>;
54 int counter = 0;
55
56 Counted* out = Traits::allocate(alloc, N);
57 for (int i = 0; i != N; ++i) {
58 Traits::construct(alloc, out + i, counter);
59 }
60 assert(counter == N);
61
62 std::ranges::destroy_n(Iterator(out), N);
63 assert(counter == 0);
64
65 Traits::deallocate(alloc, out, N);
66 }
67 }
68
tests()69 constexpr bool tests() {
70 test<Counted*>();
71 test<forward_iterator<Counted*>>();
72
73 return true;
74 }
75
test_arrays()76 constexpr bool test_arrays() {
77 // One-dimensional array.
78 {
79 constexpr int N = 5;
80 constexpr int M = 3;
81
82 using Array = Counted[M];
83 std::allocator<Array> alloc;
84 using Traits = std::allocator_traits<decltype(alloc)>;
85 int counter = 0;
86
87 Array* buffer = Traits::allocate(alloc, N);
88 for (int i = 0; i != N; ++i) {
89 Array& array_ref = *(buffer + i);
90 for (int j = 0; j != M; ++j) {
91 Traits::construct(alloc, std::addressof(array_ref[j]), counter);
92 }
93 }
94 assert(counter == N * M);
95
96 std::ranges::destroy_n(buffer, N);
97 assert(counter == 0);
98
99 Traits::deallocate(alloc, buffer, N);
100 }
101
102 // Multidimensional array.
103 {
104 constexpr int N = 5;
105 constexpr int A = 3;
106 constexpr int B = 3;
107
108 using Array = Counted[A][B];
109 std::allocator<Array> alloc;
110 using Traits = std::allocator_traits<decltype(alloc)>;
111 int counter = 0;
112
113 Array* buffer = Traits::allocate(alloc, N);
114 for (int i = 0; i != N; ++i) {
115 Array& array_ref = *(buffer + i);
116 for (int j = 0; j != A; ++j) {
117 for (int k = 0; k != B; ++k) {
118 Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
119 }
120 }
121 }
122 assert(counter == N * A * B);
123
124 std::ranges::destroy_n(buffer, N);
125 assert(counter == 0);
126
127 Traits::deallocate(alloc, buffer, N);
128 }
129
130 return true;
131 }
132
main(int,char **)133 int main(int, char**) {
134 tests();
135 test_arrays();
136
137 static_assert(tests());
138 // TODO: Until std::construct_at has support for arrays, it's impossible to test this
139 // in a constexpr context (see https://reviews.llvm.org/D114903).
140 // static_assert(test_arrays());
141
142 return 0;
143 }
144