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>::shrink_to_fit provides the strong exception guarantee when
12// T is Cpp17MoveInsertable and its move constructor does not throw exceptions during the shrink_to_fit
13// call. Additionally, it checks that for move-only types where T's move constructor is not noexcept, only
14// the basic exception guarantee is ensured.
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
30template <typename T, typename Alloc>
31void test_allocation_exception_for_strong_guarantee(std::vector<T, Alloc>& v, const std::vector<T>& values) {
32 assert(v.size() == values.size());
33 T* old_data = v.data();
34 std::size_t old_size = v.size();
35 std::size_t old_cap = v.capacity();
36
37 try {
38 v.shrink_to_fit();
39 } catch (...) {
40 }
41
42 // As shrink_to_fit may swallow any exceptions, we place the checks outisde the catch block.
43 assert(v.data() == old_data);
44 assert(v.size() == old_size);
45 assert(v.capacity() == old_cap);
46 for (std::size_t i = 0; i < v.size(); ++i)
47 assert(v[i] == values[i]);
48}
49
50template <typename T, typename Alloc>
51void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>, Alloc>& v,
52 const std::vector<T>& values) {
53 assert(v.empty() && !values.empty());
54 v.reserve(values.size() * 2);
55 int throw_after = values.size() + values.size() / 2; // Trigger an exception halfway through reallocation
56 for (std::size_t i = 0; i < values.size(); ++i)
57 v.emplace_back(values[i], throw_after);
58
59 throwing_data<T>* old_data = v.data();
60 std::size_t old_size = v.size();
61 std::size_t old_cap = v.capacity();
62
63 try {
64 v.shrink_to_fit();
65 } catch (...) {
66 }
67
68 assert(v.data() == old_data);
69 assert(v.size() == old_size);
70 assert(v.capacity() == old_cap);
71 for (std::size_t i = 0; i < v.size(); ++i)
72 assert(v[i].data_ == values[i]);
73}
74
75#if TEST_STD_VER >= 11
76
77template <typename T, typename Alloc>
78void test_move_ctor_exception_for_basic_guarantee(std::vector<move_only_throwing_t<T>, Alloc>& v,
79 const std::vector<T>& values) {
80 assert(v.empty() && !values.empty());
81 v.reserve(values.size() * 2);
82 int throw_after = values.size() + values.size() / 2; // Trigger an exception halfway through reallocation
83 for (std::size_t i = 0; i < values.size(); ++i)
84 v.emplace_back(values[i], throw_after);
85
86 try {
87 v.shrink_to_fit();
88 } catch (...) {
89 }
90 use_unspecified_but_valid_state_vector(v);
91}
92
93#endif
94
95// Check the strong exception guarantee during reallocation failures
96void test_allocation_exceptions() {
97 {
98 int a[] = {1, 2, 3, 4, 5};
99 std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
100 std::vector<int, limited_allocator<int, 100> > v;
101 v.reserve(100);
102 for (std::size_t i = 0; i < in.size(); ++i)
103 v.push_back(in[i]);
104 test_allocation_exception_for_strong_guarantee(v, in);
105 }
106 check_new_delete_called();
107
108 {
109 std::vector<int> in(50, 42);
110 std::vector<int, limited_allocator<int, 100> > v;
111 v.reserve(100);
112 for (std::size_t i = 0; i < in.size(); ++i)
113 v.push_back(in[i]);
114 test_allocation_exception_for_strong_guarantee(v, in);
115 }
116 check_new_delete_called();
117
118 {
119 std::vector<int> in(10, 42);
120 std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end());
121 v.reserve(90);
122 test_allocation_exception_for_strong_guarantee(v, in);
123 }
124 check_new_delete_called();
125
126#if TEST_STD_VER >= 11
127 {
128 std::vector<MoveOnly> in(10);
129 std::vector<MoveOnly, limited_allocator<MoveOnly, 100> > v(10);
130 v.reserve(90);
131 test_allocation_exception_for_strong_guarantee(v, in);
132 }
133 check_new_delete_called();
134
135 {
136 std::vector<MoveOnly> in(10);
137 std::vector<MoveOnly, limited_allocator<MoveOnly, 100> > v(10);
138 v.reserve(90);
139 in.insert(in.cbegin() + 5, MoveOnly(42));
140 v.insert(v.cbegin() + 5, MoveOnly(42));
141 test_allocation_exception_for_strong_guarantee(v, in);
142 }
143 check_new_delete_called();
144#endif
145
146 { // Practical example: Testing with 100 integers.
147 auto in = getIntegerInputs(n: 100);
148 std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end());
149 in.erase(first: in.end() - 10, last: in.end());
150 v.erase(v.end() - 10, v.end());
151 test_allocation_exception_for_strong_guarantee(v, in);
152 }
153 check_new_delete_called();
154
155 { // Practical example: Testing with 100 strings, each 256 characters long.
156 std::vector<std::string> in = getStringInputsWithLength(n: 100, len: 256);
157 std::vector<std::string, limited_allocator<std::string, 300> > v(in.begin(), in.end());
158 v.reserve(200);
159 test_allocation_exception_for_strong_guarantee(v, in);
160 }
161 check_new_delete_called();
162}
163
164// Check the strong exception guarantee during copy-constructor failures
165void test_copy_ctor_exceptions() {
166 {
167 int a[] = {1, 2, 3, 4, 5};
168 std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
169 std::vector<throwing_data<int> > v;
170 test_copy_ctor_exception_for_strong_guarantee(v, values: in);
171 }
172 check_new_delete_called();
173
174 {
175 int a[] = {1, 2, 3, 4, 5};
176 std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
177 std::vector<throwing_data<int>, min_allocator<throwing_data<int> > > v;
178 test_copy_ctor_exception_for_strong_guarantee(v, in);
179 }
180 check_new_delete_called();
181
182 {
183 std::vector<int> in(10, 42);
184 std::vector<throwing_data<int>, safe_allocator<throwing_data<int> > > v;
185 test_copy_ctor_exception_for_strong_guarantee(v, in);
186 }
187 check_new_delete_called();
188
189 {
190 std::vector<int> in(10, 42);
191 std::vector<throwing_data<int>, test_allocator<throwing_data<int> > > v;
192 test_copy_ctor_exception_for_strong_guarantee(v, in);
193 }
194 check_new_delete_called();
195
196 {
197 std::vector<int> in(10, 42);
198 std::vector<throwing_data<int>, limited_allocator<throwing_data<int>, 100> > v;
199 test_copy_ctor_exception_for_strong_guarantee(v, in);
200 }
201 check_new_delete_called();
202
203 { // Practical example: Testing with 100 integers.
204 auto in = getIntegerInputs(n: 100);
205 std::vector<throwing_data<int> > v;
206 test_copy_ctor_exception_for_strong_guarantee(v, values: in);
207 }
208 check_new_delete_called();
209
210 { // Practical example: Testing with 100 strings, each 256 characters long.
211 std::vector<std::string> in = getStringInputsWithLength(n: 100, len: 256);
212 std::vector<throwing_data<std::string> > v;
213 test_copy_ctor_exception_for_strong_guarantee(v, values: in);
214 }
215 check_new_delete_called();
216}
217
218#if TEST_STD_VER >= 11
219
220// Check that if T is Cpp17MoveInsertible && !Cpp17CopyInsertible, and T's move-ctor is not noexcept, then
221// std::vector::shrink_to_fit only provides basic guarantee.
222void test_move_ctor_exceptions() {
223 {
224 std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
225 std::vector<move_only_throwing_t<int>> v;
226 test_move_ctor_exception_for_basic_guarantee(v, in);
227 }
228 check_new_delete_called();
229
230# if TEST_STD_VER >= 23
231 {
232 std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
233 std::vector<move_only_throwing_t<int>, increasing_allocator<move_only_throwing_t<int>>> v;
234 test_move_ctor_exception_for_basic_guarantee(v, in);
235 }
236 check_new_delete_called();
237# endif
238
239 {
240 // Practical example: Testing with 100 strings, each 256 characters long.
241 std::vector<std::string> in = getStringInputsWithLength(100, 256);
242 std::vector<move_only_throwing_t<std::string> > v;
243 test_move_ctor_exception_for_basic_guarantee(v, in);
244 }
245 check_new_delete_called();
246}
247
248#endif
249
250int main(int, char**) {
251 test_allocation_exceptions();
252 test_copy_ctor_exceptions();
253#if TEST_STD_VER >= 11
254 test_move_ctor_exceptions();
255#endif
256 return 0;
257}
258

source code of libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp