Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libcxx / test / support / test_allocator.h
blob3bde73183ab6e39e2781af0dfb323d248c39a4f1
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 //===----------------------------------------------------------------------===//
9 #ifndef TEST_ALLOCATOR_H
10 #define TEST_ALLOCATOR_H
12 #include <type_traits>
13 #include <new>
14 #include <memory>
15 #include <utility>
16 #include <cstddef>
17 #include <cstdlib>
18 #include <climits>
19 #include <cassert>
21 #include "test_macros.h"
23 template <class Alloc>
24 TEST_CONSTEXPR_CXX20 inline typename std::allocator_traits<Alloc>::size_type alloc_max_size(Alloc const& a) {
25 typedef std::allocator_traits<Alloc> AT;
26 return AT::max_size(a);
29 struct test_allocator_statistics {
30 int time_to_throw = 0;
31 int throw_after = INT_MAX;
32 int count = 0;
33 int alloc_count = 0;
34 int construct_count = 0; // the number of times that ::construct was called
35 int destroy_count = 0; // the number of times that ::destroy was called
36 int copied = 0;
37 int moved = 0;
38 int converted = 0;
40 TEST_CONSTEXPR_CXX14 void clear() {
41 assert(count == 0 && "clearing leaking allocator data?");
42 count = 0;
43 time_to_throw = 0;
44 alloc_count = 0;
45 construct_count = 0;
46 destroy_count = 0;
47 throw_after = INT_MAX;
48 clear_ctor_counters();
51 TEST_CONSTEXPR_CXX14 void clear_ctor_counters() {
52 copied = 0;
53 moved = 0;
54 converted = 0;
58 struct test_alloc_base {
59 TEST_CONSTEXPR static const int destructed_value = -1;
60 TEST_CONSTEXPR static const int moved_value = INT_MAX;
63 template <class T>
64 class test_allocator {
65 int data_ = 0; // participates in equality
66 int id_ = 0; // unique identifier, doesn't participate in equality
67 test_allocator_statistics* stats_ = nullptr;
69 template <class U>
70 friend class test_allocator;
72 public:
73 typedef unsigned size_type;
74 typedef int difference_type;
75 typedef T value_type;
76 typedef value_type* pointer;
77 typedef const value_type* const_pointer;
78 typedef typename std::add_lvalue_reference<value_type>::type reference;
79 typedef typename std::add_lvalue_reference<const value_type>::type const_reference;
81 template <class U>
82 struct rebind {
83 typedef test_allocator<U> other;
86 TEST_CONSTEXPR test_allocator() TEST_NOEXCEPT = default;
88 TEST_CONSTEXPR_CXX14 explicit test_allocator(test_allocator_statistics* stats) TEST_NOEXCEPT : stats_(stats) {
89 if (stats_ != nullptr)
90 ++stats_->count;
93 TEST_CONSTEXPR explicit test_allocator(int data) TEST_NOEXCEPT : data_(data) {}
95 TEST_CONSTEXPR_CXX14 explicit test_allocator(int data, test_allocator_statistics* stats) TEST_NOEXCEPT
96 : data_(data), stats_(stats) {
97 if (stats != nullptr)
98 ++stats_->count;
101 TEST_CONSTEXPR explicit test_allocator(int data, int id) TEST_NOEXCEPT : data_(data), id_(id) {}
103 TEST_CONSTEXPR_CXX14 explicit test_allocator(int data, int id, test_allocator_statistics* stats) TEST_NOEXCEPT
104 : data_(data), id_(id), stats_(stats) {
105 if (stats_ != nullptr)
106 ++stats_->count;
109 TEST_CONSTEXPR_CXX14 test_allocator(const test_allocator& a) TEST_NOEXCEPT
110 : data_(a.data_), id_(a.id_), stats_(a.stats_) {
111 assert(a.data_ != test_alloc_base::destructed_value && a.id_ != test_alloc_base::destructed_value &&
112 "copying from destroyed allocator");
113 if (stats_ != nullptr) {
114 ++stats_->count;
115 ++stats_->copied;
119 TEST_CONSTEXPR_CXX14 test_allocator(test_allocator&& a) TEST_NOEXCEPT : data_(a.data_), id_(a.id_), stats_(a.stats_) {
120 if (stats_ != nullptr) {
121 ++stats_->count;
122 ++stats_->moved;
124 assert(a.data_ != test_alloc_base::destructed_value && a.id_ != test_alloc_base::destructed_value &&
125 "moving from destroyed allocator");
126 a.data_ = test_alloc_base::moved_value;
127 a.id_ = test_alloc_base::moved_value;
130 template <class U>
131 TEST_CONSTEXPR_CXX14 test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT
132 : data_(a.data_), id_(a.id_), stats_(a.stats_) {
133 if (stats_ != nullptr) {
134 ++stats_->count;
135 ++stats_->converted;
139 TEST_CONSTEXPR_CXX20 ~test_allocator() TEST_NOEXCEPT {
140 assert(data_ != test_alloc_base::destructed_value);
141 assert(id_ != test_alloc_base::destructed_value);
142 if (stats_ != nullptr)
143 --stats_->count;
144 data_ = test_alloc_base::destructed_value;
145 id_ = test_alloc_base::destructed_value;
148 TEST_CONSTEXPR pointer address(reference x) const { return &x; }
149 TEST_CONSTEXPR const_pointer address(const_reference x) const { return &x; }
151 TEST_CONSTEXPR_CXX14 pointer allocate(size_type n, const void* = nullptr) {
152 assert(data_ != test_alloc_base::destructed_value);
153 if (stats_ != nullptr) {
154 if (stats_->time_to_throw >= stats_->throw_after)
155 TEST_THROW(std::bad_alloc());
156 ++stats_->time_to_throw;
157 ++stats_->alloc_count;
159 return std::allocator<value_type>().allocate(n);
162 TEST_CONSTEXPR_CXX14 void deallocate(pointer p, size_type s) {
163 assert(data_ != test_alloc_base::destructed_value);
164 if (stats_ != nullptr)
165 --stats_->alloc_count;
166 std::allocator<value_type>().deallocate(p, s);
169 TEST_CONSTEXPR size_type max_size() const TEST_NOEXCEPT { return UINT_MAX / sizeof(T); }
171 template <class U>
172 TEST_CONSTEXPR_CXX20 void construct(pointer p, U&& val) {
173 if (stats_ != nullptr)
174 ++stats_->construct_count;
175 #if TEST_STD_VER > 17
176 std::construct_at(std::to_address(p), std::forward<U>(val));
177 #else
178 ::new (static_cast<void*>(p)) T(std::forward<U>(val));
179 #endif
182 TEST_CONSTEXPR_CXX14 void destroy(pointer p) {
183 if (stats_ != nullptr)
184 ++stats_->destroy_count;
185 p->~T();
187 TEST_CONSTEXPR friend bool operator==(const test_allocator& x, const test_allocator& y) { return x.data_ == y.data_; }
188 TEST_CONSTEXPR friend bool operator!=(const test_allocator& x, const test_allocator& y) { return !(x == y); }
190 TEST_CONSTEXPR int get_data() const { return data_; }
191 TEST_CONSTEXPR int get_id() const { return id_; }
194 template <>
195 class test_allocator<void> {
196 int data_ = 0;
197 int id_ = 0;
198 test_allocator_statistics* stats_ = nullptr;
200 template <class U>
201 friend class test_allocator;
203 public:
204 typedef unsigned size_type;
205 typedef int difference_type;
206 typedef void value_type;
207 typedef value_type* pointer;
208 typedef const value_type* const_pointer;
210 template <class U>
211 struct rebind {
212 typedef test_allocator<U> other;
215 TEST_CONSTEXPR test_allocator() TEST_NOEXCEPT = default;
217 TEST_CONSTEXPR_CXX14 explicit test_allocator(test_allocator_statistics* stats) TEST_NOEXCEPT : stats_(stats) {}
219 TEST_CONSTEXPR explicit test_allocator(int data) TEST_NOEXCEPT : data_(data) {}
221 TEST_CONSTEXPR explicit test_allocator(int data, test_allocator_statistics* stats) TEST_NOEXCEPT
222 : data_(data), stats_(stats)
225 TEST_CONSTEXPR explicit test_allocator(int data, int id) : data_(data), id_(id) {}
227 TEST_CONSTEXPR_CXX14 explicit test_allocator(int data, int id, test_allocator_statistics* stats) TEST_NOEXCEPT
228 : data_(data), id_(id), stats_(stats)
231 TEST_CONSTEXPR_CXX14 explicit test_allocator(const test_allocator& a) TEST_NOEXCEPT
232 : data_(a.data_), id_(a.id_), stats_(a.stats_)
235 template <class U>
236 TEST_CONSTEXPR_CXX14 test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT
237 : data_(a.data_), id_(a.id_), stats_(a.stats_)
240 TEST_CONSTEXPR_CXX20 ~test_allocator() TEST_NOEXCEPT {
241 data_ = test_alloc_base::destructed_value;
242 id_ = test_alloc_base::destructed_value;
245 TEST_CONSTEXPR int get_id() const { return id_; }
246 TEST_CONSTEXPR int get_data() const { return data_; }
248 TEST_CONSTEXPR friend bool operator==(const test_allocator& x, const test_allocator& y) { return x.data_ == y.data_; }
249 TEST_CONSTEXPR friend bool operator!=(const test_allocator& x, const test_allocator& y) { return !(x == y); }
252 template <class T>
253 class other_allocator {
254 int data_ = -1;
256 template <class U>
257 friend class other_allocator;
259 public:
260 typedef T value_type;
262 TEST_CONSTEXPR_CXX14 other_allocator() {}
263 TEST_CONSTEXPR_CXX14 explicit other_allocator(int i) : data_(i) {}
265 template <class U>
266 TEST_CONSTEXPR_CXX14 other_allocator(const other_allocator<U>& a) : data_(a.data_) {}
268 TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<value_type>().allocate(n); }
269 TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t s) { std::allocator<value_type>().deallocate(p, s); }
271 TEST_CONSTEXPR_CXX14 other_allocator select_on_container_copy_construction() const { return other_allocator(-2); }
273 TEST_CONSTEXPR_CXX14 friend bool operator==(const other_allocator& x, const other_allocator& y) {
274 return x.data_ == y.data_;
277 TEST_CONSTEXPR_CXX14 friend bool operator!=(const other_allocator& x, const other_allocator& y) { return !(x == y); }
279 typedef std::true_type propagate_on_container_copy_assignment;
280 typedef std::true_type propagate_on_container_move_assignment;
281 typedef std::true_type propagate_on_container_swap;
283 #if TEST_STD_VER < 11
284 std::size_t max_size() const { return UINT_MAX / sizeof(T); }
285 #endif
288 struct Ctor_Tag {};
290 template <typename T>
291 class TaggingAllocator;
293 struct Tag_X {
294 // All constructors must be passed the Tag type.
296 // DefaultInsertable into vector<X, TaggingAllocator<X>>,
297 TEST_CONSTEXPR Tag_X(Ctor_Tag) {}
298 // CopyInsertable into vector<X, TaggingAllocator<X>>,
299 TEST_CONSTEXPR Tag_X(Ctor_Tag, const Tag_X&) {}
300 // MoveInsertable into vector<X, TaggingAllocator<X>>, and
301 TEST_CONSTEXPR Tag_X(Ctor_Tag, Tag_X&&) {}
303 // EmplaceConstructible into vector<X, TaggingAllocator<X>> from args.
304 template <typename... Args>
305 TEST_CONSTEXPR Tag_X(Ctor_Tag, Args&&...) {}
307 // not DefaultConstructible, CopyConstructible or MoveConstructible.
308 Tag_X() = delete;
309 Tag_X(const Tag_X&) = delete;
310 Tag_X(Tag_X&&) = delete;
312 // CopyAssignable.
313 TEST_CONSTEXPR_CXX14 Tag_X& operator=(const Tag_X&) { return *this; };
315 // MoveAssignable.
316 TEST_CONSTEXPR_CXX14 Tag_X& operator=(Tag_X&&) { return *this; };
318 private:
319 ~Tag_X() = default;
320 // Erasable from vector<X, TaggingAllocator<X>>.
321 friend class TaggingAllocator<Tag_X>;
324 template <typename T>
325 class TaggingAllocator {
326 public:
327 using value_type = T;
328 TaggingAllocator() = default;
330 template <typename U>
331 TEST_CONSTEXPR TaggingAllocator(const TaggingAllocator<U>&) {}
333 template <typename... Args>
334 TEST_CONSTEXPR_CXX20 void construct(Tag_X* p, Args&&... args) {
335 #if TEST_STD_VER > 17
336 std::construct_at(p, Ctor_Tag{}, std::forward<Args>(args)...);
337 #else
338 ::new (static_cast<void*>(p)) Tag_X(Ctor_Tag(), std::forward<Args>(args)...);
339 #endif
342 template <typename U>
343 TEST_CONSTEXPR_CXX20 void destroy(U* p) {
344 p->~U();
347 TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
348 TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }
351 template <std::size_t MaxAllocs>
352 struct limited_alloc_handle {
353 std::size_t outstanding_ = 0;
354 void* last_alloc_ = nullptr;
356 template <class T>
357 TEST_CONSTEXPR_CXX20 T* allocate(std::size_t N) {
358 if (N + outstanding_ > MaxAllocs)
359 TEST_THROW(std::bad_alloc());
360 auto alloc = std::allocator<T>().allocate(N);
361 last_alloc_ = alloc;
362 outstanding_ += N;
363 return alloc;
366 template <class T>
367 TEST_CONSTEXPR_CXX20 void deallocate(T* ptr, std::size_t N) {
368 if (ptr == last_alloc_) {
369 last_alloc_ = nullptr;
370 assert(outstanding_ >= N);
371 outstanding_ -= N;
373 std::allocator<T>().deallocate(ptr, N);
377 namespace detail {
378 template <class T>
379 class thread_unsafe_shared_ptr {
380 public:
381 thread_unsafe_shared_ptr() = default;
383 TEST_CONSTEXPR_CXX14 thread_unsafe_shared_ptr(const thread_unsafe_shared_ptr& other) : block(other.block) {
384 ++block->ref_count;
387 TEST_CONSTEXPR_CXX20 ~thread_unsafe_shared_ptr() {
388 --block->ref_count;
389 if (block->ref_count != 0)
390 return;
391 typedef std::allocator_traits<std::allocator<control_block> > allocator_traits;
392 std::allocator<control_block> alloc;
393 allocator_traits::destroy(alloc, block);
394 allocator_traits::deallocate(alloc, block, 1);
397 TEST_CONSTEXPR const T& operator*() const { return block->content; }
398 TEST_CONSTEXPR const T* operator->() const { return &block->content; }
399 TEST_CONSTEXPR_CXX14 T& operator*() { return block->content; }
400 TEST_CONSTEXPR_CXX14 T* operator->() { return &block->content; }
401 TEST_CONSTEXPR_CXX14 T* get() { return &block->content; }
402 TEST_CONSTEXPR const T* get() const { return &block->content; }
404 private:
405 struct control_block {
406 template <class... Args>
407 TEST_CONSTEXPR control_block(Args... args) : content(std::forward<Args>(args)...) {}
408 std::size_t ref_count = 1;
409 T content;
412 control_block* block = nullptr;
414 template <class U, class... Args>
415 friend TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr<U> make_thread_unsafe_shared(Args...);
418 template <class T, class... Args>
419 TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr<T> make_thread_unsafe_shared(Args... args) {
420 typedef typename thread_unsafe_shared_ptr<T>::control_block control_block_type;
421 typedef std::allocator_traits<std::allocator<control_block_type> > allocator_traits;
423 thread_unsafe_shared_ptr<T> ptr;
424 std::allocator<control_block_type> alloc;
425 ptr.block = allocator_traits::allocate(alloc, 1);
426 allocator_traits::construct(alloc, ptr.block, std::forward<Args>(args)...);
428 return ptr;
430 } // namespace detail
432 template <class T, std::size_t N>
433 class limited_allocator {
434 template <class U, std::size_t UN>
435 friend class limited_allocator;
436 typedef limited_alloc_handle<N> BuffT;
437 detail::thread_unsafe_shared_ptr<BuffT> handle_;
439 public:
440 typedef T value_type;
441 typedef value_type* pointer;
442 typedef const value_type* const_pointer;
443 typedef value_type& reference;
444 typedef const value_type& const_reference;
445 typedef std::size_t size_type;
446 typedef std::ptrdiff_t difference_type;
448 template <class U>
449 struct rebind {
450 typedef limited_allocator<U, N> other;
453 TEST_CONSTEXPR_CXX20 limited_allocator() : handle_(detail::make_thread_unsafe_shared<BuffT>()) {}
455 limited_allocator(limited_allocator const&) = default;
457 template <class U>
458 TEST_CONSTEXPR explicit limited_allocator(limited_allocator<U, N> const& other) : handle_(other.handle_) {}
460 limited_allocator& operator=(const limited_allocator&) = delete;
462 TEST_CONSTEXPR_CXX20 pointer allocate(size_type n) { return handle_->template allocate<T>(n); }
463 TEST_CONSTEXPR_CXX20 void deallocate(pointer p, size_type n) { handle_->template deallocate<T>(p, n); }
464 TEST_CONSTEXPR size_type max_size() const { return N; }
465 TEST_CONSTEXPR BuffT* getHandle() const { return handle_.get(); }
468 template <class T, class U, std::size_t N>
469 TEST_CONSTEXPR inline bool operator==(limited_allocator<T, N> const& LHS, limited_allocator<U, N> const& RHS) {
470 return LHS.getHandle() == RHS.getHandle();
473 template <class T, class U, std::size_t N>
474 TEST_CONSTEXPR inline bool operator!=(limited_allocator<T, N> const& LHS, limited_allocator<U, N> const& RHS) {
475 return !(LHS == RHS);
478 // Track the "provenance" of this allocator instance: how many times was
479 // select_on_container_copy_construction called in order to produce it?
481 template <class T>
482 struct SocccAllocator {
483 using value_type = T;
485 int count_ = 0;
486 explicit SocccAllocator(int i) : count_(i) {}
488 template <class U>
489 SocccAllocator(const SocccAllocator<U>& a) : count_(a.count_) {}
491 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
492 void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }
494 SocccAllocator select_on_container_copy_construction() const { return SocccAllocator(count_ + 1); }
496 bool operator==(const SocccAllocator&) const { return true; }
498 using propagate_on_container_copy_assignment = std::false_type;
499 using propagate_on_container_move_assignment = std::false_type;
500 using propagate_on_container_swap = std::false_type;
503 #endif // TEST_ALLOCATOR_H