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 // (bug report: https://llvm.org/PR58392)
12 // Check that vector constructors don't leak memory when an operation inside the constructor throws an exception
13 
14 #include <type_traits>
15 #include <vector>
16 
17 #include "count_new.h"
18 #include "test_iterators.h"
19 
20 template <class T>
21 struct Allocator {
22   using value_type      = T;
23   using is_always_equal = std::false_type;
24 
AllocatorAllocator25   Allocator(bool should_throw = true) {
26     if (should_throw)
27       throw 0;
28   }
29 
allocateAllocator30   T* allocate(int n) { return std::allocator<T>().allocate(n); }
deallocateAllocator31   void deallocate(T* ptr, int n) { std::allocator<T>().deallocate(ptr, n); }
32 
operator ==(const Allocator &,const Allocator &)33   friend bool operator==(const Allocator&, const Allocator&) { return false; }
34 };
35 
36 struct ThrowingT {
37   int* throw_after_n_ = nullptr;
ThrowingTThrowingT38   ThrowingT() { throw 0; }
39 
ThrowingTThrowingT40   ThrowingT(int& throw_after_n) : throw_after_n_(&throw_after_n) {
41     if (throw_after_n == 0)
42       throw 0;
43     --throw_after_n;
44   }
45 
ThrowingTThrowingT46   ThrowingT(const ThrowingT&) {
47     if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
48       throw 1;
49     --*throw_after_n_;
50   }
51 
operator =ThrowingT52   ThrowingT& operator=(const ThrowingT&) {
53     if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
54       throw 1;
55     --*throw_after_n_;
56     return *this;
57   }
58 };
59 
60 template <class IterCat>
61 struct Iterator {
62   using iterator_category = IterCat;
63   using difference_type   = std::ptrdiff_t;
64   using value_type        = int;
65   using reference         = int&;
66   using pointer           = int*;
67 
68   int i_;
IteratorIterator69   Iterator(int i = 0) : i_(i) {}
operator *Iterator70   int& operator*() {
71     if (i_ == 1)
72       throw 1;
73     return i_;
74   }
75 
operator ==(const Iterator & lhs,const Iterator & rhs)76   friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; }
77 
operator !=(const Iterator & lhs,const Iterator & rhs)78   friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; }
79 
operator ++Iterator80   Iterator& operator++() {
81     ++i_;
82     return *this;
83   }
84 
operator ++Iterator85   Iterator operator++(int) {
86     auto tmp = *this;
87     ++i_;
88     return tmp;
89   }
90 };
91 
check_new_delete_called()92 void check_new_delete_called() {
93   assert(globalMemCounter.new_called == globalMemCounter.delete_called);
94   assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
95   assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
96   assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
97 }
98 
main(int,char **)99 int main(int, char**) {
100   using AllocVec = std::vector<int, Allocator<int> >;
101   try { // vector()
102     AllocVec vec;
103   } catch (int) {
104   }
105   check_new_delete_called();
106 
107   try { // Throw in vector(size_type) from type
108     std::vector<ThrowingT> get_alloc(1);
109   } catch (int) {
110   }
111   check_new_delete_called();
112 
113 #if TEST_STD_VER >= 14
114   try { // Throw in vector(size_type, value_type) from type
115     int throw_after = 1;
116     ThrowingT v(throw_after);
117     std::vector<ThrowingT> get_alloc(1, v);
118   } catch (int) {
119   }
120   check_new_delete_called();
121 
122   try { // Throw in vector(size_type, const allocator_type&) from allocator
123     Allocator<int> alloc(false);
124     AllocVec get_alloc(0, alloc);
125   } catch (int) {
126   }
127   check_new_delete_called();
128 
129   try { // Throw in vector(size_type, const allocator_type&) from the type
130     std::vector<ThrowingT> vec(1, std::allocator<ThrowingT>());
131   } catch (int) {
132   }
133   check_new_delete_called();
134 #endif  // TEST_STD_VER >= 14
135 
136   try { // Throw in vector(InputIterator, InputIterator) from input iterator
137     std::vector<int> vec((Iterator<std::input_iterator_tag>()), Iterator<std::input_iterator_tag>(2));
138   } catch (int) {
139   }
140   check_new_delete_called();
141 
142   try { // Throw in vector(InputIterator, InputIterator) from forward iterator
143     std::vector<int> vec((Iterator<std::forward_iterator_tag>()), Iterator<std::forward_iterator_tag>(2));
144   } catch (int) {
145   }
146   check_new_delete_called();
147 
148   try { // Throw in vector(InputIterator, InputIterator) from allocator
149     int a[] = {1, 2};
150     AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2));
151   } catch (int) {
152   }
153   check_new_delete_called();
154 
155   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator
156     std::allocator<int> alloc;
157     std::vector<int> vec(Iterator<std::input_iterator_tag>(), Iterator<std::input_iterator_tag>(2), alloc);
158   } catch (int) {
159   }
160   check_new_delete_called();
161 
162   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator
163     std::allocator<int> alloc;
164     std::vector<int> vec(Iterator<std::forward_iterator_tag>(), Iterator<std::forward_iterator_tag>(2), alloc);
165   } catch (int) {
166   }
167   check_new_delete_called();
168 
169   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
170     int a[] = {1, 2};
171     Allocator<int> alloc(false);
172     AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2), alloc);
173   } catch (int) {
174   }
175   check_new_delete_called();
176 
177   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
178     int a[] = {1, 2};
179     Allocator<int> alloc(false);
180     AllocVec vec(forward_iterator<int*>(a), forward_iterator<int*>(a + 2), alloc);
181   } catch (int) {
182   }
183   check_new_delete_called();
184 
185   try { // Throw in vector(const vector&) from type
186     std::vector<ThrowingT> vec;
187     int throw_after = 0;
188     vec.emplace_back(throw_after);
189     auto vec2 = vec;
190   } catch (int) {
191   }
192   check_new_delete_called();
193 
194   try { // Throw in vector(const vector&, const allocator_type&) from type
195     std::vector<ThrowingT> vec;
196     int throw_after = 1;
197     vec.emplace_back(throw_after);
198     std::vector<ThrowingT> vec2(vec, std::allocator<int>());
199   } catch (int) {
200   }
201   check_new_delete_called();
202 
203   try { // Throw in vector(vector&&, const allocator_type&) from type
204     std::vector<ThrowingT, Allocator<ThrowingT> > vec(Allocator<ThrowingT>(false));
205     int throw_after = 1;
206     vec.emplace_back(throw_after);
207     std::vector<ThrowingT, Allocator<ThrowingT> > vec2(std::move(vec), Allocator<ThrowingT>(false));
208   } catch (int) {
209   }
210   check_new_delete_called();
211 
212 #if TEST_STD_VER >= 11
213   try { // Throw in vector(initializer_list<value_type>) from type
214     int throw_after = 1;
215     std::vector<ThrowingT> vec({ThrowingT(throw_after)});
216   } catch (int) {
217   }
218   check_new_delete_called();
219 
220   try { // Throw in vector(initializer_list<value_type>, const allocator_type&) constructor from type
221     int throw_after = 1;
222     std::vector<ThrowingT> vec({ThrowingT(throw_after)}, std::allocator<ThrowingT>());
223   } catch (int) {
224   }
225   check_new_delete_called();
226 #endif // TEST_STD_VER >= 11
227 
228   return 0;
229 }
230