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.
28LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy_at)>);
29
30struct NotNothrowDtrable {
31 ~NotNothrowDtrable() noexcept(false) {}
32};
33static_assert(!std::is_invocable_v<decltype(std::ranges::destroy_at), NotNothrowDtrable*>);
34
35struct Counted {
36 int& count;
37
38 constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
39 constexpr ~Counted() { --count; }
40
41 friend void operator&(Counted) = delete;
42};
43
44struct VirtualCountedBase {
45 int& count;
46
47 constexpr VirtualCountedBase(int& count_ref) : count(count_ref) { ++count; }
48 constexpr virtual ~VirtualCountedBase() { --count; }
49
50 void operator&() const = delete;
51};
52
53struct VirtualCountedDerived : VirtualCountedBase {
54 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).
58 constexpr ~VirtualCountedDerived() override {}
59};
60
61constexpr 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(a&: alloc, n: 2);
69 Traits::construct(a&: alloc, p: buffer, args&: counter);
70 Traits::construct(a&: alloc, p: buffer + 1, args&: 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(a&: alloc, p: buffer, n: 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(a&: alloc, n: 2);
88 Traits::construct(a&: alloc, p: buffer, args&: counter);
89 Traits::construct(a&: alloc, p: buffer + 1, args&: 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(a&: alloc, p: buffer, n: 2);
98 }
99
100 return true;
101}
102
103constexpr 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(a&: alloc, n: 1);
112 Array& array_ref = *array;
113 for (int i = 0; i != 3; ++i) {
114 Traits::construct(a&: alloc, p: std::addressof(r&: array_ref[i]), args&: counter);
115 }
116 assert(counter == 3);
117
118 std::ranges::destroy_at(array);
119 assert(counter == 0);
120
121 Traits::deallocate(a&: alloc, p: array, n: 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(a&: alloc, n: 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(a&: alloc, p: std::addressof(r&: array_ref[i][j]), args&: counter);
136 }
137 }
138 assert(counter == 3 * 2);
139
140 std::ranges::destroy_at(array);
141 assert(counter == 0);
142
143 Traits::deallocate(a&: alloc, p: array, n: 1);
144 }
145
146 return true;
147}
148
149int 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

source code of libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp