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// shared_ptr
14
15// template<class T, class A>
16// shared_ptr<T> allocate_shared(const A& a); // T is U[N]
17//
18// template<class T, class A>
19// shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& u); // T is U[N]
20
21#include <cassert>
22#include <concepts>
23#include <cstdint> // std::uintptr_t
24#include <memory>
25#include <utility>
26
27#include "min_allocator.h"
28#include "operator_hijacker.h"
29#include "types.h"
30
31template <class T, class ...Args>
32concept CanAllocateShared = requires(Args&& ...args) {
33 { std::allocate_shared<T>(std::forward<Args>(args)...) } -> std::same_as<std::shared_ptr<T>>;
34};
35
36int main(int, char**) {
37 // Make sure we initialize elements correctly
38 {
39 // Without passing an initial value
40 {
41 using Array = int[8];
42 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
43 for (unsigned i = 0; i < 8; ++i) {
44 assert(ptr[i] == 0);
45 }
46 }
47 {
48 using Array = int[8][3];
49 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
50 for (unsigned i = 0; i < 8; ++i) {
51 assert(ptr[i][0] == 0);
52 assert(ptr[i][1] == 0);
53 assert(ptr[i][2] == 0);
54 }
55 }
56 {
57 using Array = int[8][3][2];
58 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
59 for (unsigned i = 0; i < 8; ++i) {
60 assert(ptr[i][0][0] == 0);
61 assert(ptr[i][0][1] == 0);
62 assert(ptr[i][1][0] == 0);
63 assert(ptr[i][1][1] == 0);
64 assert(ptr[i][2][0] == 0);
65 assert(ptr[i][2][1] == 0);
66 }
67 }
68
69 // Passing an initial value
70 {
71 using Array = int[8];
72 int init = 42;
73 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
74 for (unsigned i = 0; i < 8; ++i) {
75 assert(ptr[i] == init);
76 }
77 }
78 {
79 using Array = int[8][3];
80 int init[3] = {42, 43, 44};
81 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
82 for (unsigned i = 0; i < 8; ++i) {
83 assert(ptr[i][0] == 42);
84 assert(ptr[i][1] == 43);
85 assert(ptr[i][2] == 44);
86 }
87 }
88 {
89 using Array = int[8][3][2];
90 int init[3][2] = {{31, 32}, {41, 42}, {51, 52}};
91 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
92 for (unsigned i = 0; i < 8; ++i) {
93 assert(ptr[i][0][0] == 31);
94 assert(ptr[i][0][1] == 32);
95 assert(ptr[i][1][0] == 41);
96 assert(ptr[i][1][1] == 42);
97 assert(ptr[i][2][0] == 51);
98 assert(ptr[i][2][1] == 52);
99 }
100 }
101 }
102
103 // Make sure array elements are destroyed in reverse order
104 {
105 // Without passing an initial value
106 {
107 using Array = DestroyInReverseOrder[8];
108 DestroyInReverseOrder::reset();
109 {
110 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
111 assert(DestroyInReverseOrder::alive() == 8);
112 }
113 assert(DestroyInReverseOrder::alive() == 0);
114 }
115 {
116 using Array = DestroyInReverseOrder[8][3];
117 DestroyInReverseOrder::reset();
118 {
119 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
120 assert(DestroyInReverseOrder::alive() == 8 * 3);
121 }
122 assert(DestroyInReverseOrder::alive() == 0);
123 }
124 {
125 using Array = DestroyInReverseOrder[8][3][2];
126 DestroyInReverseOrder::reset();
127 {
128 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
129 assert(DestroyInReverseOrder::alive() == 8 * 3 * 2);
130 }
131 assert(DestroyInReverseOrder::alive() == 0);
132 }
133
134 // Passing an initial value
135 {
136 using Array = DestroyInReverseOrder[8];
137 int count = 0;
138 DestroyInReverseOrder init(&count);
139 int init_count = 1;
140 {
141 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
142 assert(count == 8 + init_count);
143 }
144 assert(count == init_count);
145 }
146 {
147 using Array = DestroyInReverseOrder[8][3];
148 int count = 0;
149 DestroyInReverseOrder init[3] = {&count, &count, &count};
150 int init_count = 3;
151 {
152 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
153 assert(count == 8 * 3 + init_count);
154 }
155 assert(count == init_count);
156 }
157 {
158 using Array = DestroyInReverseOrder[8][3][2];
159 int count = 0;
160 DestroyInReverseOrder init[3][2] = {{&count, &count}, {&count, &count}, {&count, &count}};
161 int init_count = 3 * 2;
162 {
163 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
164 assert(count == 8 * 3 * 2 + init_count);
165 }
166 assert(count == init_count);
167 }
168 }
169
170 // Count the number of copies being made
171 {
172 // Without passing an initial value
173 {
174 using Array = CountCopies[8];
175 CountCopies::reset();
176 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
177 assert(CountCopies::copies() == 0);
178 }
179 {
180 using Array = CountCopies[8][3];
181 CountCopies::reset();
182 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());;
183 assert(CountCopies::copies() == 0);
184 }
185 {
186 using Array = CountCopies[8][3][2];
187 CountCopies::reset();
188 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());;
189 assert(CountCopies::copies() == 0);
190 }
191
192 // Passing an initial value
193 {
194 using Array = CountCopies[8];
195 int copies = 0;
196 CountCopies init(&copies);
197 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
198 assert(copies == 8);
199 }
200 {
201 using Array = CountCopies[8][3];
202 int copies = 0;
203 CountCopies init[3] = {&copies, &copies, &copies};
204 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
205 assert(copies == 8 * 3);
206 }
207 {
208 using Array = CountCopies[8][3][2];
209 int copies = 0;
210 CountCopies init[3][2] = {{&copies, &copies}, {&copies, &copies}, {&copies, &copies}};
211 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
212 assert(copies == 8 * 3 * 2);
213 }
214 }
215
216 // Make sure array elements are aligned properly when the array contains an overaligned type.
217 //
218 // Here, we don't need to test both the with-initial-value and without-initial-value code paths,
219 // since we're just checking the alignment and both are going to use the same code path unless
220 // the implementation is completely crazy.
221 {
222 auto check_alignment = []<class T> {
223 {
224 using Array = T[8];
225 std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>());
226 for (int i = 0; i < 8; ++i) {
227 T* p = std::addressof(ptr[i]);
228 assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
229 }
230 }
231 {
232 using Array = T[8][3];
233 std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>());
234 for (int i = 0; i < 8; ++i) {
235 for (int j = 0; j < 3; ++j) {
236 T* p = std::addressof(ptr[i][j]);
237 assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
238 }
239 }
240 }
241 {
242 using Array = T[8][3][2];
243 std::shared_ptr ptr = std::allocate_shared<Array>(std::allocator<Array>());
244 for (int i = 0; i < 8; ++i) {
245 for (int j = 0; j < 3; ++j) {
246 for (int k = 0; k < 2; ++k) {
247 T* p = std::addressof(ptr[i][j][k]);
248 assert(reinterpret_cast<std::uintptr_t>(p) % alignof(T) == 0);
249 }
250 }
251 }
252 }
253 };
254
255 struct Empty { };
256 check_alignment.operator()<Empty>();
257 check_alignment.operator()<OverAligned>();
258 check_alignment.operator()<MaxAligned>();
259
260 // test non corner cases as well while we're at it
261 struct Foo { int i; char c; };
262 check_alignment.operator()<int>();
263 check_alignment.operator()<Foo>();
264 }
265
266 // Make sure that we destroy all the elements constructed so far when an exception
267 // is thrown. Also make sure that we do it in reverse order of construction.
268#ifndef TEST_HAS_NO_EXCEPTIONS
269 {
270 struct Sentinel : ThrowOnConstruction, DestroyInReverseOrder { };
271
272 // Without passing an initial value
273 {
274 using Array = Sentinel[8];
275 for (int i = 0; i < 8; ++i) {
276 ThrowOnConstruction::throw_after(n: i);
277 DestroyInReverseOrder::reset();
278 try {
279 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
280 assert(false);
281 } catch (ThrowOnConstruction::exception const&) {
282 assert(DestroyInReverseOrder::alive() == 0);
283 }
284 }
285 }
286 {
287 using Array = Sentinel[8][3];
288 for (int i = 0; i < 8 * 3; ++i) {
289 ThrowOnConstruction::throw_after(n: i);
290 DestroyInReverseOrder::reset();
291 try {
292 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
293 assert(false);
294 } catch (ThrowOnConstruction::exception const&) {
295 assert(DestroyInReverseOrder::alive() == 0);
296 }
297 }
298 }
299 {
300 using Array = Sentinel[8][3][2];
301 for (int i = 0; i < 8 * 3 * 2; ++i) {
302 ThrowOnConstruction::throw_after(n: i);
303 DestroyInReverseOrder::reset();
304 try {
305 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
306 assert(false);
307 } catch (ThrowOnConstruction::exception const&) {
308 assert(DestroyInReverseOrder::alive() == 0);
309 }
310 }
311 }
312
313 // Passing an initial value
314 {
315 using Array = Sentinel[8];
316 for (int i = 0; i < 8; ++i) {
317 DestroyInReverseOrder::reset();
318 ThrowOnConstruction::reset();
319 Sentinel init;
320 ThrowOnConstruction::throw_after(n: i);
321 try {
322 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
323 assert(false);
324 } catch (ThrowOnConstruction::exception const&) {
325 assert(DestroyInReverseOrder::alive() == 1);
326 }
327 }
328 }
329 {
330 using Array = Sentinel[8][3];
331 for (int i = 0; i < 8 * 3; ++i) {
332 DestroyInReverseOrder::reset();
333 ThrowOnConstruction::reset();
334 Sentinel init[3] = {};
335 ThrowOnConstruction::throw_after(n: i);
336 try {
337 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
338 assert(false);
339 } catch (ThrowOnConstruction::exception const&) {
340 assert(DestroyInReverseOrder::alive() == 3);
341 }
342 }
343 }
344 {
345 using Array = Sentinel[8][3][2];
346 for (int i = 0; i < 8 * 3 * 2; ++i) {
347 DestroyInReverseOrder::reset();
348 ThrowOnConstruction::reset();
349 Sentinel init[3][2] = {};
350 ThrowOnConstruction::throw_after(n: i);
351 try {
352 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>(), args&: init);
353 assert(false);
354 } catch (ThrowOnConstruction::exception const&) {
355 assert(DestroyInReverseOrder::alive() == 3 * 2);
356 }
357 }
358 }
359 }
360#endif // TEST_HAS_NO_EXCEPTIONS
361
362 // Test with another allocator that's not std::allocator
363 {
364 // Without passing an initial value
365 {
366 using Array = int[8][3];
367 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(min_allocator<Array>());
368 for (unsigned i = 0; i < 8; ++i) {
369 assert(ptr[i][0] == 0);
370 assert(ptr[i][1] == 0);
371 assert(ptr[i][2] == 0);
372 }
373 }
374
375 // Passing an initial value
376 {
377 using Array = int[8][3];
378 int init[3] = {42, 43, 44};
379 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(min_allocator<Array>(), init);
380 for (unsigned i = 0; i < 8; ++i) {
381 assert(ptr[i][0] == 42);
382 assert(ptr[i][1] == 43);
383 assert(ptr[i][2] == 44);
384 }
385 }
386 }
387
388 // Make sure the version without an initialization argument works even for non-movable types
389 {
390 using Array = NonMovable[8][3];
391 std::shared_ptr<Array> ptr = std::allocate_shared<Array>(a: std::allocator<Array>());
392 (void)ptr;
393 }
394
395 // Make sure std::allocate_shared handles badly-behaved types properly
396 {
397 using Array = operator_hijacker[3];
398 std::shared_ptr<Array> p1 = std::allocate_shared<Array>(std::allocator<Array>());
399 std::shared_ptr<Array> p2 = std::allocate_shared<Array>(std::allocator<Array>(), operator_hijacker());
400 assert(p1 != nullptr);
401 assert(p2 != nullptr);
402 }
403
404 // Check that we SFINAE-away for invalid arguments
405 {
406 struct T { };
407 static_assert( CanAllocateShared<T[8], std::allocator<T[8]>>);
408 static_assert( CanAllocateShared<T[8], std::allocator<T[8]>, T>);
409 static_assert(!CanAllocateShared<T[8], std::allocator<T[8]>, T, int>); // too many arguments
410 static_assert(!CanAllocateShared<T[8], std::allocator<T[8]>, int>); // T is not constructible from int
411 }
412
413 return 0;
414}
415

source code of libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared.array.bounded.pass.cpp