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 constructors don't leak memory when an operation inside the constructor throws an exception
13
14 #include <type_traits>
15 #include <vector>
16
17 #include "count_new.h"
18 #include "test_iterators.h"
19
20 template <class T>
21 struct Allocator {
22 using value_type = T;
23 using is_always_equal = std::false_type;
24
AllocatorAllocator25 Allocator(bool should_throw = true) {
26 if (should_throw)
27 throw 0;
28 }
29
allocateAllocator30 T* allocate(int n) { return std::allocator<T>().allocate(n); }
deallocateAllocator31 void deallocate(T* ptr, int n) { std::allocator<T>().deallocate(ptr, n); }
32
operator ==(const Allocator &,const Allocator &)33 friend bool operator==(const Allocator&, const Allocator&) { return false; }
34 };
35
36 struct ThrowingT {
37 int* throw_after_n_ = nullptr;
ThrowingTThrowingT38 ThrowingT() { throw 0; }
39
ThrowingTThrowingT40 ThrowingT(int& throw_after_n) : throw_after_n_(&throw_after_n) {
41 if (throw_after_n == 0)
42 throw 0;
43 --throw_after_n;
44 }
45
ThrowingTThrowingT46 ThrowingT(const ThrowingT&) {
47 if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
48 throw 1;
49 --*throw_after_n_;
50 }
51
operator =ThrowingT52 ThrowingT& operator=(const ThrowingT&) {
53 if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
54 throw 1;
55 --*throw_after_n_;
56 return *this;
57 }
58 };
59
60 template <class IterCat>
61 struct Iterator {
62 using iterator_category = IterCat;
63 using difference_type = std::ptrdiff_t;
64 using value_type = int;
65 using reference = int&;
66 using pointer = int*;
67
68 int i_;
IteratorIterator69 Iterator(int i = 0) : i_(i) {}
operator *Iterator70 int& operator*() {
71 if (i_ == 1)
72 throw 1;
73 return i_;
74 }
75
operator ==(const Iterator & lhs,const Iterator & rhs)76 friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; }
77
operator !=(const Iterator & lhs,const Iterator & rhs)78 friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; }
79
operator ++Iterator80 Iterator& operator++() {
81 ++i_;
82 return *this;
83 }
84
operator ++Iterator85 Iterator operator++(int) {
86 auto tmp = *this;
87 ++i_;
88 return tmp;
89 }
90 };
91
check_new_delete_called()92 void check_new_delete_called() {
93 assert(globalMemCounter.new_called == globalMemCounter.delete_called);
94 assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
95 assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
96 assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
97 }
98
main(int,char **)99 int main(int, char**) {
100 using AllocVec = std::vector<int, Allocator<int> >;
101 try { // vector()
102 AllocVec vec;
103 } catch (int) {
104 }
105 check_new_delete_called();
106
107 try { // Throw in vector(size_type) from type
108 std::vector<ThrowingT> get_alloc(1);
109 } catch (int) {
110 }
111 check_new_delete_called();
112
113 #if TEST_STD_VER >= 14
114 try { // Throw in vector(size_type, value_type) from type
115 int throw_after = 1;
116 ThrowingT v(throw_after);
117 std::vector<ThrowingT> get_alloc(1, v);
118 } catch (int) {
119 }
120 check_new_delete_called();
121
122 try { // Throw in vector(size_type, const allocator_type&) from allocator
123 Allocator<int> alloc(false);
124 AllocVec get_alloc(0, alloc);
125 } catch (int) {
126 }
127 check_new_delete_called();
128
129 try { // Throw in vector(size_type, const allocator_type&) from the type
130 std::vector<ThrowingT> vec(1, std::allocator<ThrowingT>());
131 } catch (int) {
132 }
133 check_new_delete_called();
134 #endif // TEST_STD_VER >= 14
135
136 try { // Throw in vector(InputIterator, InputIterator) from input iterator
137 std::vector<int> vec((Iterator<std::input_iterator_tag>()), Iterator<std::input_iterator_tag>(2));
138 } catch (int) {
139 }
140 check_new_delete_called();
141
142 try { // Throw in vector(InputIterator, InputIterator) from forward iterator
143 std::vector<int> vec((Iterator<std::forward_iterator_tag>()), Iterator<std::forward_iterator_tag>(2));
144 } catch (int) {
145 }
146 check_new_delete_called();
147
148 try { // Throw in vector(InputIterator, InputIterator) from allocator
149 int a[] = {1, 2};
150 AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2));
151 } catch (int) {
152 }
153 check_new_delete_called();
154
155 try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator
156 std::allocator<int> alloc;
157 std::vector<int> vec(Iterator<std::input_iterator_tag>(), Iterator<std::input_iterator_tag>(2), alloc);
158 } catch (int) {
159 }
160 check_new_delete_called();
161
162 try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator
163 std::allocator<int> alloc;
164 std::vector<int> vec(Iterator<std::forward_iterator_tag>(), Iterator<std::forward_iterator_tag>(2), alloc);
165 } catch (int) {
166 }
167 check_new_delete_called();
168
169 try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
170 int a[] = {1, 2};
171 Allocator<int> alloc(false);
172 AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2), alloc);
173 } catch (int) {
174 }
175 check_new_delete_called();
176
177 try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
178 int a[] = {1, 2};
179 Allocator<int> alloc(false);
180 AllocVec vec(forward_iterator<int*>(a), forward_iterator<int*>(a + 2), alloc);
181 } catch (int) {
182 }
183 check_new_delete_called();
184
185 try { // Throw in vector(const vector&) from type
186 std::vector<ThrowingT> vec;
187 int throw_after = 0;
188 vec.emplace_back(throw_after);
189 auto vec2 = vec;
190 } catch (int) {
191 }
192 check_new_delete_called();
193
194 try { // Throw in vector(const vector&, const allocator_type&) from type
195 std::vector<ThrowingT> vec;
196 int throw_after = 1;
197 vec.emplace_back(throw_after);
198 std::vector<ThrowingT> vec2(vec, std::allocator<int>());
199 } catch (int) {
200 }
201 check_new_delete_called();
202
203 try { // Throw in vector(vector&&, const allocator_type&) from type
204 std::vector<ThrowingT, Allocator<ThrowingT> > vec(Allocator<ThrowingT>(false));
205 int throw_after = 1;
206 vec.emplace_back(throw_after);
207 std::vector<ThrowingT, Allocator<ThrowingT> > vec2(std::move(vec), Allocator<ThrowingT>(false));
208 } catch (int) {
209 }
210 check_new_delete_called();
211
212 #if TEST_STD_VER >= 11
213 try { // Throw in vector(initializer_list<value_type>) from type
214 int throw_after = 1;
215 std::vector<ThrowingT> vec({ThrowingT(throw_after)});
216 } catch (int) {
217 }
218 check_new_delete_called();
219
220 try { // Throw in vector(initializer_list<value_type>, const allocator_type&) constructor from type
221 int throw_after = 1;
222 std::vector<ThrowingT> vec({ThrowingT(throw_after)}, std::allocator<ThrowingT>());
223 } catch (int) {
224 }
225 check_new_delete_called();
226 #endif // TEST_STD_VER >= 11
227
228 return 0;
229 }
230