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