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: no-exceptions |
10 | |
11 | // (bug report: https://llvm.org/PR58392) |
12 | // Check that vector<bool> constructors don't leak memory when an operation inside the constructor throws an exception |
13 | |
14 | #include <cstddef> |
15 | #include <type_traits> |
16 | #include <memory> |
17 | #include <vector> |
18 | |
19 | #include "count_new.h" |
20 | #include "test_iterators.h" |
21 | |
22 | template <class T> |
23 | struct Allocator { |
24 | using value_type = T; |
25 | using is_always_equal = std::false_type; |
26 | |
27 | template <class U> |
28 | Allocator(const Allocator<U>&) {} |
29 | |
30 | Allocator(bool should_throw = true) { |
31 | if (should_throw) |
32 | throw 0; |
33 | } |
34 | |
35 | T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); } |
36 | void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); } |
37 | |
38 | template <class U> |
39 | friend bool operator==(const Allocator&, const Allocator<U>&) { return true; } |
40 | }; |
41 | |
42 | template <class IterCat> |
43 | struct Iterator { |
44 | using iterator_category = IterCat; |
45 | using difference_type = std::ptrdiff_t; |
46 | using value_type = bool; |
47 | using reference = bool&; |
48 | using pointer = bool*; |
49 | |
50 | int i_; |
51 | bool b_ = true; |
52 | Iterator(int i = 0) : i_(i) {} |
53 | bool& operator*() { |
54 | if (i_ == 1) |
55 | throw 1; |
56 | return b_; |
57 | } |
58 | |
59 | friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; } |
60 | |
61 | friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; } |
62 | |
63 | Iterator& operator++() { |
64 | ++i_; |
65 | return *this; |
66 | } |
67 | |
68 | Iterator operator++(int) { |
69 | auto tmp = *this; |
70 | ++i_; |
71 | return tmp; |
72 | } |
73 | }; |
74 | |
75 | void check_new_delete_called() { |
76 | assert(globalMemCounter.new_called == globalMemCounter.delete_called); |
77 | assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called); |
78 | assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called); |
79 | assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called); |
80 | } |
81 | |
82 | int main(int, char**) { |
83 | using AllocVec = std::vector<bool, Allocator<bool> >; |
84 | |
85 | #if TEST_STD_VER >= 14 |
86 | try { // Throw in vector(size_type, const allocator_type&) from allocator |
87 | Allocator<bool> alloc(false); |
88 | AllocVec get_alloc(0, alloc); |
89 | } catch (int) { |
90 | } |
91 | check_new_delete_called(); |
92 | #endif // TEST_STD_VER >= 14 |
93 | |
94 | try { // Throw in vector(InputIterator, InputIterator) from input iterator |
95 | std::vector<bool> vec((Iterator<std::input_iterator_tag>()), Iterator<std::input_iterator_tag>(2)); |
96 | } catch (int) { |
97 | } |
98 | check_new_delete_called(); |
99 | |
100 | try { // Throw in vector(InputIterator, InputIterator) from forward iterator |
101 | std::vector<bool> vec((Iterator<std::forward_iterator_tag>()), Iterator<std::forward_iterator_tag>(2)); |
102 | } catch (int) { |
103 | } |
104 | check_new_delete_called(); |
105 | |
106 | try { // Throw in vector(InputIterator, InputIterator) from allocator |
107 | int a[] = {1, 2}; |
108 | AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2)); |
109 | } catch (int) { |
110 | } |
111 | check_new_delete_called(); |
112 | |
113 | try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator |
114 | std::allocator<bool> alloc; |
115 | std::vector<bool> vec(Iterator<std::input_iterator_tag>(), Iterator<std::input_iterator_tag>(2), alloc); |
116 | } catch (int) { |
117 | } |
118 | check_new_delete_called(); |
119 | |
120 | try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator |
121 | std::allocator<bool> alloc; |
122 | std::vector<bool> vec(Iterator<std::forward_iterator_tag>(), Iterator<std::forward_iterator_tag>(2), alloc); |
123 | } catch (int) { |
124 | } |
125 | check_new_delete_called(); |
126 | |
127 | try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator |
128 | bool a[] = {true, false}; |
129 | Allocator<bool> alloc(false); |
130 | AllocVec vec(cpp17_input_iterator<bool*>(a), cpp17_input_iterator<bool*>(a + 2), alloc); |
131 | } catch (int) { |
132 | } |
133 | check_new_delete_called(); |
134 | |
135 | try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator |
136 | bool a[] = {true, false}; |
137 | Allocator<bool> alloc(false); |
138 | AllocVec vec(forward_iterator<bool*>(a), forward_iterator<bool*>(a + 2), alloc); |
139 | } catch (int) { |
140 | } |
141 | check_new_delete_called(); |
142 | |
143 | return 0; |
144 | } |
145 | |