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