Branch data Line data Source code
1 : : // Copyright (c) The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or https://opensource.org/license/mit/.
4 : :
5 : : #include <sync.h>
6 : : #include <test/util/common.h>
7 : : #include <util/not_null.h>
8 : :
9 : : #include <boost/test/unit_test.hpp>
10 : :
11 : : #include <memory>
12 : : #include <set>
13 : : #include <type_traits>
14 : : #include <unordered_set>
15 : :
16 : : BOOST_AUTO_TEST_SUITE(util_not_null_tests)
17 : :
18 : : static_assert(std::is_same_v<util::NotNullUniquePtr<int>, util::NotNull<std::unique_ptr<int>>>);
19 : : static_assert(std::is_same_v<util::NotNullSharedPtr<int>, util::NotNull<std::shared_ptr<int>>>);
20 : :
21 : : template <class P>
22 : : concept NotNullConstructible = requires { typename std::bool_constant<(util::NotNull{P{}}, true)>; };
23 : :
24 [ + - + - : 6 : BOOST_AUTO_TEST_CASE(check_ctor)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
25 : : {
26 : 0 : struct SomePtr {
27 : : constexpr bool operator==(std::nullptr_t) const { return false; }
28 : : };
29 : 0 : struct NullPtr {
30 : : constexpr bool operator==(std::nullptr_t) const { return true; }
31 : : };
32 : :
33 : 0 : static_assert(NotNullConstructible<SomePtr>);
34 : 0 : static_assert(!NotNullConstructible<NullPtr>);
35 : 0 : static_assert(!NotNullConstructible<std::nullptr_t>);
36 : 0 : static_assert(!NotNullConstructible<int*>);
37 : 0 : }
38 : :
39 : : template <class T>
40 : : concept NotNullType = requires { typename util::NotNull<T>; };
41 : :
42 [ + - + - : 6 : BOOST_AUTO_TEST_CASE(check_ctor_ref)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
43 : : {
44 : 0 : static_assert(!NotNullType<std::shared_ptr<int>&>);
45 : 0 : static_assert(!NotNullType<int*&>);
46 : 0 : }
47 : :
48 [ + - + - : 6 : BOOST_AUTO_TEST_CASE(check_ctor_conv)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
49 : : {
50 : 0 : using Ptr = std::shared_ptr<int>;
51 : 0 : using NNPtr = util::NotNullSharedPtr<int>;
52 : :
53 : : // For context: std::shared_ptr's raw pointer constructor is explicit
54 : 0 : static_assert(std::constructible_from<Ptr, int*>);
55 : 0 : static_assert(!std::convertible_to<int*, Ptr>);
56 : :
57 : : // util::NotNull's constructor is explicit
58 : 0 : static_assert(std::constructible_from<NNPtr, Ptr>);
59 : 0 : static_assert(!std::convertible_to<Ptr, NNPtr>);
60 : :
61 : : // For context: std::reference_wrapper's constructor is implicit
62 : 0 : using Ref = std::reference_wrapper<int>;
63 : 0 : static_assert(std::constructible_from<Ref, int&>);
64 : 0 : static_assert(std::convertible_to<int&, Ref>);
65 : :
66 : 0 : using UniqPtr = std::unique_ptr<int>;
67 : 0 : using NNUniqPtr = util::NotNullUniquePtr<int>;
68 : :
69 : : // NNPtr can be copied, but NNUniqPtr can not:
70 : 0 : static_assert(std::is_nothrow_constructible_v<NNPtr, NNPtr&>);
71 : 0 : static_assert(std::is_nothrow_constructible_v<NNPtr, const NNPtr&>);
72 : 0 : static_assert(std::is_nothrow_constructible_v<NNUniqPtr, NNUniqPtr&&>);
73 : 0 : static_assert(!std::is_constructible_v<NNUniqPtr, NNUniqPtr&>);
74 : 0 : static_assert(!std::is_constructible_v<NNUniqPtr, const NNUniqPtr&>);
75 : 0 : static_assert(!std::is_constructible_v<NNPtr, const NNUniqPtr&>);
76 : :
77 : : // NNPtr from NNUniqPtr may throw:
78 : 0 : static_assert(!std::is_nothrow_constructible_v<NNPtr, NNUniqPtr&&>);
79 : 0 : static_assert(std::is_constructible_v<NNPtr, NNUniqPtr&&>);
80 : :
81 : : // Extraction is implicit:
82 : 0 : static_assert(std::convertible_to<NNUniqPtr&&, UniqPtr>);
83 : 0 : static_assert(std::convertible_to<const NNPtr&, Ptr>);
84 : 0 : static_assert(std::convertible_to<NNPtr&&, Ptr>);
85 : 0 : }
86 : :
87 : : template <class Target, class Source>
88 : : concept NotNullCopyConvertible = requires {
89 : : typename std::bool_constant<([] {
90 : : util::NotNull<Source> copy_source{Source{}};
91 : : util::NotNull<Target>{copy_source};
92 : : return true;
93 : : }())>;
94 : : };
95 : :
96 : : template <class Target, class Source>
97 : : concept NotNullMoveConvertible = requires { typename std::bool_constant<(util::NotNull<Target>{util::NotNull<Source>{Source{}}}, true)>; };
98 : :
99 [ + - + - : 6 : BOOST_AUTO_TEST_CASE(check_cross_type_conversion)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
100 : : {
101 : 0 : struct SomePtr;
102 : 0 : struct NullPtr {
103 : : constexpr NullPtr(const SomePtr&) {}
104 : : constexpr bool operator==(std::nullptr_t) const { return true; }
105 : : };
106 : 0 : struct SomePtr {
107 : : constexpr explicit operator bool() const { return true; }
108 : : constexpr bool operator==(std::nullptr_t) const { return false; }
109 : : };
110 : :
111 : : // Assume checks failed conversions during constant evaluation.
112 : : #if !defined(_MSC_VER)
113 : : // MSVC does not reject this failed constant evaluation in a requires-expression.
114 : : // See https://github.com/bitcoin/bitcoin/issues/36153.
115 : 0 : static_assert(!NotNullCopyConvertible<NullPtr, SomePtr>);
116 : 0 : static_assert(!NotNullMoveConvertible<NullPtr, SomePtr>);
117 : : #endif
118 : 0 : }
119 : :
120 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_null_smart_pointer)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
121 : : {
122 : : // This conversion can throw, allowing the test hook to observe Assert.
123 : 1 : static_assert(!std::is_nothrow_constructible_v<util::NotNullSharedPtr<int>, std::unique_ptr<int>>);
124 [ + - ]: 1 : test_only_CheckFailuresAreExceptionsNotAborts mock_checks{};
125 [ + - - + : 3 : BOOST_CHECK_EXCEPTION(util::NotNullSharedPtr<int>{std::unique_ptr<int>{nullptr}}, NonFatalCheckError,
- - - - -
+ + - + -
+ - + - ]
126 : : HasReason{"Internal bug detected: m_ptr != nullptr"});
127 : 1 : }
128 : :
129 : : template <class P>
130 : : concept NullptrComparable = requires(P val) { val == nullptr; };
131 : : template <class P>
132 : : concept HasOperatorBool = requires(P val) { bool{val}; };
133 : :
134 [ + - + - : 6 : BOOST_AUTO_TEST_CASE(check_nullptr_compare)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
135 : : {
136 : : // The deleted ctor for nullptr also disallows nullptr compare:
137 : 0 : static_assert(NullptrComparable<std::shared_ptr<int>>);
138 : 0 : static_assert(!NullptrComparable<util::NotNullSharedPtr<int>>);
139 : :
140 : 0 : static_assert(HasOperatorBool<std::shared_ptr<int>>);
141 : 0 : static_assert(!HasOperatorBool<util::NotNullSharedPtr<int>>);
142 : 0 : }
143 : :
144 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_nocopy_ptr)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
145 : : {
146 : 1 : using NoCopyPtr = std::unique_ptr<int>;
147 : 1 : {
148 : 1 : NoCopyPtr no_copy_ptr{std::make_unique<int>()};
149 : 1 : util::NotNull no_copy{std::move(no_copy_ptr)};
150 : 1 : NoCopyPtr extract{std::move(no_copy)};
151 : 1 : (void)extract;
152 : 1 : }
153 : 1 : {
154 : 1 : util::NotNull no_copy{NoCopyPtr{std::make_unique<int>()}};
155 : 1 : NoCopyPtr extract{std::move(no_copy).get()};
156 : 1 : (void)extract;
157 : 1 : }
158 : 1 : }
159 : :
160 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_derived)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
161 : : {
162 : 2 : struct MyBase {
163 : 2 : virtual ~MyBase() = default;
164 : : };
165 : 2 : struct MyDerived : public MyBase {
166 : : };
167 : :
168 [ - + ]: 1 : util::NotNull p{std::make_shared<MyDerived>()};
169 [ + - - + ]: 1 : util::NotNull q{std::make_shared<MyBase>()};
170 [ - + ]: 1 : q = p;
171 : 1 : const bool same_ptr{q == p};
172 [ + - + - : 2 : BOOST_CHECK(same_ptr);
+ - ]
173 : :
174 [ + - ]: 1 : util::NotNull nn_derived{std::make_unique<MyDerived>()};
175 [ + - ]: 1 : util::NotNull nn_base{std::make_unique<MyBase>()};
176 : 1 : nn_base = std::move(nn_derived);
177 : 1 : std::unique_ptr<MyBase> base_inner{std::move(nn_base)};
178 [ + - + - ]: 3 : }
179 : :
180 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_move)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
181 : : {
182 : 1 : auto a{std::make_unique<int>()};
183 : 1 : util::NotNull box{std::move(a)};
184 [ + - ]: 1 : auto new_box{std::move(box)};
185 : :
186 [ + - ]: 1 : auto b{std::make_shared<int>()};
187 : 1 : util::NotNull arc{b};
188 [ + - ]: 1 : auto new_arc{std::move(arc)};
189 [ - + + - ]: 2 : }
190 : :
191 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_swap)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
192 : : {
193 : 1 : util::NotNull box_a{std::make_unique<int>(1)};
194 [ + - ]: 1 : util::NotNull box_b{std::make_unique<int>(2)};
195 : 1 : static_assert(std::is_nothrow_swappable_v<decltype(box_a)>);
196 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*box_a - *box_b, -1);
197 : 1 : swap(box_a, box_b);
198 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*box_a - *box_b, 1);
199 : 1 : }
200 : :
201 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_deref)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
202 : : {
203 : 1 : util::NotNull p{std::make_unique<int>(2)};
204 [ + - ]: 1 : *p = 3;
205 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*p, 3);
206 [ + - ]: 1 : *p.get() = 4;
207 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*p, 4);
208 : 1 : }
209 : :
210 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_compare_set)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
211 : : {
212 : 1 : auto a{std::make_shared<int>(1)};
213 [ + - ]: 1 : auto b{std::make_shared<int>(2)};
214 [ + - ]: 1 : std::set<util::NotNullSharedPtr<int>> uniq{};
215 [ + - ]: 1 : uniq.emplace(a);
216 [ + - ]: 1 : uniq.emplace(a);
217 [ + - ]: 1 : uniq.emplace(b);
218 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(uniq.size(), 2);
219 [ + - + - ]: 3 : }
220 : :
221 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_hash_set)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
222 : : {
223 : 1 : auto a{std::make_shared<int>(1)};
224 [ + - ]: 1 : auto b{std::make_shared<int>(2)};
225 [ + - ]: 1 : std::unordered_set<util::NotNullSharedPtr<int>> uniq{};
226 [ + - ]: 1 : uniq.emplace(a);
227 [ + - ]: 1 : uniq.emplace(a);
228 [ + - ]: 1 : uniq.emplace(b);
229 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(uniq.size(), 2);
230 [ + - + - ]: 3 : }
231 : :
232 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(check_tsa)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
233 : : {
234 : 2 : struct Thing {
235 : : Mutex mutex{};
236 : : int a GUARDED_BY(mutex){2};
237 : : };
238 [ - + ]: 1 : const util::NotNull p{std::make_shared<Thing>()};
239 : : // Clang thread safety annotation (TSA) may not be able to derive that
240 : : // operator-> and operator* refer to the same object. So use a named
241 : : // reference before taking the lock.
242 [ + - ]: 1 : Thing& ref{*p};
243 [ + - ]: 1 : LOCK(ref.mutex);
244 [ + - + - : 1 : BOOST_CHECK_EQUAL(ref.a, 2);
+ - ]
245 [ + - ]: 2 : }
246 : :
247 : : BOOST_AUTO_TEST_SUITE_END()
|