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 {
~NotNothrowDtrableNotNothrowDtrable37   ~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 
CountedCounted44   constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
CountedCounted45   constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
~CountedCounted46   constexpr ~Counted() { --count; }
47 
48   friend void operator&(Counted) = delete;
49 };
50 
51 template <class Iterator>
test()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(alloc, N);
61     for (int i = 0; i != N; ++i) {
62       Traits::construct(alloc, out + i, counter);
63     }
64     assert(counter == N);
65 
66     std::ranges::destroy(Iterator(out), Iterator(out + N));
67     assert(counter == 0);
68 
69     Traits::deallocate(alloc, out, 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(alloc, N);
80     for (int i = 0; i != N; ++i) {
81       Traits::construct(alloc, out + i, 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(alloc, out, N);
90   }
91 }
92 
tests()93 constexpr bool tests() {
94   test<Counted*>();
95   test<forward_iterator<Counted*>>();
96 
97   return true;
98 }
99 
test_arrays()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(alloc, 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(alloc, std::addressof(array_ref[j]), counter);
116       }
117     }
118     assert(counter == N * M);
119 
120     std::ranges::destroy(buffer, buffer + N);
121     assert(counter == 0);
122 
123     Traits::deallocate(alloc, buffer, 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(alloc, 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(alloc, std::addressof(array_ref[j]), 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(alloc, buffer, 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(alloc, 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(alloc, std::addressof(array_ref[j][k]), 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(alloc, buffer, 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(alloc, 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(alloc, std::addressof(array_ref[j][k]), 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(alloc, buffer, N);
206   }
207 
208   return true;
209 }
210 
main(int,char **)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