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