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