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