Skip to content

Commit 5cf00d4

Browse files
committed
[libc++] Destroy elements when exceptions are thrown in __construct_at_end
1 parent af456df commit 5cf00d4

File tree

2 files changed

+338
-1
lines changed

2 files changed

+338
-1
lines changed

libcxx/include/__vector/vector.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,19 +749,21 @@ class vector {
749749

750750
struct _ConstructTransaction {
751751
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit _ConstructTransaction(vector& __v, size_type __n)
752-
: __v_(__v), __pos_(__v.__end_), __new_end_(__v.__end_ + __n) {
752+
: __v_(__v), __pos_(__v.__end_), __old_end_(__v.__end_), __new_end_(__v.__end_ + __n) {
753753
__v_.__annotate_increase(__n);
754754
}
755755

756756
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~_ConstructTransaction() {
757757
__v_.__end_ = __pos_;
758758
if (__pos_ != __new_end_) {
759+
__v_.__destruct_at_end(__old_end_);
759760
__v_.__annotate_shrink(__new_end_ - __v_.__begin_);
760761
}
761762
}
762763

763764
vector& __v_;
764765
pointer __pos_;
766+
pointer const __old_end_;
765767
const_pointer const __new_end_;
766768

767769
_ConstructTransaction(_ConstructTransaction const&) = delete;
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
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+
// <vector>
12+
13+
// Make sure elements are destroyed when exceptions thrown in __construct_at_end
14+
15+
#include <cassert>
16+
#include <cstddef>
17+
#include <memory>
18+
#include <vector>
19+
20+
#include "test_macros.h"
21+
#if TEST_STD_VER >= 20
22+
# include <ranges>
23+
#endif
24+
25+
#include "common.h"
26+
#include "count_new.h"
27+
28+
struct throw_context {
29+
static int num;
30+
static int limit;
31+
32+
throw_context(int lim = 2) {
33+
num = 0;
34+
limit = lim;
35+
}
36+
37+
static void inc() {
38+
++num;
39+
if (num >= limit) {
40+
--num;
41+
throw 1;
42+
}
43+
}
44+
45+
static void dec() { --num; }
46+
};
47+
48+
int throw_context::num = 0;
49+
int throw_context::limit = 0;
50+
51+
int debug = 0;
52+
53+
class throw_element {
54+
public:
55+
throw_element() : data(new int(1)) {
56+
try {
57+
throw_context::inc();
58+
} catch (int) {
59+
delete data;
60+
throw;
61+
}
62+
}
63+
64+
throw_element([[maybe_unused]] throw_element const& other) : data(new int(1)) {
65+
try {
66+
throw_context::inc();
67+
} catch (int) {
68+
delete data;
69+
throw;
70+
}
71+
}
72+
73+
~throw_element() {
74+
if (data) {
75+
delete data;
76+
throw_context::dec();
77+
if (debug)
78+
printf("dctor\n");
79+
}
80+
}
81+
82+
throw_element& operator=([[maybe_unused]] throw_element const& other) {
83+
// nothing to do
84+
return *this;
85+
}
86+
87+
private:
88+
int* data;
89+
};
90+
91+
int main(int, char*[]) {
92+
using AllocType = std::allocator<throw_element>;
93+
94+
// vector(size_type __n)
95+
{
96+
throw_context ctx;
97+
try {
98+
std::vector<throw_element> v(3);
99+
} catch (int) {
100+
}
101+
check_new_delete_called();
102+
}
103+
104+
// vector(size_type __n, const allocator_type& __a)
105+
{
106+
throw_context ctx;
107+
AllocType alloc;
108+
try {
109+
std::vector<throw_element> v(3, alloc);
110+
} catch (int) {
111+
}
112+
check_new_delete_called();
113+
}
114+
115+
// vector(size_type __n, const value_type& __x)
116+
{
117+
throw_context ctx(3);
118+
try {
119+
throw_element e;
120+
std::vector<throw_element> v(3, e);
121+
} catch (int) {
122+
}
123+
check_new_delete_called();
124+
}
125+
126+
// vector(size_type __n, const value_type& __x, const allocator_type& __a)
127+
{
128+
throw_context ctx(3);
129+
try {
130+
throw_element e;
131+
AllocType alloc;
132+
std::vector<throw_element> v(4, e, alloc);
133+
} catch (int) {
134+
}
135+
check_new_delete_called();
136+
}
137+
138+
// vector(_ForwardIterator __first, _ForwardIterator __last)
139+
{
140+
throw_context ctx(4);
141+
try {
142+
std::vector<throw_element> v1(2);
143+
std::vector<throw_element> v2(v1.begin(), v1.end());
144+
} catch (int) {
145+
}
146+
check_new_delete_called();
147+
}
148+
149+
// vector(_ForwardIterator __first, _ForwardIterator __last, const allocator_type& __a)
150+
{
151+
throw_context ctx(4);
152+
AllocType alloc;
153+
try {
154+
std::vector<throw_element> v1(2);
155+
std::vector<throw_element> v2(v1.begin(), v1.end(), alloc);
156+
} catch (int) {
157+
}
158+
check_new_delete_called();
159+
}
160+
161+
#if _LIBCPP_STD_VER >= 23
162+
// vector(from_range_t, _Range&& __range, const allocator_type& __alloc = allocator_type())
163+
{
164+
throw_context ctx(4);
165+
try {
166+
std::vector<throw_element> r(2);
167+
std::vector<throw_element> v(std::from_range, std::views::counted(r.begin(), 2));
168+
} catch (int) {
169+
}
170+
check_new_delete_called();
171+
}
172+
#endif
173+
174+
// vector(const vector& __x)
175+
{
176+
throw_context ctx(4);
177+
try {
178+
std::vector<throw_element> v1(2);
179+
std::vector<throw_element> v2(v1);
180+
} catch (int) {
181+
}
182+
check_new_delete_called();
183+
}
184+
185+
// vector(initializer_list<value_type> __il)
186+
{
187+
throw_context ctx(6);
188+
try {
189+
throw_element e;
190+
std::vector<throw_element> v({e, e, e});
191+
} catch (int) {
192+
}
193+
check_new_delete_called();
194+
}
195+
196+
// vector(initializer_list<value_type> __il, const allocator_type& __a)
197+
{
198+
throw_context ctx(6);
199+
AllocType alloc;
200+
try {
201+
throw_element e;
202+
std::vector<throw_element> v({e, e, e}, alloc);
203+
} catch (int) {
204+
}
205+
check_new_delete_called();
206+
}
207+
208+
// void resize(size_type __sz)
209+
{
210+
// cap < size
211+
throw_context ctx;
212+
std::vector<throw_element> v;
213+
v.reserve(1);
214+
try {
215+
v.resize(4);
216+
} catch (int) {
217+
}
218+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
219+
}
220+
check_new_delete_called();
221+
222+
// void resize(size_type __sz, const_reference __x)
223+
{
224+
// cap < size
225+
throw_context ctx(3);
226+
std::vector<throw_element> v;
227+
v.reserve(1);
228+
try {
229+
throw_element e;
230+
v.resize(4, e);
231+
} catch (int) {
232+
}
233+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
234+
}
235+
check_new_delete_called();
236+
237+
// void assign(_ForwardIterator __first, _ForwardIterator __last)
238+
{
239+
// new size <= cap && new size > size
240+
throw_context ctx(4);
241+
std::vector<throw_element> v;
242+
v.reserve(3);
243+
try {
244+
std::vector<throw_element> data(2);
245+
v.assign(data.begin(), data.end());
246+
} catch (int) {
247+
}
248+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
249+
}
250+
check_new_delete_called();
251+
252+
{
253+
// new size > cap
254+
throw_context ctx(4);
255+
std::vector<throw_element> v;
256+
try {
257+
std::vector<throw_element> data(2);
258+
v.assign(data.begin(), data.end());
259+
} catch (int) {
260+
}
261+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
262+
}
263+
check_new_delete_called();
264+
265+
#if _LIBCPP_STD_VER >= 23
266+
// void assign_range(_Range&& __range)
267+
{
268+
throw_context ctx(5);
269+
std::vector<throw_element> v;
270+
try {
271+
std::vector<throw_element> r(3);
272+
v.assign_range(r);
273+
} catch (int) {
274+
}
275+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
276+
}
277+
check_new_delete_called();
278+
#endif
279+
280+
// vector& operator=(initializer_list<value_type> __il)
281+
{
282+
throw_context ctx(5);
283+
std::vector<throw_element> v;
284+
try {
285+
throw_element e;
286+
v = {e, e};
287+
} catch (int) {
288+
}
289+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
290+
}
291+
check_new_delete_called();
292+
293+
// vector<_Tp, _Allocator>& vector<_Tp, _Allocator>::operator=(const vector& __x)
294+
{
295+
throw_context ctx(4);
296+
std::vector<throw_element> v;
297+
try {
298+
std::vector<throw_element> data(2);
299+
v = data;
300+
} catch (int) {
301+
}
302+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
303+
}
304+
check_new_delete_called();
305+
306+
// iterator insert(const_iterator __position, _ForwardIterator __first, _ForwardIterator __last)
307+
{
308+
throw_context ctx(6);
309+
std::vector<throw_element> v;
310+
v.reserve(10);
311+
try {
312+
std::vector<throw_element> data(3);
313+
v.insert(v.begin(), data.begin(), data.end());
314+
} catch (int) {
315+
}
316+
assert(globalMemCounter.new_called == globalMemCounter.delete_called + 1);
317+
}
318+
check_new_delete_called();
319+
320+
#if _LIBCPP_STD_VER >= 23
321+
// iterator insert_range(const_iterator __position, _Range&& __range)
322+
{
323+
throw_context ctx(3);
324+
std::vector<throw_element> v;
325+
try {
326+
std::vector<throw_element> data(2);
327+
v.insert_range(v.begin(), data);
328+
} catch (int) {
329+
}
330+
check_new_delete_called();
331+
}
332+
#endif
333+
334+
return 0;
335+
}

0 commit comments

Comments
 (0)