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