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