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<destructible T>
15 //     constexpr void destroy_at(T* location) noexcept; // since C++20
16 // }
17 
18 #include <cassert>
19 #include <memory>
20 #include <type_traits>
21 
22 #include "test_macros.h"
23 
24 // TODO(varconst): consolidate the ADL checks into a single file.
25 // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
26 // implementations are allowed to use a different mechanism to achieve this effect, so this check is
27 // libc++-specific.
28 LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy_at)>);
29 
30 struct NotNothrowDtrable {
~NotNothrowDtrableNotNothrowDtrable31   ~NotNothrowDtrable() noexcept(false) {}
32 };
33 static_assert(!std::is_invocable_v<decltype(std::ranges::destroy_at), NotNothrowDtrable*>);
34 
35 struct Counted {
36   int& count;
37 
CountedCounted38   constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
~CountedCounted39   constexpr ~Counted() { --count; }
40 
41   friend void operator&(Counted) = delete;
42 };
43 
44 struct VirtualCountedBase {
45   int& count;
46 
VirtualCountedBaseVirtualCountedBase47   constexpr VirtualCountedBase(int& count_ref) : count(count_ref) { ++count; }
~VirtualCountedBaseVirtualCountedBase48   constexpr virtual ~VirtualCountedBase() { --count; }
49 
50   void operator&() const = delete;
51 };
52 
53 struct VirtualCountedDerived : VirtualCountedBase {
VirtualCountedDerivedVirtualCountedDerived54   constexpr VirtualCountedDerived(int& count_ref) : VirtualCountedBase(count_ref) {}
55 
56   // Without a definition, GCC gives an error when the destructor is invoked in a constexpr context (see
57   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93413).
~VirtualCountedDerivedVirtualCountedDerived58   constexpr ~VirtualCountedDerived() override {}
59 };
60 
test()61 constexpr bool test() {
62   // Destroying a "trivial" object.
63   {
64     std::allocator<Counted> alloc;
65     using Traits = std::allocator_traits<decltype(alloc)>;
66     int counter = 0;
67 
68     Counted* buffer = Traits::allocate(alloc, 2);
69     Traits::construct(alloc, buffer, counter);
70     Traits::construct(alloc, buffer + 1, counter);
71     assert(counter == 2);
72 
73     std::ranges::destroy_at(buffer);
74     assert(counter == 1);
75     std::ranges::destroy_at(buffer + 1);
76     assert(counter == 0);
77 
78     Traits::deallocate(alloc, buffer, 2);
79   }
80 
81   // Destroying a derived object with a virtual destructor.
82   {
83     std::allocator<VirtualCountedDerived> alloc;
84     using Traits = std::allocator_traits<decltype(alloc)>;
85     int counter = 0;
86 
87     VirtualCountedDerived* buffer = Traits::allocate(alloc, 2);
88     Traits::construct(alloc, buffer, counter);
89     Traits::construct(alloc, buffer + 1, counter);
90     assert(counter == 2);
91 
92     std::ranges::destroy_at(buffer);
93     assert(counter == 1);
94     std::ranges::destroy_at(buffer + 1);
95     assert(counter == 0);
96 
97     Traits::deallocate(alloc, buffer, 2);
98   }
99 
100   return true;
101 }
102 
test_arrays()103 constexpr bool test_arrays() {
104   // Pointer to an array.
105   {
106     using Array = Counted[3];
107     std::allocator<Array> alloc;
108     using Traits = std::allocator_traits<decltype(alloc)>;
109     int counter = 0;
110 
111     Array* array = Traits::allocate(alloc, 1);
112     Array& array_ref = *array;
113     for (int i = 0; i != 3; ++i) {
114       Traits::construct(alloc, std::addressof(array_ref[i]), counter);
115     }
116     assert(counter == 3);
117 
118     std::ranges::destroy_at(array);
119     assert(counter == 0);
120 
121     Traits::deallocate(alloc, array, 1);
122   }
123 
124   // Pointer to a two-dimensional array.
125   {
126     using Array = Counted[3][2];
127     std::allocator<Array> alloc;
128     using Traits = std::allocator_traits<decltype(alloc)>;
129     int counter = 0;
130 
131     Array* array = Traits::allocate(alloc, 1);
132     Array& array_ref = *array;
133     for (int i = 0; i != 3; ++i) {
134       for (int j = 0; j != 2; ++j) {
135         Traits::construct(alloc, std::addressof(array_ref[i][j]), counter);
136       }
137     }
138     assert(counter == 3 * 2);
139 
140     std::ranges::destroy_at(array);
141     assert(counter == 0);
142 
143     Traits::deallocate(alloc, array, 1);
144   }
145 
146   return true;
147 }
148 
main(int,char **)149 int main(int, char**) {
150   test();
151   test_arrays();
152 
153   static_assert(test());
154   // TODO: Until std::construct_at has support for arrays, it's impossible to test this
155   //       in a constexpr context (see https://reviews.llvm.org/D114903).
156   // static_assert(test_arrays());
157 
158   return 0;
159 }
160