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 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
11
12 // <memory>
13 //
14 // namespace ranges {
15 // template<nothrow-input-iterator InputIterator, nothrow-sentinel-for<InputIterator> Sentinel>
16 // requires destructible<iter_value_t<InputIterator>>
17 // constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20
18 // template<nothrow-input-range InputRange>
19 // requires destructible<range_value_t<InputRange>>
20 // constexpr borrowed_iterator_t<InputRange> destroy(InputRange&& range) noexcept; // since C++20
21 // }
22
23 #include <cassert>
24 #include <memory>
25 #include <ranges>
26 #include <type_traits>
27
28 #include "test_iterators.h"
29 #include "test_macros.h"
30
31 // TODO(varconst): consolidate the ADL checks into a single file.
32 // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
33 // implementations are allowed to use a different mechanism to achieve this effect, so this check is
34 // libc++-specific.
35 LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy)>);
36
37 struct NotNothrowDtrable {
~NotNothrowDtrableNotNothrowDtrable38 ~NotNothrowDtrable() noexcept(false) {}
39 };
40 static_assert(!std::is_invocable_v<decltype(std::ranges::destroy), NotNothrowDtrable*, NotNothrowDtrable*>);
41
42 struct Counted {
43 int& count;
44
CountedCounted45 constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
CountedCounted46 constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
~CountedCounted47 constexpr ~Counted() { --count; }
48
49 friend void operator&(Counted) = delete;
50 };
51
52 template <class Iterator>
test()53 constexpr void test() {
54 // (iterator + sentinel) overload.
55 {
56 constexpr int N = 5;
57 std::allocator<Counted> alloc;
58 using Traits = std::allocator_traits<decltype(alloc)>;
59 int counter = 0;
60
61 Counted* out = Traits::allocate(alloc, N);
62 for (int i = 0; i != N; ++i) {
63 Traits::construct(alloc, out + i, counter);
64 }
65 assert(counter == N);
66
67 std::ranges::destroy(Iterator(out), Iterator(out + N));
68 assert(counter == 0);
69
70 Traits::deallocate(alloc, out, N);
71 }
72
73 // (range) overload.
74 {
75 constexpr int N = 5;
76 std::allocator<Counted> alloc;
77 using Traits = std::allocator_traits<decltype(alloc)>;
78 int counter = 0;
79
80 Counted* out = Traits::allocate(alloc, N);
81 for (int i = 0; i != N; ++i) {
82 Traits::construct(alloc, out + i, counter);
83 }
84 assert(counter == N);
85
86 auto range = std::ranges::subrange(Iterator(out), Iterator(out + N));
87 std::ranges::destroy(range);
88 assert(counter == 0);
89
90 Traits::deallocate(alloc, out, N);
91 }
92 }
93
tests()94 constexpr bool tests() {
95 test<Counted*>();
96 test<forward_iterator<Counted*>>();
97
98 return true;
99 }
100
test_arrays()101 constexpr bool test_arrays() {
102 // One-dimensional array, (iterator + sentinel) overload.
103 {
104 constexpr int N = 5;
105 constexpr int M = 3;
106
107 using Array = Counted[M];
108 std::allocator<Array> alloc;
109 using Traits = std::allocator_traits<decltype(alloc)>;
110 int counter = 0;
111
112 Array* buffer = Traits::allocate(alloc, N);
113 for (int i = 0; i != N; ++i) {
114 Array& array_ref = *(buffer + i);
115 for (int j = 0; j != M; ++j) {
116 Traits::construct(alloc, std::addressof(array_ref[j]), counter);
117 }
118 }
119 assert(counter == N * M);
120
121 std::ranges::destroy(buffer, buffer + N);
122 assert(counter == 0);
123
124 Traits::deallocate(alloc, buffer, N);
125 }
126
127 // One-dimensional array, (range) overload.
128 {
129 constexpr int N = 5;
130 constexpr int A = 3;
131
132 using Array = Counted[A];
133 std::allocator<Array> alloc;
134 using Traits = std::allocator_traits<decltype(alloc)>;
135 int counter = 0;
136
137 Array* buffer = Traits::allocate(alloc, N);
138 for (int i = 0; i != N; ++i) {
139 Array& array_ref = *(buffer + i);
140 for (int j = 0; j != A; ++j) {
141 Traits::construct(alloc, std::addressof(array_ref[j]), counter);
142 }
143 }
144 assert(counter == N * A);
145
146 auto range = std::ranges::subrange(buffer, buffer + N);
147 std::ranges::destroy(range);
148 assert(counter == 0);
149
150 Traits::deallocate(alloc, buffer, N);
151 }
152
153 // Multidimensional array, (iterator + sentinel ) overload.
154 {
155 constexpr int N = 5;
156 constexpr int A = 3;
157 constexpr int B = 3;
158
159 using Array = Counted[A][B];
160 std::allocator<Array> alloc;
161 using Traits = std::allocator_traits<decltype(alloc)>;
162 int counter = 0;
163
164 Array* buffer = Traits::allocate(alloc, N);
165 for (int i = 0; i != N; ++i) {
166 Array& array_ref = *(buffer + i);
167 for (int j = 0; j != A; ++j) {
168 for (int k = 0; k != B; ++k) {
169 Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
170 }
171 }
172 }
173 assert(counter == N * A * B);
174
175 std::ranges::destroy(buffer, buffer + N);
176 assert(counter == 0);
177
178 Traits::deallocate(alloc, buffer, N);
179 }
180
181 // Multidimensional array, (range) overload.
182 {
183 constexpr int N = 5;
184 constexpr int A = 3;
185 constexpr int B = 3;
186
187 using Array = Counted[A][B];
188 std::allocator<Array> alloc;
189 using Traits = std::allocator_traits<decltype(alloc)>;
190 int counter = 0;
191
192 Array* buffer = Traits::allocate(alloc, N);
193 for (int i = 0; i != N; ++i) {
194 Array& array_ref = *(buffer + i);
195 for (int j = 0; j != A; ++j) {
196 for (int k = 0; k != B; ++k) {
197 Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
198 }
199 }
200 }
201 assert(counter == N * A * B);
202
203 std::ranges::destroy(buffer, buffer + N);
204 assert(counter == 0);
205
206 Traits::deallocate(alloc, buffer, N);
207 }
208
209 return true;
210 }
211
main(int,char **)212 int main(int, char**) {
213 tests();
214 test_arrays();
215
216 static_assert(tests());
217 // TODO: Until std::construct_at has support for arrays, it's impossible to test this
218 // in a constexpr context (see https://reviews.llvm.org/D114903).
219 // static_assert(test_arrays());
220
221 return 0;
222 }
223