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