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 | |
37 | template <class T, class ...Args> |
38 | concept CanAllocateShared = requires(Args&& ...args) { |
39 | { std::allocate_shared<T>(std::forward<Args>(args)...) } -> std::same_as<std::shared_ptr<T>>; |
40 | }; |
41 | |
42 | int 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 | |