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