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 | // This test file validates that std::vector<T>::reserve provides the strong exception guarantee if T is |
12 | // Cpp17MoveInsertable and no exception is thrown by the move constructor of T during the reserve call. |
13 | // It also checks that if T's move constructor is not noexcept, reserve provides only the basic exception |
14 | // guarantee. |
15 | |
16 | #include <cstddef> |
17 | #include <memory> |
18 | #include <type_traits> |
19 | #include <vector> |
20 | |
21 | #include "../common.h" |
22 | #include "MoveOnly.h" |
23 | #include "count_new.h" |
24 | #include "increasing_allocator.h" |
25 | #include "min_allocator.h" |
26 | #include "test_allocator.h" |
27 | #include "test_iterators.h" |
28 | #include "test_macros.h" |
29 | |
30 | template <typename T, typename Alloc> |
31 | void test_allocation_exception_for_strong_guarantee( |
32 | std::vector<T, Alloc>& v, const std::vector<T>& values, std::size_t new_cap) { |
33 | assert(v.size() == values.size()); |
34 | T* old_data = v.data(); |
35 | std::size_t old_size = v.size(); |
36 | std::size_t old_cap = v.capacity(); |
37 | |
38 | try { |
39 | v.reserve(new_cap); |
40 | } catch (...) { // std::length_error, std::bad_alloc |
41 | assert(v.data() == old_data); |
42 | assert(v.size() == old_size); |
43 | assert(v.capacity() == old_cap); |
44 | for (std::size_t i = 0; i < v.size(); ++i) |
45 | assert(v[i] == values[i]); |
46 | } |
47 | } |
48 | |
49 | template <typename T, typename Alloc> |
50 | void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>, Alloc>& v, |
51 | const std::vector<T>& values) { |
52 | assert(v.empty() && !values.empty()); |
53 | int throw_after = values.size() + values.size() / 2; // Trigger an exception halfway through reallocation |
54 | v.reserve(values.size()); |
55 | for (std::size_t i = 0; i < values.size(); ++i) |
56 | v.emplace_back(values[i], throw_after); |
57 | |
58 | throwing_data<T>* old_data = v.data(); |
59 | std::size_t old_size = v.size(); |
60 | std::size_t old_cap = v.capacity(); |
61 | std::size_t new_cap = 2 * old_cap; |
62 | |
63 | try { |
64 | v.reserve(new_cap); |
65 | } catch (...) { |
66 | assert(v.data() == old_data); |
67 | assert(v.size() == old_size); |
68 | assert(v.capacity() == old_cap); |
69 | for (std::size_t i = 0; i < v.size(); ++i) |
70 | assert(v[i].data_ == values[i]); |
71 | } |
72 | } |
73 | |
74 | #if TEST_STD_VER >= 11 |
75 | |
76 | template <typename T, typename Alloc> |
77 | void test_move_ctor_exception_for_basic_guarantee(std::vector<move_only_throwing_t<T>, Alloc>& v, |
78 | const std::vector<T>& values) { |
79 | assert(v.empty() && !values.empty()); |
80 | int throw_after = values.size() + values.size() / 2; // Trigger an exception halfway through reallocation |
81 | v.reserve(values.size()); |
82 | for (std::size_t i = 0; i < values.size(); ++i) |
83 | v.emplace_back(values[i], throw_after); |
84 | |
85 | try { |
86 | v.reserve(2 * v.capacity()); |
87 | } catch (...) { |
88 | use_unspecified_but_valid_state_vector(v); |
89 | } |
90 | } |
91 | |
92 | #endif |
93 | |
94 | // Check the strong exception guarantee during reallocation failures |
95 | void test_allocation_exceptions() { |
96 | // |
97 | // Tests for std::length_error during reallocation failures |
98 | // |
99 | { |
100 | std::vector<int> v; |
101 | test_allocation_exception_for_strong_guarantee(v, values: std::vector<int>(), new_cap: v.max_size() + 1); |
102 | } |
103 | check_new_delete_called(); |
104 | |
105 | { |
106 | int a[] = {1, 2, 3, 4, 5}; |
107 | std::vector<int> in(a, a + sizeof(a) / sizeof(a[0])); |
108 | std::vector<int> v(in.begin(), in.end()); |
109 | test_allocation_exception_for_strong_guarantee(v, values: in, new_cap: v.max_size() + 1); |
110 | } |
111 | check_new_delete_called(); |
112 | |
113 | { |
114 | int a[] = {1, 2, 3, 4, 5}; |
115 | std::vector<int> in(a, a + sizeof(a) / sizeof(a[0])); |
116 | std::vector<int, min_allocator<int> > v(in.begin(), in.end()); |
117 | test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1); |
118 | } |
119 | check_new_delete_called(); |
120 | |
121 | { |
122 | int a[] = {1, 2, 3, 4, 5}; |
123 | std::vector<int> in(a, a + sizeof(a) / sizeof(a[0])); |
124 | std::vector<int, safe_allocator<int> > v(in.begin(), in.end()); |
125 | test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1); |
126 | } |
127 | check_new_delete_called(); |
128 | |
129 | { |
130 | int a[] = {1, 2, 3, 4, 5}; |
131 | std::vector<int> in(a, a + sizeof(a) / sizeof(a[0])); |
132 | std::vector<int, test_allocator<int> > v(in.begin(), in.end()); |
133 | test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1); |
134 | } |
135 | check_new_delete_called(); |
136 | |
137 | { |
138 | std::vector<int> in(10, 42); |
139 | std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end()); |
140 | test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1); |
141 | } |
142 | check_new_delete_called(); |
143 | |
144 | #if TEST_STD_VER >= 23 |
145 | { |
146 | std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
147 | std::vector<int, increasing_allocator<int>> v(in.begin(), in.end()); |
148 | test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1); |
149 | } |
150 | check_new_delete_called(); |
151 | #endif |
152 | |
153 | // |
154 | // Tests for std::bad_alloc during reallocation failures |
155 | // |
156 | { |
157 | std::vector<int> in(10, 42); |
158 | std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end()); |
159 | test_allocation_exception_for_strong_guarantee(v, in, 91); |
160 | } |
161 | check_new_delete_called(); |
162 | |
163 | { |
164 | std::vector<int> in(10, 42); |
165 | std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end()); |
166 | v.reserve(30); |
167 | test_allocation_exception_for_strong_guarantee(v, in, 61); |
168 | } |
169 | check_new_delete_called(); |
170 | |
171 | #if TEST_STD_VER >= 11 |
172 | { |
173 | std::vector<MoveOnly> in(10); |
174 | std::vector<MoveOnly, limited_allocator<MoveOnly, 100> > v(10); |
175 | test_allocation_exception_for_strong_guarantee(v, in, 91); |
176 | } |
177 | check_new_delete_called(); |
178 | |
179 | { |
180 | std::vector<MoveOnly> in(10); |
181 | in.insert(in.cbegin() + 5, MoveOnly(42)); |
182 | std::vector<MoveOnly, limited_allocator<MoveOnly, 100> > v(10); |
183 | v.reserve(30); |
184 | v.insert(v.cbegin() + 5, MoveOnly(42)); |
185 | test_allocation_exception_for_strong_guarantee(v, in, 61); |
186 | } |
187 | check_new_delete_called(); |
188 | #endif |
189 | |
190 | { // Practical example: Testing with 100 integers. |
191 | auto in = getIntegerInputs(n: 100); |
192 | std::vector<int, limited_allocator<int, 299> > v(in.begin(), in.end()); |
193 | test_allocation_exception_for_strong_guarantee(v, in, 200); |
194 | } |
195 | check_new_delete_called(); |
196 | |
197 | { // Practical example: Testing with 100 strings, each 256 characters long. |
198 | std::vector<std::string> in = getStringInputsWithLength(n: 100, len: 256); |
199 | std::vector<std::string, limited_allocator<std::string, 299> > v(in.begin(), in.end()); |
200 | test_allocation_exception_for_strong_guarantee(v, in, 200); |
201 | } |
202 | check_new_delete_called(); |
203 | } |
204 | |
205 | // Check the strong exception guarantee during copy-constructor failures |
206 | void test_copy_ctor_exceptions() { |
207 | { |
208 | int a[] = {1, 2, 3, 4, 5}; |
209 | std::vector<int> in(a, a + sizeof(a) / sizeof(a[0])); |
210 | std::vector<throwing_data<int> > v; |
211 | test_copy_ctor_exception_for_strong_guarantee(v, values: in); |
212 | } |
213 | check_new_delete_called(); |
214 | |
215 | { |
216 | int a[] = {1, 2, 3, 4, 5}; |
217 | std::vector<int> in(a, a + sizeof(a) / sizeof(a[0])); |
218 | std::vector<throwing_data<int>, min_allocator<throwing_data<int> > > v; |
219 | test_copy_ctor_exception_for_strong_guarantee(v, in); |
220 | } |
221 | check_new_delete_called(); |
222 | |
223 | { |
224 | std::vector<int> in(10, 42); |
225 | std::vector<throwing_data<int>, safe_allocator<throwing_data<int> > > v; |
226 | test_copy_ctor_exception_for_strong_guarantee(v, in); |
227 | } |
228 | check_new_delete_called(); |
229 | |
230 | { |
231 | std::vector<int> in(10, 42); |
232 | std::vector<throwing_data<int>, test_allocator<throwing_data<int> > > v; |
233 | test_copy_ctor_exception_for_strong_guarantee(v, in); |
234 | } |
235 | check_new_delete_called(); |
236 | |
237 | { |
238 | std::vector<int> in(10, 42); |
239 | std::vector<throwing_data<int>, limited_allocator<throwing_data<int>, 100> > v; |
240 | test_copy_ctor_exception_for_strong_guarantee(v, in); |
241 | } |
242 | check_new_delete_called(); |
243 | |
244 | #if TEST_STD_VER >= 23 |
245 | { |
246 | std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
247 | std::vector<throwing_data<int>, increasing_allocator<throwing_data<int>>> v; |
248 | test_copy_ctor_exception_for_strong_guarantee(v, in); |
249 | } |
250 | check_new_delete_called(); |
251 | #endif |
252 | |
253 | { // Practical example: Testing with 100 integers. |
254 | auto in = getIntegerInputs(n: 100); |
255 | std::vector<throwing_data<int> > v; |
256 | test_copy_ctor_exception_for_strong_guarantee(v, values: in); |
257 | } |
258 | check_new_delete_called(); |
259 | |
260 | { // Practical example: Testing with 100 strings, each 256 characters long. |
261 | std::vector<std::string> in = getStringInputsWithLength(n: 100, len: 256); |
262 | std::vector<throwing_data<std::string> > v; |
263 | test_copy_ctor_exception_for_strong_guarantee(v, values: in); |
264 | } |
265 | check_new_delete_called(); |
266 | } |
267 | |
268 | #if TEST_STD_VER >= 11 |
269 | |
270 | // Check that if T is Cpp17MoveInsertible && !Cpp17CopyInsertible, and T's move-ctor is not noexcept, then |
271 | // std::vector::reserve only provides basic guarantee. |
272 | void test_move_ctor_exceptions() { |
273 | { |
274 | std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
275 | std::vector<move_only_throwing_t<int>> v; |
276 | test_move_ctor_exception_for_basic_guarantee(v, in); |
277 | } |
278 | check_new_delete_called(); |
279 | |
280 | # if TEST_STD_VER >= 23 |
281 | { |
282 | std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
283 | std::vector<move_only_throwing_t<int>, increasing_allocator<move_only_throwing_t<int>>> v; |
284 | test_move_ctor_exception_for_basic_guarantee(v, in); |
285 | } |
286 | check_new_delete_called(); |
287 | # endif |
288 | |
289 | { |
290 | // Practical example: Testing with 100 strings, each 256 characters long. |
291 | std::vector<std::string> in = getStringInputsWithLength(100, 256); |
292 | std::vector<move_only_throwing_t<std::string> > v; |
293 | test_move_ctor_exception_for_basic_guarantee(v, in); |
294 | } |
295 | check_new_delete_called(); |
296 | } |
297 | |
298 | #endif |
299 | |
300 | int main(int, char**) { |
301 | test_allocation_exceptions(); |
302 | test_copy_ctor_exceptions(); |
303 | #if TEST_STD_VER >= 11 |
304 | test_move_ctor_exceptions(); |
305 | #endif |
306 | } |
307 | |