Skip to content

Commit 631eddd

Browse files
committed
[libc++] Destroy elements when exceptions are thrown in __construct_at_end
1 parent 0246f33 commit 631eddd

File tree

3 files changed

+348
-2
lines changed

3 files changed

+348
-2
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;

libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp

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

0 commit comments

Comments
 (0)