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