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 | |
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>(args: 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>(args: 0, args: 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>(args: 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>(args: 1, args: 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>(args: 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>(args: 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>(args: 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>(args: 8, args&: 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>(args: 8, args&: 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>(args: 8, args&: 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>(args: 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>(args: 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>(args: 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>(args: 8, args&: 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>(args: 8, args&: 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>(args: 8, args&: 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>(args: 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>(args: 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>(args: 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>(args: 8, args&: 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>(args: 8, args&: 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>(args: 8, args&: 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(n: i); |
317 | DestroyInReverseOrder::reset(); |
318 | try { |
319 | std::shared_ptr<Array> ptr = std::make_shared<Array>(args: 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(n: i); |
330 | DestroyInReverseOrder::reset(); |
331 | try { |
332 | std::shared_ptr<Array> ptr = std::make_shared<Array>(args: 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(n: i); |
343 | DestroyInReverseOrder::reset(); |
344 | try { |
345 | std::shared_ptr<Array> ptr = std::make_shared<Array>(args: 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(n: i); |
361 | try { |
362 | std::shared_ptr<Array> ptr = std::make_shared<Array>(args: 8, args&: 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(n: i); |
376 | try { |
377 | std::shared_ptr<Array> ptr = std::make_shared<Array>(args: 8, args&: 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(n: i); |
391 | try { |
392 | std::shared_ptr<Array> ptr = std::make_shared<Array>(args: 8, args&: 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>(args: 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 | |