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

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