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