]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - test/support/debug_mode_helper.h
Vendor import of libc++ trunk r290819:
[FreeBSD/FreeBSD.git] / test / support / debug_mode_helper.h
1 //===----------------------------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef TEST_SUPPORT_DEBUG_MODE_HELPER_H
11 #define TEST_SUPPORT_DEBUG_MODE_HELPER_H
12
13 #ifndef _LIBCPP_DEBUG
14 #error _LIBCPP_DEBUG must be defined before including this header
15 #endif
16 #ifndef _LIBCPP_DEBUG_USE_EXCEPTIONS
17 #error _LIBCPP_DEBUG_USE_EXCEPTIONS must be defined before including this header
18 #endif
19
20 #include <ciso646>
21 #ifndef _LIBCPP_VERSION
22 #error This header may only be used for libc++ tests"
23 #endif
24
25 #include <__debug>
26 #include <utility>
27 #include <cstddef>
28 #include <cstdlib>
29 #include <cassert>
30
31 #include "test_macros.h"
32 #include "assert_checkpoint.h"
33 #include "test_allocator.h"
34
35 // These test make use of 'if constexpr'.
36 #if TEST_STD_VER <= 14
37 #error This header may only be used in C++17 and greater
38 #endif
39 #ifdef TEST_HAS_NO_EXCEPTIONS
40 #error These tests require exceptions
41 #endif
42
43 #ifndef __cpp_if_constexpr
44 #error These tests require if constexpr
45 #endif
46
47 /// Assert that the specified expression throws a libc++ debug exception.
48 #define CHECK_DEBUG_THROWS(...) assert((CheckDebugThrows( [&]() { __VA_ARGS__; } )))
49
50 template <class Func>
51 inline bool CheckDebugThrows(Func&& func) {
52   try {
53     func();
54   } catch (std::__libcpp_debug_exception const&) {
55     return true;
56   }
57   return false;
58 }
59
60 namespace IteratorDebugChecks {
61
62 enum ContainerType {
63   CT_None,
64   CT_String,
65   CT_Vector,
66   CT_VectorBool,
67   CT_List,
68   CT_Deque,
69   CT_ForwardList,
70   CT_Map,
71   CT_Set,
72   CT_MultiMap,
73   CT_MultiSet,
74   CT_UnorderedMap,
75   CT_UnorderedSet,
76   CT_UnorderedMultiMap,
77   CT_UnorderedMultiSet
78 };
79
80 constexpr bool isSequential(ContainerType CT) {
81   return CT_Vector >= CT && CT_ForwardList <= CT;
82 }
83
84 constexpr bool isAssociative(ContainerType CT) {
85   return CT_Map >= CT && CT_MultiSet <= CT;
86 }
87
88 constexpr bool isUnordered(ContainerType CT) {
89   return CT_UnorderedMap >= CT && CT_UnorderedMultiSet <= CT;
90 }
91
92 constexpr bool isSet(ContainerType CT) {
93   return CT == CT_Set
94       || CT == CT_MultiSet
95       || CT == CT_UnorderedSet
96       || CT == CT_UnorderedMultiSet;
97 }
98
99 constexpr bool isMap(ContainerType CT) {
100   return CT == CT_Map
101       || CT == CT_MultiMap
102       || CT == CT_UnorderedMap
103       || CT == CT_UnorderedMultiMap;
104 }
105
106 constexpr bool isMulti(ContainerType CT) {
107   return CT == CT_MultiMap
108       || CT == CT_MultiSet
109       || CT == CT_UnorderedMultiMap
110       || CT == CT_UnorderedMultiSet;
111 }
112
113 template <class Container, class ValueType = typename Container::value_type>
114 struct ContainerDebugHelper {
115   static_assert(std::is_constructible<ValueType, int>::value,
116                 "must be constructible from int");
117
118   static ValueType makeValueType(int val = 0, int = 0) {
119     return ValueType(val);
120   }
121 };
122
123 template <class Container>
124 struct ContainerDebugHelper<Container, char> {
125   static char makeValueType(int = 0, int = 0) {
126     return 'A';
127   }
128 };
129
130 template <class Container, class Key, class Value>
131 struct ContainerDebugHelper<Container, std::pair<const Key, Value> > {
132   using ValueType = std::pair<const Key, Value>;
133   static_assert(std::is_constructible<Key, int>::value,
134                 "must be constructible from int");
135   static_assert(std::is_constructible<Value, int>::value,
136                 "must be constructible from int");
137
138   static ValueType makeValueType(int key = 0, int val = 0) {
139     return ValueType(key, val);
140   }
141 };
142
143 template <class Container, ContainerType CT,
144     class Helper = ContainerDebugHelper<Container> >
145 struct BasicContainerChecks {
146   using value_type = typename Container::value_type;
147   using iterator = typename Container::iterator;
148   using const_iterator = typename Container::const_iterator;
149   using allocator_type = typename Container::allocator_type;
150   using traits = std::iterator_traits<iterator>;
151   using category = typename traits::iterator_category;
152
153   static_assert(std::is_same<test_allocator<value_type>, allocator_type>::value,
154                 "the container must use a test allocator");
155
156   static constexpr bool IsBiDir =
157       std::is_convertible<category, std::bidirectional_iterator_tag>::value;
158
159 public:
160   static void run() {
161     run_iterator_tests();
162     run_container_tests();
163     run_allocator_aware_tests();
164   }
165
166   static void run_iterator_tests() {
167     try {
168       TestNullIterators<iterator>();
169       TestNullIterators<const_iterator>();
170       if constexpr (IsBiDir) { DecrementBegin(); }
171       IncrementEnd();
172       DerefEndIterator();
173     } catch (...) {
174       assert(false && "uncaught debug exception");
175     }
176   }
177
178   static void run_container_tests() {
179     try {
180       CopyInvalidatesIterators();
181       MoveInvalidatesIterators();
182       if constexpr (CT != CT_ForwardList) {
183           EraseIter();
184           EraseIterIter();
185       }
186     } catch (...) {
187       assert(false && "uncaught debug exception");
188     }
189   }
190
191   static void run_allocator_aware_tests() {
192     try {
193       SwapNonEqualAllocators();
194       if constexpr (CT != CT_ForwardList) {
195           SwapInvalidatesIterators(); // FIXME: This should work
196       }
197     } catch (...) {
198       assert(false && "uncaught debug exception");
199     }
200   }
201
202   static Container makeContainer(int size, allocator_type A = allocator_type()) {
203     Container C(A);
204     if constexpr (CT == CT_ForwardList) {
205       for (int i = 0; i < size; ++i)
206         C.insert_after(C.before_begin(), Helper::makeValueType(i));
207     } else {
208       for (int i = 0; i < size; ++i)
209         C.insert(C.end(), Helper::makeValueType(i));
210       assert(C.size() == static_cast<std::size_t>(size));
211     }
212     return C;
213   }
214
215   static value_type makeValueType(int value) {
216     return Helper::makeValueType(value);
217   }
218
219 private:
220   // Iterator tests
221   template <class Iter>
222   static void TestNullIterators() {
223     CHECKPOINT("testing null iterator");
224     Iter it;
225     CHECK_DEBUG_THROWS( ++it );
226     CHECK_DEBUG_THROWS( it++ );
227     CHECK_DEBUG_THROWS( *it );
228     if constexpr (CT != CT_VectorBool) {
229       CHECK_DEBUG_THROWS( it.operator->() );
230     }
231     if constexpr (IsBiDir) {
232       CHECK_DEBUG_THROWS( --it );
233       CHECK_DEBUG_THROWS( it-- );
234     }
235   }
236
237   static void DecrementBegin() {
238     CHECKPOINT("testing decrement on begin");
239     Container C = makeContainer(1);
240     iterator i = C.end();
241     const_iterator ci = C.cend();
242     --i;
243     --ci;
244     assert(i == C.begin());
245     CHECK_DEBUG_THROWS( --i );
246     CHECK_DEBUG_THROWS( i-- );
247     CHECK_DEBUG_THROWS( --ci );
248     CHECK_DEBUG_THROWS( ci-- );
249   }
250
251   static void IncrementEnd() {
252     CHECKPOINT("testing increment on end");
253     Container C = makeContainer(1);
254     iterator i = C.begin();
255     const_iterator ci = C.begin();
256     ++i;
257     ++ci;
258     assert(i == C.end());
259     CHECK_DEBUG_THROWS( ++i );
260     CHECK_DEBUG_THROWS( i++ );
261     CHECK_DEBUG_THROWS( ++ci );
262     CHECK_DEBUG_THROWS( ci++ );
263   }
264
265   static void DerefEndIterator() {
266     CHECKPOINT("testing deref end iterator");
267     Container C = makeContainer(1);
268     iterator i = C.begin();
269     const_iterator ci = C.cbegin();
270     (void)*i; (void)*ci;
271     if constexpr (CT != CT_VectorBool) {
272       i.operator->();
273       ci.operator->();
274     }
275     ++i; ++ci;
276     assert(i == C.end());
277     CHECK_DEBUG_THROWS( *i );
278     CHECK_DEBUG_THROWS( *ci );
279     if constexpr (CT != CT_VectorBool) {
280       CHECK_DEBUG_THROWS( i.operator->() );
281       CHECK_DEBUG_THROWS( ci.operator->() );
282     }
283   }
284
285   // Container tests
286   static void CopyInvalidatesIterators() {
287     CHECKPOINT("copy invalidates iterators");
288     Container C1 = makeContainer(3);
289     iterator i = C1.begin();
290     Container C2 = C1;
291     if constexpr (CT == CT_ForwardList) {
292       iterator i_next = i;
293       ++i_next;
294       (void)*i_next;
295       CHECK_DEBUG_THROWS( C2.erase_after(i) );
296       C1.erase_after(i);
297       CHECK_DEBUG_THROWS( *i_next );
298     } else {
299       CHECK_DEBUG_THROWS( C2.erase(i) );
300       (void)*i;
301       C1.erase(i);
302       CHECK_DEBUG_THROWS( *i );
303     }
304   }
305
306   static void MoveInvalidatesIterators() {
307     CHECKPOINT("copy move invalidates iterators");
308     Container C1 = makeContainer(3);
309     iterator i = C1.begin();
310     Container C2 = std::move(C1);
311     (void) *i;
312     if constexpr (CT == CT_ForwardList) {
313       CHECK_DEBUG_THROWS( C1.erase_after(i) );
314       C2.erase_after(i);
315     } else {
316       CHECK_DEBUG_THROWS( C1.erase(i) );
317       C2.erase(i);
318       CHECK_DEBUG_THROWS(*i);
319     }
320   }
321
322   static void EraseIter() {
323     CHECKPOINT("testing erase invalidation");
324     Container C1 = makeContainer(2);
325     iterator it1 = C1.begin();
326     iterator it1_next = it1;
327     ++it1_next;
328     Container C2 = C1;
329     CHECK_DEBUG_THROWS( C2.erase(it1) ); // wrong container
330     CHECK_DEBUG_THROWS( C2.erase(C2.end()) ); // erase with end
331     C1.erase(it1_next);
332     CHECK_DEBUG_THROWS( C1.erase(it1_next) ); // invalidated iterator
333     C1.erase(it1);
334     CHECK_DEBUG_THROWS( C1.erase(it1) ); // invalidated iterator
335   }
336
337   static void EraseIterIter() {
338     CHECKPOINT("testing erase iter iter invalidation");
339     Container C1 = makeContainer(2);
340     iterator it1 = C1.begin();
341     iterator it1_next = it1;
342     ++it1_next;
343     Container C2 = C1;
344     iterator it2 = C2.begin();
345     iterator it2_next = it2;
346     ++it2_next;
347     CHECK_DEBUG_THROWS( C2.erase(it1, it1_next) ); // begin from wrong container
348     CHECK_DEBUG_THROWS( C2.erase(it1, it2_next) ); // end   from wrong container
349     CHECK_DEBUG_THROWS( C2.erase(it2, it1_next) ); // both  from wrong container
350     C2.erase(it2, it2_next);
351   }
352
353   // Allocator aware tests
354   static void SwapInvalidatesIterators() {
355     CHECKPOINT("testing swap invalidates iterators");
356     Container C1 = makeContainer(3);
357     Container C2 = makeContainer(3);
358     iterator it1 = C1.begin();
359     iterator it2 = C2.begin();
360     swap(C1, C2);
361     CHECK_DEBUG_THROWS( C1.erase(it1) );
362     C1.erase(it2);
363     //C2.erase(it1);
364     CHECK_DEBUG_THROWS( C1.erase(it1) );
365   }
366
367   static void SwapNonEqualAllocators() {
368     CHECKPOINT("testing swap with non-equal allocators");
369     Container C1 = makeContainer(3, allocator_type(1));
370     Container C2 = makeContainer(1, allocator_type(2));
371     Container C3 = makeContainer(2, allocator_type(2));
372     swap(C2, C3);
373     CHECK_DEBUG_THROWS( swap(C1, C2) );
374   }
375
376 private:
377   BasicContainerChecks() = delete;
378 };
379
380 } // namespace IteratorDebugChecks
381
382 #endif // TEST_SUPPORT_DEBUG_MODE_HELPER_H