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