tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NotNull.h (15864B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_NotNull_h
      8 #define mozilla_NotNull_h
      9 
     10 // It's often unclear if a particular pointer, be it raw (T*) or smart
     11 // (RefPtr<T>, nsCOMPtr<T>, etc.) can be null. This leads to missing null
     12 // checks (which can cause crashes) and unnecessary null checks (which clutter
     13 // the code).
     14 //
     15 // C++ has a built-in alternative that avoids these problems: references. This
     16 // module defines another alternative, NotNull, which can be used in cases
     17 // where references are not suitable.
     18 //
     19 // In the comments below we use the word "handle" to cover all varieties of
     20 // pointers and references.
     21 //
     22 // References
     23 // ----------
     24 // References are always non-null. (You can do |T& r = *p;| where |p| is null,
     25 // but that's undefined behaviour. C++ doesn't provide any built-in, ironclad
     26 // guarantee of non-nullness.)
     27 //
     28 // A reference works well when you need a temporary handle to an existing
     29 // single object, e.g. for passing a handle to a function, or as a local handle
     30 // within another object. (In Rust parlance, this is a "borrow".)
     31 //
     32 // A reference is less appropriate in the following cases.
     33 //
     34 // - As a primary handle to an object. E.g. code such as this is possible but
     35 //   strange: |T& t = *new T(); ...; delete &t;|
     36 //
     37 // - As a handle to an array. It's common for |T*| to refer to either a single
     38 //   |T| or an array of |T|, but |T&| cannot refer to an array of |T| because
     39 //   you can't index off a reference (at least, not without first converting it
     40 //   to a pointer).
     41 //
     42 // - When the handle identity is meaningful, e.g. if you have a hashtable of
     43 //   handles, because you have to use |&| on the reference to convert it to a
     44 //   pointer.
     45 //
     46 // - Some people don't like using non-const references as function parameters,
     47 //   because it is not clear at the call site that the argument might be
     48 //   modified.
     49 //
     50 // - When you need "smart" behaviour. E.g. we lack reference equivalents to
     51 //   RefPtr and nsCOMPtr.
     52 //
     53 // - When interfacing with code that uses pointers a lot, sometimes using a
     54 //   reference just feels like an odd fit.
     55 //
     56 // Furthermore, a reference is impossible in the following cases.
     57 //
     58 // - When the handle is rebound to another object. References don't allow this.
     59 //
     60 // - When the handle has type |void|. |void&| is not allowed.
     61 //
     62 // NotNull is an alternative that can be used in any of the above cases except
     63 // for the last one, where the handle type is |void|. See below.
     64 
     65 #include <cstddef>
     66 #include <type_traits>
     67 #include <utility>
     68 
     69 #include "mozilla/Assertions.h"
     70 
     71 namespace mozilla {
     72 
     73 namespace detail {
     74 template <typename T>
     75 struct CopyablePtr {
     76  T mPtr;
     77 
     78  template <typename U>
     79  explicit CopyablePtr(U&& aPtr) : mPtr{std::forward<U>(aPtr)} {}
     80 
     81  template <typename U>
     82  explicit CopyablePtr(CopyablePtr<U> aPtr) : mPtr{std::move(aPtr.mPtr)} {}
     83 };
     84 }  // namespace detail
     85 
     86 template <typename T>
     87 class MovingNotNull;
     88 
     89 // NotNull can be used to wrap a "base" pointer (raw or smart) to indicate it
     90 // is not null. Some examples:
     91 //
     92 // - NotNull<char*>
     93 // - NotNull<RefPtr<Event>>
     94 // - NotNull<nsCOMPtr<Event>>
     95 // - NotNull<UniquePtr<Pointee>>
     96 //
     97 // NotNull has the following notable properties.
     98 //
     99 // - It has zero space overhead.
    100 //
    101 // - It must be initialized explicitly. There is no default initialization.
    102 //
    103 // - It auto-converts to the base pointer type.
    104 //
    105 // - It does not auto-convert from a base pointer. Implicit conversion from a
    106 //   less-constrained type (e.g. T*) to a more-constrained type (e.g.
    107 //   NotNull<T*>) is dangerous. Creation and assignment from a base pointer can
    108 //   only be done with WrapNotNull() or MakeNotNull<>(), which makes them
    109 //   impossible to overlook, both when writing and reading code.
    110 //
    111 // - When initialized (or assigned) it is checked, and if it is null we abort.
    112 //   This guarantees that it cannot be null.
    113 //
    114 // - |operator bool()| is deleted. This means you cannot check a NotNull in a
    115 //   boolean context, which eliminates the possibility of unnecessary null
    116 //   checks.
    117 //
    118 // - It is not movable, but copyable if the base pointer type is copyable. It
    119 //   may be used together with MovingNotNull to avoid unnecessary copies or when
    120 //   the base pointer type is not copyable (such as UniquePtr<T>).
    121 //
    122 template <typename T>
    123 class NotNull {
    124  template <typename U>
    125  friend constexpr NotNull<U> WrapNotNull(U aBasePtr);
    126  template <typename U>
    127  friend constexpr NotNull<U> WrapNotNullUnchecked(U aBasePtr);
    128  template <typename U, typename... Args>
    129  friend constexpr NotNull<U> MakeNotNull(Args&&... aArgs);
    130  template <typename U>
    131  friend class NotNull;
    132 
    133  detail::CopyablePtr<T> mBasePtr;
    134 
    135  // This constructor is only used by WrapNotNull() and MakeNotNull<U>().
    136  template <typename U>
    137  constexpr explicit NotNull(U aBasePtr) : mBasePtr(T{std::move(aBasePtr)}) {
    138    static_assert(sizeof(T) == sizeof(NotNull<T>),
    139                  "NotNull must have zero space overhead.");
    140    static_assert(offsetof(NotNull<T>, mBasePtr) == 0,
    141                  "mBasePtr must have zero offset.");
    142  }
    143 
    144 public:
    145  // Disallow default construction.
    146  NotNull() = delete;
    147 
    148  // Construct/assign from another NotNull with a compatible base pointer type.
    149  template <typename U,
    150            typename = std::enable_if_t<std::is_convertible_v<const U&, T>>>
    151  constexpr MOZ_IMPLICIT NotNull(const NotNull<U>& aOther)
    152      : mBasePtr(aOther.mBasePtr) {}
    153 
    154  template <typename U,
    155            typename = std::enable_if_t<std::is_convertible_v<U&&, T>>>
    156  constexpr MOZ_IMPLICIT NotNull(MovingNotNull<U>&& aOther)
    157      : mBasePtr(std::move(aOther).unwrapBasePtr()) {}
    158 
    159  // Disallow null checks, which are unnecessary for this type.
    160  explicit operator bool() const = delete;
    161 
    162  // Explicit conversion to a base pointer. Use only to resolve ambiguity or to
    163  // get a castable pointer.
    164  constexpr const T& get() const { return mBasePtr.mPtr; }
    165 
    166  // Implicit conversion to a base pointer. Preferable to get().
    167  constexpr operator const T&() const { return get(); }
    168 
    169  // Implicit conversion to a raw pointer from const lvalue-reference if
    170  // supported by the base pointer (for RefPtr<T> -> T* compatibility).
    171  template <typename U,
    172            std::enable_if_t<!std::is_pointer_v<T> &&
    173                                 std::is_convertible_v<const T&, U*>,
    174                             int> = 0>
    175  constexpr operator U*() const& {
    176    return get();
    177  }
    178 
    179  // Don't allow implicit conversions to raw pointers from rvalue-references.
    180  template <typename U,
    181            std::enable_if_t<!std::is_pointer_v<T> &&
    182                                 std::is_convertible_v<const T&, U*> &&
    183                                 !std::is_convertible_v<const T&&, U*>,
    184                             int> = 0>
    185  constexpr operator U*() const&& = delete;
    186 
    187  // Dereference operators.
    188  constexpr auto* operator->() const MOZ_NONNULL_RETURN {
    189    return mBasePtr.mPtr.operator->();
    190  }
    191  constexpr decltype(*mBasePtr.mPtr) operator*() const {
    192    return *mBasePtr.mPtr;
    193  }
    194 
    195  // NotNull can be copied, but not moved. Moving a NotNull with a smart base
    196  // pointer would leave a nullptr NotNull behind. The move operations must not
    197  // be explicitly deleted though, since that would cause overload resolution to
    198  // fail in situations where a copy is possible.
    199  NotNull(const NotNull&) = default;
    200  NotNull& operator=(const NotNull&) = default;
    201 };
    202 
    203 // Specialization for T* to allow adding MOZ_NONNULL_RETURN attributes.
    204 template <typename T>
    205 class NotNull<T*> {
    206  template <typename U>
    207  friend constexpr NotNull<U> WrapNotNull(U aBasePtr);
    208  template <typename U>
    209  friend constexpr NotNull<U*> WrapNotNullUnchecked(U* aBasePtr);
    210  template <typename U, typename... Args>
    211  friend constexpr NotNull<U> MakeNotNull(Args&&... aArgs);
    212  template <typename U>
    213  friend class NotNull;
    214 
    215  T* mBasePtr;
    216 
    217  // This constructor is only used by WrapNotNull() and MakeNotNull<U>().
    218  template <typename U>
    219  constexpr explicit NotNull(U* aBasePtr) : mBasePtr(aBasePtr) {}
    220 
    221 public:
    222  // Disallow default construction.
    223  NotNull() = delete;
    224 
    225  // Construct/assign from another NotNull with a compatible base pointer type.
    226  template <typename U,
    227            typename = std::enable_if_t<std::is_convertible_v<const U&, T*>>>
    228  constexpr MOZ_IMPLICIT NotNull(const NotNull<U>& aOther)
    229      : mBasePtr(aOther.get()) {
    230    static_assert(sizeof(T*) == sizeof(NotNull<T*>),
    231                  "NotNull must have zero space overhead.");
    232    static_assert(offsetof(NotNull<T*>, mBasePtr) == 0,
    233                  "mBasePtr must have zero offset.");
    234  }
    235 
    236  template <typename U,
    237            typename = std::enable_if_t<std::is_convertible_v<U&&, T*>>>
    238  constexpr MOZ_IMPLICIT NotNull(MovingNotNull<U>&& aOther)
    239      : mBasePtr(NotNull{std::move(aOther)}) {}
    240 
    241  // Disallow null checks, which are unnecessary for this type.
    242  explicit operator bool() const = delete;
    243 
    244  // Explicit conversion to a base pointer. Use only to resolve ambiguity or to
    245  // get a castable pointer.
    246  constexpr T* get() const MOZ_NONNULL_RETURN { return mBasePtr; }
    247 
    248  // Implicit conversion to a base pointer. Preferable to get().
    249  constexpr operator T*() const MOZ_NONNULL_RETURN { return get(); }
    250 
    251  // Dereference operators.
    252  constexpr T* operator->() const MOZ_NONNULL_RETURN { return get(); }
    253  constexpr T& operator*() const { return *mBasePtr; }
    254 };
    255 
    256 template <typename T>
    257 constexpr NotNull<T> WrapNotNull(T aBasePtr) {
    258  MOZ_RELEASE_ASSERT(aBasePtr);
    259  return NotNull<T>{std::move(aBasePtr)};
    260 }
    261 
    262 // WrapNotNullUnchecked should only be used in situations, where it is
    263 // statically known that aBasePtr is non-null, and redundant release assertions
    264 // should be avoided. It is only defined for raw base pointers, since it is only
    265 // needed for those right now. There is no fundamental reason not to allow
    266 // arbitrary base pointers here.
    267 template <typename T>
    268 constexpr NotNull<T> WrapNotNullUnchecked(T aBasePtr) {
    269  return NotNull<T>{std::move(aBasePtr)};
    270 }
    271 
    272 template <typename T>
    273 MOZ_NONNULL(1)
    274 constexpr NotNull<T*> WrapNotNullUnchecked(T* const aBasePtr) {
    275 #if defined(__clang__)
    276 #  pragma clang diagnostic push
    277 #  pragma clang diagnostic ignored "-Wpointer-bool-conversion"
    278 #elif defined(__GNUC__)
    279 #  pragma GCC diagnostic push
    280 #  pragma GCC diagnostic ignored "-Wnonnull-compare"
    281 #endif
    282  MOZ_ASSERT(aBasePtr);
    283 #if defined(__clang__)
    284 #  pragma clang diagnostic pop
    285 #elif defined(__GNUC__)
    286 #  pragma GCC diagnostic pop
    287 #endif
    288  return NotNull<T*>{aBasePtr};
    289 }
    290 
    291 // A variant of NotNull that can be used as a return value or parameter type and
    292 // moved into both NotNull and non-NotNull targets. This is not possible with
    293 // NotNull, as it is not movable. MovingNotNull can therefore not guarantee it
    294 // is always non-nullptr, but it can't be dereferenced, and there are debug
    295 // assertions that ensure it is only moved once.
    296 template <typename T>
    297 class MOZ_NON_AUTOABLE MovingNotNull {
    298  template <typename U>
    299  friend constexpr MovingNotNull<U> WrapMovingNotNullUnchecked(U aBasePtr);
    300 
    301  T mBasePtr;
    302 #ifdef DEBUG
    303  bool mConsumed = false;
    304 #endif
    305 
    306  // This constructor is only used by WrapNotNull() and MakeNotNull<U>().
    307  template <typename U>
    308  constexpr explicit MovingNotNull(U aBasePtr) : mBasePtr{std::move(aBasePtr)} {
    309 #ifndef DEBUG
    310    static_assert(sizeof(T) == sizeof(MovingNotNull<T>),
    311                  "NotNull must have zero space overhead.");
    312 #endif
    313    static_assert(offsetof(MovingNotNull<T>, mBasePtr) == 0,
    314                  "mBasePtr must have zero offset.");
    315  }
    316 
    317 public:
    318  MovingNotNull() = delete;
    319 
    320  MOZ_IMPLICIT MovingNotNull(const NotNull<T>& aSrc) : mBasePtr(aSrc.get()) {}
    321 
    322  template <typename U,
    323            typename = std::enable_if_t<std::is_convertible_v<U, T>>>
    324  MOZ_IMPLICIT MovingNotNull(const NotNull<U>& aSrc) : mBasePtr(aSrc.get()) {}
    325 
    326  template <typename U,
    327            typename = std::enable_if_t<std::is_convertible_v<U, T>>>
    328  MOZ_IMPLICIT MovingNotNull(MovingNotNull<U>&& aSrc)
    329      : mBasePtr(std::move(aSrc).unwrapBasePtr()) {}
    330 
    331  MOZ_IMPLICIT operator T() && { return std::move(*this).unwrapBasePtr(); }
    332 
    333  MOZ_IMPLICIT operator NotNull<T>() && { return std::move(*this).unwrap(); }
    334 
    335  NotNull<T> unwrap() && {
    336    return WrapNotNullUnchecked(std::move(*this).unwrapBasePtr());
    337  }
    338 
    339  T unwrapBasePtr() && {
    340 #ifdef DEBUG
    341    MOZ_ASSERT(!mConsumed);
    342    mConsumed = true;
    343 #endif
    344    return std::move(mBasePtr);
    345  }
    346 
    347  MovingNotNull(MovingNotNull&&) = default;
    348  MovingNotNull& operator=(MovingNotNull&&) = default;
    349 };
    350 
    351 template <typename T>
    352 constexpr MovingNotNull<T> WrapMovingNotNullUnchecked(T aBasePtr) {
    353  return MovingNotNull<T>{std::move(aBasePtr)};
    354 }
    355 
    356 template <typename T>
    357 constexpr MovingNotNull<T> WrapMovingNotNull(T aBasePtr) {
    358  MOZ_RELEASE_ASSERT(aBasePtr);
    359  return WrapMovingNotNullUnchecked(std::move(aBasePtr));
    360 }
    361 
    362 namespace detail {
    363 
    364 // Extract the pointed-to type from a pointer type (be it raw or smart).
    365 // The default implementation uses the dereferencing operator of the pointer
    366 // type to find what it's pointing to.
    367 template <typename Pointer>
    368 struct PointedTo {
    369  // Remove the reference that dereferencing operators may return.
    370  using Type = std::remove_reference_t<decltype(*std::declval<Pointer>())>;
    371  using NonConstType = std::remove_const_t<Type>;
    372 };
    373 
    374 // Specializations for raw pointers.
    375 // This is especially required because VS 2017 15.6 (March 2018) started
    376 // rejecting the above `decltype(*std::declval<Pointer>())` trick for raw
    377 // pointers.
    378 // See bug 1443367.
    379 template <typename T>
    380 struct PointedTo<T*> {
    381  using Type = T;
    382  using NonConstType = T;
    383 };
    384 
    385 template <typename T>
    386 struct PointedTo<const T*> {
    387  using Type = const T;
    388  using NonConstType = T;
    389 };
    390 
    391 }  // namespace detail
    392 
    393 // Allocate an object with infallible new, and wrap its pointer in NotNull.
    394 // |MakeNotNull<Ptr<Ob>>(args...)| will run |new Ob(args...)|
    395 // and return NotNull<Ptr<Ob>>.
    396 template <typename T, typename... Args>
    397 constexpr NotNull<T> MakeNotNull(Args&&... aArgs) {
    398  using Pointee = typename detail::PointedTo<T>::NonConstType;
    399  static_assert(!std::is_array_v<Pointee>,
    400                "MakeNotNull cannot construct an array");
    401  return NotNull<T>(new Pointee(std::forward<Args>(aArgs)...));
    402 }
    403 
    404 // Compare two NotNulls.
    405 template <typename T, typename U>
    406 constexpr bool operator==(const NotNull<T>& aLhs, const NotNull<U>& aRhs) {
    407  return aLhs.get() == aRhs.get();
    408 }
    409 template <typename T, typename U>
    410 constexpr bool operator!=(const NotNull<T>& aLhs, const NotNull<U>& aRhs) {
    411  return aLhs.get() != aRhs.get();
    412 }
    413 
    414 // Compare a NotNull to a base pointer.
    415 template <typename T, typename U>
    416 constexpr bool operator==(const NotNull<T>& aLhs, const U& aRhs) {
    417  return aLhs.get() == aRhs;
    418 }
    419 template <typename T, typename U>
    420 constexpr bool operator!=(const NotNull<T>& aLhs, const U& aRhs) {
    421  return aLhs.get() != aRhs;
    422 }
    423 
    424 // Compare a base pointer to a NotNull.
    425 template <typename T, typename U>
    426 constexpr bool operator==(const T& aLhs, const NotNull<U>& aRhs) {
    427  return aLhs == aRhs.get();
    428 }
    429 template <typename T, typename U>
    430 constexpr bool operator!=(const T& aLhs, const NotNull<U>& aRhs) {
    431  return aLhs != aRhs.get();
    432 }
    433 
    434 // Disallow comparing a NotNull to a nullptr.
    435 template <typename T>
    436 bool operator==(const NotNull<T>&, std::nullptr_t) = delete;
    437 template <typename T>
    438 bool operator!=(const NotNull<T>&, std::nullptr_t) = delete;
    439 
    440 // Disallow comparing a nullptr to a NotNull.
    441 template <typename T>
    442 bool operator==(std::nullptr_t, const NotNull<T>&) = delete;
    443 template <typename T>
    444 bool operator!=(std::nullptr_t, const NotNull<T>&) = delete;
    445 
    446 }  // namespace mozilla
    447 
    448 #endif /* mozilla_NotNull_h */