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