Skip to content

Commit 136be22

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

File tree

3 files changed

+341
-2
lines changed

3 files changed

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

0 commit comments

Comments
 (0)