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