Skip to content

Commit 842bfb9

Browse files
committed
Add exception tests for vector<bool>
1 parent 991dcbc commit 842bfb9

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
// Check that vector<bool> constructors don't leak memory when an operation inside the constructor throws an exception
12+
13+
#include <cstddef>
14+
#include <memory>
15+
#include <type_traits>
16+
#include <vector>
17+
18+
#include "exception_test_helpers.h"
19+
#include "test_iterators.h"
20+
21+
int main(int, char**) {
22+
using AllocVec = std::vector<bool, throwing_allocator<bool> >;
23+
try { // Throw in vector() from allocator
24+
AllocVec vec; // Throw on default construction
25+
} catch (int) {
26+
}
27+
check_new_delete_called();
28+
29+
#if TEST_STD_VER >= 14
30+
try { // Throw in vector(size_type, const allocator_type&) from allocator
31+
throwing_allocator<bool> alloc(false, true); // Throw on copy only
32+
AllocVec get_alloc(0, alloc);
33+
} catch (int) {
34+
}
35+
check_new_delete_called();
36+
#endif // TEST_STD_VER >= 14
37+
38+
try { // Throw in vector(size_type, const value_type&, const allocator_type&) from allocator
39+
throwing_allocator<bool> alloc(false, true); // Throw on copy only
40+
AllocVec get_alloc(0, true, alloc);
41+
} catch (int) {
42+
}
43+
check_new_delete_called();
44+
45+
try { // Throw in vector(InputIterator, InputIterator) from input iterator
46+
std::vector<bool> vec((throwing_iterator<bool, std::input_iterator_tag>()), throwing_iterator<bool, std::input_iterator_tag>(2));
47+
} catch (int) {
48+
}
49+
check_new_delete_called();
50+
51+
try { // Throw in vector(InputIterator, InputIterator) from forward iterator
52+
std::vector<bool> vec((throwing_iterator<bool, std::forward_iterator_tag>()), throwing_iterator<bool, std::forward_iterator_tag>(2));
53+
} catch (int) {
54+
}
55+
check_new_delete_called();
56+
57+
try { // Throw in vector(InputIterator, InputIterator) from allocator
58+
bool a[] = {true, true};
59+
AllocVec vec(cpp17_input_iterator<bool*>(a), cpp17_input_iterator<bool*>(a + 2)); // throwing_allocator throws on default construction
60+
} catch (int) {
61+
}
62+
check_new_delete_called();
63+
64+
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator
65+
std::allocator<bool> alloc;
66+
std::vector<bool> vec(throwing_iterator<bool, std::input_iterator_tag>(), throwing_iterator<bool, std::input_iterator_tag>(2), alloc);
67+
} catch (int) {
68+
}
69+
check_new_delete_called();
70+
71+
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator
72+
std::allocator<bool> alloc;
73+
std::vector<bool> vec(throwing_iterator<bool, std::forward_iterator_tag>(), throwing_iterator<bool, std::forward_iterator_tag>(2), alloc);
74+
} catch (int) {
75+
}
76+
check_new_delete_called();
77+
78+
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
79+
bool a[] = {true, true};
80+
throwing_allocator<bool> alloc(false, true); // Throw on copy only
81+
AllocVec vec(cpp17_input_iterator<bool*>(a), cpp17_input_iterator<bool*>(a + 2), alloc);
82+
} catch (int) {
83+
}
84+
check_new_delete_called();
85+
86+
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
87+
bool a[] = {true, true};
88+
throwing_allocator<bool> alloc(false, true); // Throw on copy only
89+
AllocVec vec(forward_iterator<bool*>(a), forward_iterator<bool*>(a + 2), alloc);
90+
} catch (int) {
91+
}
92+
check_new_delete_called();
93+
94+
#if TEST_STD_VER >= 11
95+
try { // Throw in vector(const vector&, const allocator_type&) from allocator
96+
throwing_allocator<bool> alloc(false, false);
97+
AllocVec vec(alloc);
98+
vec.emplace_back(true);
99+
alloc.throw_on_copy_ = true;
100+
AllocVec vec2(vec, alloc);
101+
} catch (int) {
102+
}
103+
check_new_delete_called();
104+
105+
try { // Throw in vector(vector&&, const allocator_type&) from allocator
106+
throwing_allocator<bool> alloc(false, false);
107+
AllocVec vec(alloc);
108+
vec.emplace_back(true);
109+
alloc.throw_on_copy_ = true;
110+
AllocVec vec2(std::move(vec), alloc);
111+
} catch (int) {
112+
}
113+
check_new_delete_called();
114+
115+
try { // Throw in vector(initializer_list<value_type>, const allocator_type&) constructor from allocator
116+
throwing_allocator<bool> alloc(false, true); // Throw on copy only
117+
AllocVec vec({true, true}, alloc);
118+
} catch (int) {
119+
}
120+
check_new_delete_called();
121+
#endif // TEST_STD_VER >= 11
122+
123+
return 0;
124+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
#ifndef VECTOR_EXCEPTION_TEST_HELPER_H
10+
#define VECTOR_EXCEPTION_TEST_HELPER_H
11+
12+
#include "count_new.h"
13+
14+
template <class T>
15+
struct throwing_allocator {
16+
using value_type = T;
17+
using is_always_equal = std::false_type;
18+
19+
bool throw_on_copy_ = false;
20+
21+
throwing_allocator(bool throw_on_ctor = true, bool throw_on_copy = false) : throw_on_copy_(throw_on_copy) {
22+
if (throw_on_ctor)
23+
throw 0;
24+
}
25+
26+
template <class U>
27+
throwing_allocator(const throwing_allocator<U>& rhs) : throw_on_copy_(rhs.throw_on_copy_) {
28+
if (throw_on_copy_)
29+
throw 0;
30+
}
31+
32+
T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
33+
void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); }
34+
35+
template <class U>
36+
friend bool operator==(const throwing_allocator&, const throwing_allocator<U>&) { return true; }
37+
};
38+
39+
template <class T, class IterCat>
40+
struct throwing_iterator {
41+
using iterator_category = IterCat;
42+
using difference_type = std::ptrdiff_t;
43+
using value_type = T;
44+
using reference = T&;
45+
using pointer = T*;
46+
47+
int i_;
48+
T v_;
49+
50+
throwing_iterator(int i = 0, const T& v = T()) : i_(i), v_(v) {}
51+
52+
reference operator*() {
53+
if (i_ == 1)
54+
throw 1;
55+
return v_;
56+
}
57+
58+
friend bool operator==(const throwing_iterator& lhs, const throwing_iterator& rhs) { return lhs.i_ == rhs.i_; }
59+
friend bool operator!=(const throwing_iterator& lhs, const throwing_iterator& rhs) { return lhs.i_ != rhs.i_; }
60+
61+
throwing_iterator& operator++() {
62+
++i_;
63+
return *this;
64+
}
65+
66+
throwing_iterator operator++(int) {
67+
auto tmp = *this;
68+
++i_;
69+
return tmp;
70+
}
71+
};
72+
73+
inline void check_new_delete_called() {
74+
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
75+
assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
76+
assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
77+
assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
78+
}
79+
80+
#endif // VECTOR_EXCEPTION_TEST_HELPER_H

0 commit comments

Comments
 (0)