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