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 
main(int,char **)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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 0, 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 1, 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 8, 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(i);
319         DestroyInReverseOrder::reset();
320         try {
321           std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 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(i);
332         DestroyInReverseOrder::reset();
333         try {
334           std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 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(i);
345         DestroyInReverseOrder::reset();
346         try {
347           std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 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(i);
363         try {
364           std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, 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(i);
378         try {
379           std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, 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(i);
393         try {
394           std::shared_ptr<Array> ptr = std::allocate_shared<Array>(std::allocator<Array>(), 8, 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>(std::allocator<Array>(), 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