Coverage Report

Created: 2026-10-07 13:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/util/not_null.h
Line
Count
Source
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
#ifndef BITCOIN_UTIL_NOT_NULL_H
6
#define BITCOIN_UTIL_NOT_NULL_H
7
8
#include <attributes.h>
9
#include <util/check.h>
10
11
#include <compare>
12
#include <concepts>
13
#include <cstddef>
14
#include <functional>
15
#include <memory>
16
#include <type_traits>
17
#include <utility>
18
19
namespace util {
20
21
/// NotNull is intended to be used to denote that a smart pointer type can not
22
/// hold a nullptr value.
23
///
24
/// The type is more useful, the earlier in the lifetime of a pointer it is
25
/// used. So instead of passing an existing pointer into NotNull, it is
26
/// recommended to use the type when creating the smart pointer. E.g.:
27
///
28
/// util::NotNull p{std::make_shared<Thing>()};
29
///
30
/// This makes it obvious that a pointer really is not null and that the
31
/// constructor will not fail.
32
///
33
/// NotNull does *not* support raw pointers.
34
/// The C++ language provides raw references for this use case, and the C++
35
/// standard library provides std::reference_wrapper, where raw references can
36
/// not be used.
37
///
38
/// This type is movable, meaning that the inner pointer *is* null after a
39
/// move. Clang-tidy static analysis is used to enforce use-after-move
40
/// violations, so that the null should never be observed. Moreover, the
41
/// runtime Assume check in the get() member function will ensure the same.
42
template <class T>
43
    requires std::is_class_v<T> && requires(T t) { { t != nullptr } -> std::convertible_to<bool>; }
44
class NotNull
45
{
46
public:
47
    template <std::convertible_to<T> U>
48
        requires(!std::same_as<std::remove_cvref_t<U>, NotNull>)
49
    constexpr explicit NotNull(U&& u) noexcept(std::is_nothrow_constructible_v<T, U&&>)
50
75.4k
        : m_ptr(std::forward<U>(u))
51
75.4k
    {
52
75.4k
        Assert(m_ptr != nullptr);
53
75.4k
    }
util::NotNull<std::shared_ptr<ThreadPool>>::NotNull<std::shared_ptr<ThreadPool>>(T&&)
Line
Count
Source
50
1.37k
        : m_ptr(std::forward<U>(u))
51
1.37k
    {
52
1.37k
        Assert(m_ptr != nullptr);
53
1.37k
    }
util::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>(T&&)
Line
Count
Source
50
69.1k
        : m_ptr(std::forward<U>(u))
51
69.1k
    {
52
69.1k
        Assert(m_ptr != nullptr);
53
69.1k
    }
util::NotNull<std::shared_ptr<int>>::NotNull<std::unique_ptr<int, std::default_delete<int>>>(T&&)
Line
Count
Source
50
1
        : m_ptr(std::forward<U>(u))
51
1
    {
52
1
        Assert(m_ptr != nullptr);
53
1
    }
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::NotNull<std::unique_ptr<int, std::default_delete<int>>>(T&&)
Line
Count
Source
50
6
        : m_ptr(std::forward<U>(u))
51
6
    {
52
6
        Assert(m_ptr != nullptr);
53
6
    }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyDerived>>::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyDerived>>(T&&)
Line
Count
Source
50
1
        : m_ptr(std::forward<U>(u))
51
1
    {
52
1
        Assert(m_ptr != nullptr);
53
1
    }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyBase>>::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyBase>>(T&&)
Line
Count
Source
50
1
        : m_ptr(std::forward<U>(u))
51
1
    {
52
1
        Assert(m_ptr != nullptr);
53
1
    }
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyDerived, std::default_delete<util_not_null_tests::check_derived::test_method()::MyDerived>>>::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyDerived, std::default_delete<util_not_null_tests::check_derived::test_method()::MyDerived>>>(T&&)
Line
Count
Source
50
1
        : m_ptr(std::forward<U>(u))
51
1
    {
52
1
        Assert(m_ptr != nullptr);
53
1
    }
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>(T&&)
Line
Count
Source
50
1
        : m_ptr(std::forward<U>(u))
51
1
    {
52
1
        Assert(m_ptr != nullptr);
53
1
    }
util::NotNull<std::shared_ptr<int>>::NotNull<std::shared_ptr<int>&>(T&&)
Line
Count
Source
50
7
        : m_ptr(std::forward<U>(u))
51
7
    {
52
7
        Assert(m_ptr != nullptr);
53
7
    }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_tsa::test_method()::Thing>>::NotNull<std::shared_ptr<util_not_null_tests::check_tsa::test_method()::Thing>>(T&&)
Line
Count
Source
50
1
        : m_ptr(std::forward<U>(u))
51
1
    {
52
1
        Assert(m_ptr != nullptr);
53
1
    }
util::NotNull<std::unique_ptr<LevelDBContext, std::default_delete<LevelDBContext>>>::NotNull<std::unique_ptr<LevelDBContext, std::default_delete<LevelDBContext>>>(T&&)
Line
Count
Source
50
3.04k
        : m_ptr(std::forward<U>(u))
51
3.04k
    {
52
3.04k
        Assert(m_ptr != nullptr);
53
3.04k
    }
util::NotNull<std::unique_ptr<V2Transport, std::default_delete<V2Transport>>>::NotNull<std::unique_ptr<V2Transport, std::default_delete<V2Transport>>>(T&&)
Line
Count
Source
50
207
        : m_ptr(std::forward<U>(u))
51
207
    {
52
207
        Assert(m_ptr != nullptr);
53
207
    }
util::NotNull<std::unique_ptr<V1Transport, std::default_delete<V1Transport>>>::NotNull<std::unique_ptr<V1Transport, std::default_delete<V1Transport>>>(T&&)
Line
Count
Source
50
1.61k
        : m_ptr(std::forward<U>(u))
51
1.61k
    {
52
1.61k
        Assert(m_ptr != nullptr);
53
1.61k
    }
54
55
    template <class U>
56
        requires std::move_constructible<U> && std::constructible_from<T, U&&>
57
    constexpr explicit(!std::convertible_to<U, T>)
58
        NotNull(NotNull<U>&& other) noexcept(std::is_nothrow_move_constructible_v<U> && std::is_nothrow_constructible_v<T, U&&>)
59
1.82k
        : m_ptr(std::move(other).get())
60
1.82k
    {
61
1.82k
        Assume(m_ptr != nullptr);
62
1.82k
    }
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyDerived, std::default_delete<util_not_null_tests::check_derived::test_method()::MyDerived>>>(util::NotNull<T>&&)
Line
Count
Source
59
1
        : m_ptr(std::move(other).get())
60
1
    {
61
1
        Assume(m_ptr != nullptr);
62
1
    }
util::NotNull<std::unique_ptr<Transport, std::default_delete<Transport>>>::NotNull<std::unique_ptr<V2Transport, std::default_delete<V2Transport>>>(util::NotNull<T>&&)
Line
Count
Source
59
207
        : m_ptr(std::move(other).get())
60
207
    {
61
207
        Assume(m_ptr != nullptr);
62
207
    }
util::NotNull<std::unique_ptr<Transport, std::default_delete<Transport>>>::NotNull<std::unique_ptr<V1Transport, std::default_delete<V1Transport>>>(util::NotNull<T>&&)
Line
Count
Source
59
1.61k
        : m_ptr(std::move(other).get())
60
1.61k
    {
61
1.61k
        Assume(m_ptr != nullptr);
62
1.61k
    }
63
64
    template <class U>
65
        requires std::convertible_to<const U&, T>
66
    constexpr NotNull(const NotNull<U>& other) noexcept(std::is_nothrow_constructible_v<T, const U&>)
67
1
        : m_ptr(other.get())
68
1
    {
69
1
        Assume(m_ptr != nullptr);
70
1
    }
71
72
    NotNull(std::nullptr_t) = delete;
73
    NotNull& operator=(std::nullptr_t) = delete;
74
75
2.74k
    constexpr NotNull(NotNull&&) = default;
util::NotNull<std::shared_ptr<ThreadPool>>::NotNull(util::NotNull<std::shared_ptr<ThreadPool>>&&)
Line
Count
Source
75
2.74k
    constexpr NotNull(NotNull&&) = default;
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::NotNull(util::NotNull<std::unique_ptr<int, std::default_delete<int>>>&&)
Line
Count
Source
75
2
    constexpr NotNull(NotNull&&) = default;
util::NotNull<std::shared_ptr<int>>::NotNull(util::NotNull<std::shared_ptr<int>>&&)
Line
Count
Source
75
1
    constexpr NotNull(NotNull&&) = default;
76
4
    constexpr NotNull& operator=(NotNull&&) = default;
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyBase>>::operator=(util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyBase>>&&)
Line
Count
Source
76
1
    constexpr NotNull& operator=(NotNull&&) = default;
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>::operator=(util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>&&)
Line
Count
Source
76
1
    constexpr NotNull& operator=(NotNull&&) = default;
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::operator=(util::NotNull<std::unique_ptr<int, std::default_delete<int>>>&&)
Line
Count
Source
76
2
    constexpr NotNull& operator=(NotNull&&) = default;
77
2
    constexpr NotNull(const NotNull&) = default;
78
    constexpr NotNull& operator=(const NotNull&) = default;
79
80
13.7M
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util::NotNull<std::unique_ptr<LevelDBContext, std::default_delete<LevelDBContext>>>::get() const &
Line
Count
Source
80
11.0M
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util::NotNull<std::shared_ptr<ThreadPool>>::get() const &
Line
Count
Source
80
119k
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util::NotNull<std::unique_ptr<Transport, std::default_delete<Transport>>>::get() const &
Line
Count
Source
80
2.49M
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>::get() const &
Line
Count
Source
80
64.1k
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyDerived>>::get() const &
Line
Count
Source
80
2
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyBase>>::get() const &
Line
Count
Source
80
1
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::get() const &
Line
Count
Source
80
8
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_tsa::test_method()::Thing>>::get() const &
Line
Count
Source
80
1
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
util::NotNull<std::shared_ptr<int>>::get() const &
Line
Count
Source
80
14
    constexpr const T& get() const& noexcept LIFETIMEBOUND { return Assume(m_ptr); }
Unexecuted instantiation: util_not_null_tests.cpp:util::NotNull<util_not_null_tests::check_cross_type_conversion::test_method()::SomePtr>::get() const &
81
    constexpr T get() && noexcept(std::is_nothrow_move_constructible_v<T>)
82
        requires std::move_constructible<T>
83
48.3k
    {
84
48.3k
        return std::move(Assume(m_ptr));
85
48.3k
    }
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::get() && requires std::move_constructible<T>
Line
Count
Source
83
2
    {
84
2
        return std::move(Assume(m_ptr));
85
2
    }
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyDerived, std::default_delete<util_not_null_tests::check_derived::test_method()::MyDerived>>>::get() && requires std::move_constructible<T>
Line
Count
Source
83
1
    {
84
1
        return std::move(Assume(m_ptr));
85
1
    }
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>::get() && requires std::move_constructible<T>
Line
Count
Source
83
1
    {
84
1
        return std::move(Assume(m_ptr));
85
1
    }
Unexecuted instantiation: util_not_null_tests.cpp:util::NotNull<util_not_null_tests::check_cross_type_conversion::test_method()::SomePtr>::get() && requires std::move_constructible<T>
util::NotNull<std::unique_ptr<V2Transport, std::default_delete<V2Transport>>>::get() && requires std::move_constructible<T>
Line
Count
Source
83
207
    {
84
207
        return std::move(Assume(m_ptr));
85
207
    }
util::NotNull<std::unique_ptr<V1Transport, std::default_delete<V1Transport>>>::get() && requires std::move_constructible<T>
Line
Count
Source
83
1.61k
    {
84
1.61k
        return std::move(Assume(m_ptr));
85
1.61k
    }
util::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>::get() && requires std::move_constructible<T>
Line
Count
Source
83
46.5k
    {
84
46.5k
        return std::move(Assume(m_ptr));
85
46.5k
    }
86
87
2.68M
    constexpr decltype(auto) operator->() const LIFETIMEBOUND { return get(); }
util::NotNull<std::shared_ptr<ThreadPool>>::operator->() const
Line
Count
Source
87
119k
    constexpr decltype(auto) operator->() const LIFETIMEBOUND { return get(); }
util::NotNull<std::unique_ptr<Transport, std::default_delete<Transport>>>::operator->() const
Line
Count
Source
87
2.49M
    constexpr decltype(auto) operator->() const LIFETIMEBOUND { return get(); }
util::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>::operator->() const
Line
Count
Source
87
64.1k
    constexpr decltype(auto) operator->() const LIFETIMEBOUND { return get(); }
88
11.0M
    constexpr decltype(auto) operator*() const LIFETIMEBOUND { return *get(); }
util::NotNull<std::unique_ptr<LevelDBContext, std::default_delete<LevelDBContext>>>::operator*() const
Line
Count
Source
88
11.0M
    constexpr decltype(auto) operator*() const LIFETIMEBOUND { return *get(); }
util::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>::operator*() const
Line
Count
Source
88
5
    constexpr decltype(auto) operator*() const LIFETIMEBOUND { return *get(); }
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::operator*() const
Line
Count
Source
88
7
    constexpr decltype(auto) operator*() const LIFETIMEBOUND { return *get(); }
util_not_null_tests.cpp:util::NotNull<std::shared_ptr<util_not_null_tests::check_tsa::test_method()::Thing>>::operator*() const
Line
Count
Source
88
1
    constexpr decltype(auto) operator*() const LIFETIMEBOUND { return *get(); }
89
90
    constexpr operator T() const& noexcept(std::is_nothrow_copy_constructible_v<T>)
91
        requires std::copy_constructible<T>
92
    {
93
        return get();
94
    }
95
    constexpr operator T() && noexcept(std::is_nothrow_move_constructible_v<T>)
96
        requires std::move_constructible<T>
97
46.5k
    {
98
46.5k
        return std::move(*this).get();
99
46.5k
    }
util::NotNull<std::unique_ptr<int, std::default_delete<int>>>::operator std::unique_ptr<int, std::default_delete<int>>() && requires std::move_constructible<T>
Line
Count
Source
97
1
    {
98
1
        return std::move(*this).get();
99
1
    }
util_not_null_tests.cpp:util::NotNull<std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>>::operator std::unique_ptr<util_not_null_tests::check_derived::test_method()::MyBase, std::default_delete<util_not_null_tests::check_derived::test_method()::MyBase>>() && requires std::move_constructible<T>
Line
Count
Source
97
1
    {
98
1
        return std::move(*this).get();
99
1
    }
util::NotNull<std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>>::operator std::unique_ptr<CTxMemPool::ChangeSet, std::default_delete<CTxMemPool::ChangeSet>>() && requires std::move_constructible<T>
Line
Count
Source
97
46.5k
    {
98
46.5k
        return std::move(*this).get();
99
46.5k
    }
100
101
    template <class U>
102
    constexpr auto operator<=>(const NotNull<U>& other) const
103
4
    {
104
4
        return std::compare_three_way{}(get(), other.get());
105
4
    }
106
    template <class U>
107
    constexpr bool operator==(const NotNull<U>& other) const
108
2
    {
109
2
        return get() == other.get();
110
2
    }
util_not_null_tests.cpp:bool util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyBase>>::operator==<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyDerived>>(util::NotNull<std::shared_ptr<util_not_null_tests::check_derived::test_method()::MyDerived>> const&) const
Line
Count
Source
108
1
    {
109
1
        return get() == other.get();
110
1
    }
bool util::NotNull<std::shared_ptr<int>>::operator==<std::shared_ptr<int>>(util::NotNull<std::shared_ptr<int>> const&) const
Line
Count
Source
108
1
    {
109
1
        return get() == other.get();
110
1
    }
111
112
private:
113
    T m_ptr;
114
};
115
116
template <class T>
117
NotNull(T) -> NotNull<T>;
118
119
template <typename T, typename Deleter = std::default_delete<T>>
120
using NotNullUniquePtr = NotNull<std::unique_ptr<T, Deleter>>;
121
122
template <typename T>
123
using NotNullSharedPtr = NotNull<std::shared_ptr<T>>;
124
125
} // namespace util
126
127
template <class T>
128
struct std::hash<util::NotNull<T>> {
129
    std::size_t operator()(const util::NotNull<T>& v) const noexcept
130
4
    {
131
4
        return std::hash<T>{}(v.get());
132
4
    }
133
};
134
135
#endif // BITCOIN_UTIL_NOT_NULL_H