tor-browser

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

CheckedUnsafePtr.h (20161B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // Diagnostic class template that helps finding dangling pointers.
      6 
      7 #ifndef mozilla_CheckedUnsafePtr_h
      8 #define mozilla_CheckedUnsafePtr_h
      9 
     10 #include <cstddef>
     11 #include <type_traits>
     12 #include <utility>
     13 
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/DataMutex.h"
     17 #include "mozilla/StackWalk.h"
     18 #include "mozilla/StaticPrefs_dom.h"
     19 #include "nsContentUtils.h"
     20 #include "nsString.h"
     21 #include "nsTArray.h"
     22 
     23 #if defined __has_builtin
     24 #  if __has_builtin(__builtin_FUNCTION)
     25 #    define bt_function __builtin_FUNCTION()
     26 #  else
     27 #    define bt_function "__builtin_FUNCTION() is undefined"
     28 #  endif
     29 #  if __has_builtin(__builtin_FILE)
     30 #    define bt_file __builtin_FILE()
     31 #  else
     32 #    define bt_file "__builtin_FILE() is undefined"
     33 #  endif
     34 #  if __has_builtin(__builtin_LINE)
     35 #    define bt_line __builtin_LINE()
     36 #  else
     37 #    define bt_line -1
     38 #  endif
     39 #else
     40 #  define bt_function "__builtin_FUNCTION() is undefined"
     41 #  define bt_file "__builtin_FILE() is undefined"
     42 #  define bt_line -1
     43 #endif
     44 
     45 namespace mozilla {
     46 enum class CheckingSupport {
     47  Disabled,
     48  Enabled,
     49 };
     50 
     51 template <typename T>
     52 class CheckedUnsafePtr;
     53 
     54 namespace detail {
     55 
     56 static constexpr auto kSourceFileRelativePathMap =
     57    std::array<std::pair<nsLiteralCString, nsLiteralCString>, 1>{
     58        {{"mozilla/dom/CheckedUnsafePtr.h"_ns,
     59          "dom/quota/CheckedUnsafePtr.h"_ns}}};
     60 
     61 static inline nsDependentCSubstring GetSourceFileRelativePath(
     62    const nsACString& aSourceFilePath) {
     63  static constexpr auto error = "ERROR"_ns;
     64  static constexpr auto mozillaRelativeBase = "mozilla/"_ns;
     65  static constexpr auto thisSourceFileRelativePath =
     66      "/dom/quota/CheckedUnsafePtr.h"_ns;
     67  static constexpr auto filePath = nsLiteralCString(__FILE__);
     68 
     69  MOZ_ASSERT(StringEndsWith(filePath, thisSourceFileRelativePath));
     70  static const auto sourceTreeBase = Substring(
     71      filePath, 0, filePath.Length() - thisSourceFileRelativePath.Length());
     72 
     73  if (MOZ_LIKELY(StringBeginsWith(aSourceFilePath, sourceTreeBase))) {
     74    return Substring(aSourceFilePath, sourceTreeBase.Length() + 1);
     75  }
     76 
     77  // The source file could have been exported to the OBJDIR/dist/include
     78  // directory, so we need to check that case as well.
     79  static constexpr auto commonHSourceFileRelativePath =
     80      "/mozilla/dom/quota/CheckedUnsafePtr.h"_ns;
     81  MOZ_ASSERT(StringEndsWith(filePath, commonHSourceFileRelativePath));
     82  static const auto objdirDistIncludeTreeBase = Substring(
     83      filePath, 0, filePath.Length() - commonHSourceFileRelativePath.Length());
     84 
     85  if (MOZ_LIKELY(
     86          StringBeginsWith(aSourceFilePath, objdirDistIncludeTreeBase))) {
     87    const auto sourceFileRelativePath =
     88        Substring(aSourceFilePath, objdirDistIncludeTreeBase.Length() + 1);
     89 
     90    // Exported source files don't have to use the same directory structure as
     91    // original source files. Check if we have a mapping for the exported
     92    // source file.
     93    const auto foundIt = std::find_if(
     94        kSourceFileRelativePathMap.cbegin(), kSourceFileRelativePathMap.cend(),
     95        [&sourceFileRelativePath](const auto& entry) {
     96          return entry.first == sourceFileRelativePath;
     97        });
     98 
     99    if (MOZ_UNLIKELY(foundIt != kSourceFileRelativePathMap.cend())) {
    100      return Substring(foundIt->second, 0);
    101    }
    102 
    103    // If we don't have a mapping for it, just remove the mozilla/ prefix
    104    // (if there's any).
    105    if (MOZ_LIKELY(
    106            StringBeginsWith(sourceFileRelativePath, mozillaRelativeBase))) {
    107      return Substring(sourceFileRelativePath, mozillaRelativeBase.Length());
    108    }
    109 
    110    // At this point, we don't know how to transform the relative path of the
    111    // exported source file back to the relative path of the original source
    112    // file. This can happen when QM_TRY is used in an exported nsIFoo.h file.
    113    // If you really need to use QM_TRY there, consider adding a new mapping
    114    // for the exported source file.
    115    return sourceFileRelativePath;
    116  }
    117 
    118  nsCString::const_iterator begin, end;
    119  if (RFindInReadable("/"_ns, aSourceFilePath.BeginReading(begin),
    120                      aSourceFilePath.EndReading(end))) {
    121    // Use the basename as a fallback, to avoid exposing any user parts of the
    122    // path.
    123    ++begin;
    124    return Substring(begin, aSourceFilePath.EndReading(end));
    125  }
    126 
    127  return nsDependentCSubstring{static_cast<mozilla::Span<const char>>(
    128      static_cast<const nsCString&>(error))};
    129 }
    130 
    131 static inline void CheckedUnsafePtrStackCallback(uint32_t aFrameNumber,
    132                                                 void* aPC, void* aSP,
    133                                                 void* aClosure) {
    134  auto* stack = static_cast<nsCString*>(aClosure);
    135  MozCodeAddressDetails details;
    136  MozDescribeCodeAddress(aPC, &details);
    137  char buf[1025];
    138  (void)MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC,
    139                                    &details);
    140  stack->Append(buf);
    141  stack->Append("\n");
    142 }
    143 
    144 class CheckedUnsafePtrBaseCheckingEnabled;
    145 
    146 struct CheckedUnsafePtrCheckData {
    147  using Data = nsTArray<CheckedUnsafePtrBaseCheckingEnabled*>;
    148 
    149  DataMutex<Data> mPtrs{"mozilla::SupportsCheckedUnsafePtr"};
    150 };
    151 
    152 class CheckedUnsafePtrBaseCheckingEnabled {
    153  friend class CheckedUnsafePtrBaseAccess;
    154 
    155 protected:
    156  CheckedUnsafePtrBaseCheckingEnabled() = delete;
    157  CheckedUnsafePtrBaseCheckingEnabled(
    158      const CheckedUnsafePtrBaseCheckingEnabled& aOther) = default;
    159  CheckedUnsafePtrBaseCheckingEnabled(const char* aFunction, const char* aFile,
    160                                      const int aLine)
    161      : mFunctionName(aFunction), mSourceFile(aFile), mLineNo(aLine) {}
    162 
    163  // When copying an CheckedUnsafePtr, its mIsDangling member must be copied as
    164  // well; otherwise the new copy might try to dereference a dangling pointer
    165  // when destructed.
    166  void CopyDanglingFlagIfAvailableFrom(
    167      const CheckedUnsafePtrBaseCheckingEnabled& aOther) {
    168    mIsDangling = aOther.mIsDangling;
    169  }
    170 
    171  template <typename Ptr>
    172  using DisableForCheckedUnsafePtr = std::enable_if_t<
    173      !std::is_base_of<CheckedUnsafePtrBaseCheckingEnabled, Ptr>::value>;
    174 
    175  // When constructing an CheckedUnsafePtr from a different kind of pointer it's
    176  // not possible to determine whether it's dangling; therefore it's undefined
    177  // behavior to construct one from a dangling pointer, and we assume that any
    178  // CheckedUnsafePtr thus constructed is not dangling.
    179  template <typename Ptr>
    180  DisableForCheckedUnsafePtr<Ptr> CopyDanglingFlagIfAvailableFrom(const Ptr&) {}
    181 
    182  template <typename F>
    183  void WithCheckedUnsafePtrsImpl(CheckedUnsafePtrCheckData* const aRawPtr,
    184                                 F&& aClosure) {
    185    if (!mIsDangling && aRawPtr) {
    186      const auto CheckedUnsafePtrs = aRawPtr->mPtrs.Lock();
    187      aClosure(this, *CheckedUnsafePtrs);
    188    }
    189  }
    190 
    191  void DumpDebugMsg() {
    192    fprintf(stderr, "CheckedUnsafePtr [%p]\n", this);
    193    fprintf(stderr, "Location of creation: %s, %s:%d\n", mFunctionName.get(),
    194            GetSourceFileRelativePath(mSourceFile).BeginReading(), mLineNo);
    195    fprintf(stderr, "Stack of creation:\n%s\n", mCreationStack.get());
    196    fprintf(stderr, "Stack of last assignment\n%s\n\n",
    197            mLastAssignmentStack.get());
    198  }
    199 
    200  nsCString mFunctionName{EmptyCString()};
    201  nsCString mSourceFile{EmptyCString()};
    202  int32_t mLineNo{-1};
    203  nsCString mCreationStack{EmptyCString()};
    204  nsCString mLastAssignmentStack{EmptyCString()};
    205 
    206 private:
    207  bool mIsDangling = false;
    208 };
    209 
    210 class CheckedUnsafePtrBaseAccess {
    211 protected:
    212  static void SetDanglingFlag(CheckedUnsafePtrBaseCheckingEnabled& aBase) {
    213    aBase.mIsDangling = true;
    214    aBase.DumpDebugMsg();
    215  }
    216 };
    217 
    218 template <typename T, CheckingSupport = T::SupportsChecking::value>
    219 class CheckedUnsafePtrBase;
    220 
    221 template <typename T, typename U, typename S = std::nullptr_t>
    222 using EnableIfCompatible = std::enable_if_t<
    223    std::is_base_of<
    224        T, std::remove_reference_t<decltype(*std::declval<U>())>>::value,
    225    S>;
    226 
    227 template <typename T>
    228 class CheckedUnsafePtrBase<T, CheckingSupport::Enabled>
    229    : detail::CheckedUnsafePtrBaseCheckingEnabled {
    230 public:
    231  MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(
    232      const std::nullptr_t = nullptr, const char* aFunction = bt_function,
    233      const char* aFile = bt_file, const int32_t aLine = bt_line)
    234      : detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine),
    235        mRawPtr(nullptr) {
    236    if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
    237      MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
    238                   &mCreationStack);
    239    }
    240  }
    241 
    242  template <typename U, typename = EnableIfCompatible<T, U>>
    243  MOZ_IMPLICIT CheckedUnsafePtrBase(const U& aPtr,
    244                                    const char* aFunction = bt_function,
    245                                    const char* aFile = bt_file,
    246                                    const int32_t aLine = bt_line)
    247      : detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine) {
    248    if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
    249      MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
    250                   &mCreationStack);
    251    }
    252    Set(aPtr);
    253  }
    254 
    255  CheckedUnsafePtrBase(const CheckedUnsafePtrBase& aOther,
    256                       const char* aFunction = bt_function,
    257                       const char* aFile = bt_file,
    258                       const int32_t aLine = bt_line)
    259      : detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine) {
    260    if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
    261      MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
    262                   &mCreationStack);
    263    }
    264    Set(aOther.Downcast());
    265  }
    266 
    267  ~CheckedUnsafePtrBase() { Reset(); }
    268 
    269  CheckedUnsafePtr<T>& operator=(const std::nullptr_t) {
    270    // Assign to nullptr, no need to record the last assignment stack.
    271    if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
    272      mLastAssignmentStack.Truncate();
    273    }
    274    Reset();
    275    return Downcast();
    276  }
    277 
    278  template <typename U>
    279  EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=(const U& aPtr) {
    280    if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
    281      mLastAssignmentStack.Truncate();
    282      MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
    283                   &mLastAssignmentStack);
    284    }
    285    Replace(aPtr);
    286    return Downcast();
    287  }
    288 
    289  CheckedUnsafePtrBase& operator=(const CheckedUnsafePtrBase& aOther) {
    290    if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) {
    291      mLastAssignmentStack.Truncate();
    292      if (aOther.get()) {
    293        MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0,
    294                     &mLastAssignmentStack);
    295      }
    296    }
    297    if (&aOther != this) {
    298      if (aOther.get()) {
    299        Replace(aOther.Downcast());
    300      } else {
    301        Reset();
    302      }
    303    }
    304    return Downcast();
    305  }
    306 
    307  constexpr T* get() const { return mRawPtr; }
    308 
    309 private:
    310  template <typename U, CheckingSupport>
    311  friend class CheckedUnsafePtrBase;
    312 
    313  CheckedUnsafePtr<T>& Downcast() {
    314    return static_cast<CheckedUnsafePtr<T>&>(*this);
    315  }
    316  const CheckedUnsafePtr<T>& Downcast() const {
    317    return static_cast<const CheckedUnsafePtr<T>&>(*this);
    318  }
    319 
    320  using Base = detail::CheckedUnsafePtrBaseCheckingEnabled;
    321 
    322  template <typename U>
    323  void Replace(const U& aPtr) {
    324    Reset();
    325    Set(aPtr);
    326  }
    327 
    328  void Reset() {
    329    WithCheckedUnsafePtrs(
    330        [](Base* const aSelf,
    331           detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) {
    332          const auto index = aCheckedUnsafePtrs.IndexOf(aSelf);
    333          aCheckedUnsafePtrs.UnorderedRemoveElementAt(index);
    334        });
    335    mRawPtr = nullptr;
    336  }
    337 
    338  template <typename U>
    339  void Set(const U& aPtr) {
    340    this->CopyDanglingFlagIfAvailableFrom(aPtr);
    341    mRawPtr = &*aPtr;
    342    WithCheckedUnsafePtrs(
    343        [](Base* const aSelf,
    344           detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) {
    345          aCheckedUnsafePtrs.AppendElement(aSelf);
    346        });
    347  }
    348 
    349  template <typename F>
    350  void WithCheckedUnsafePtrs(F&& aClosure) {
    351    this->WithCheckedUnsafePtrsImpl(mRawPtr, std::forward<F>(aClosure));
    352  }
    353 
    354  T* mRawPtr;
    355 };
    356 
    357 template <typename T>
    358 class CheckedUnsafePtrBase<T, CheckingSupport::Disabled> {
    359 public:
    360  MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const std::nullptr_t = nullptr)
    361      : mRawPtr(nullptr) {}
    362 
    363  template <typename U, typename = EnableIfCompatible<T, U>>
    364  MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const U& aPtr) : mRawPtr(aPtr) {}
    365 
    366  constexpr CheckedUnsafePtr<T>& operator=(const std::nullptr_t) {
    367    mRawPtr = nullptr;
    368    return Downcast();
    369  }
    370 
    371  template <typename U>
    372  constexpr EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=(
    373      const U& aPtr) {
    374    mRawPtr = aPtr;
    375    return Downcast();
    376  }
    377 
    378  constexpr T* get() const { return mRawPtr; }
    379 
    380 private:
    381  constexpr CheckedUnsafePtr<T>& Downcast() {
    382    return static_cast<CheckedUnsafePtr<T>&>(*this);
    383  }
    384 
    385  T* mRawPtr;
    386 };
    387 }  // namespace detail
    388 
    389 class CheckingPolicyAccess {
    390 protected:
    391  template <typename CheckingPolicy>
    392  static void NotifyCheckFailure(CheckingPolicy& aPolicy) {
    393    aPolicy.NotifyCheckFailure();
    394  }
    395 };
    396 
    397 template <typename Derived>
    398 class CheckCheckedUnsafePtrs : private CheckingPolicyAccess,
    399                               private detail::CheckedUnsafePtrBaseAccess {
    400 public:
    401  using SupportsChecking =
    402      std::integral_constant<CheckingSupport, CheckingSupport::Enabled>;
    403 
    404 protected:
    405  static constexpr bool ShouldCheck() {
    406    static_assert(
    407        std::is_base_of<CheckCheckedUnsafePtrs, Derived>::value,
    408        "cannot instantiate with a type that's not a subclass of this class");
    409    return true;
    410  }
    411 
    412  void Check(detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) {
    413    if (!aCheckedUnsafePtrs.IsEmpty()) {
    414      for (auto* const aCheckedUnsafePtrBase : aCheckedUnsafePtrs) {
    415        SetDanglingFlag(*aCheckedUnsafePtrBase);
    416      }
    417      NotifyCheckFailure(*static_cast<Derived*>(this));
    418    }
    419  }
    420 };
    421 
    422 class CrashOnDanglingCheckedUnsafePtr
    423    : public CheckCheckedUnsafePtrs<CrashOnDanglingCheckedUnsafePtr> {
    424  friend class mozilla::CheckingPolicyAccess;
    425  void NotifyCheckFailure() { MOZ_CRASH("Found dangling CheckedUnsafePtr"); }
    426 };
    427 
    428 struct DoNotCheckCheckedUnsafePtrs {
    429  using SupportsChecking =
    430      std::integral_constant<CheckingSupport, CheckingSupport::Disabled>;
    431 };
    432 
    433 namespace detail {
    434 // Template parameter CheckingSupport controls the inclusion of
    435 // CheckedUnsafePtrCheckData as a subobject of instantiations of
    436 // SupportsCheckedUnsafePtr, ensuring that choosing a policy without checking
    437 // support incurs no size overhead.
    438 template <typename CheckingPolicy,
    439          CheckingSupport = CheckingPolicy::SupportsChecking::value>
    440 class SupportCheckedUnsafePtrImpl;
    441 
    442 template <typename CheckingPolicy>
    443 class SupportCheckedUnsafePtrImpl<CheckingPolicy, CheckingSupport::Disabled>
    444    : public CheckingPolicy {
    445 protected:
    446  template <typename... Args>
    447  explicit SupportCheckedUnsafePtrImpl(Args&&... aArgs)
    448      : CheckingPolicy(std::forward<Args>(aArgs)...) {}
    449 };
    450 
    451 template <typename CheckingPolicy>
    452 class SupportCheckedUnsafePtrImpl<CheckingPolicy, CheckingSupport::Enabled>
    453    : public CheckedUnsafePtrCheckData, public CheckingPolicy {
    454  template <typename T>
    455  friend class CheckedUnsafePtr;
    456 
    457 protected:
    458  template <typename... Args>
    459  explicit SupportCheckedUnsafePtrImpl(Args&&... aArgs)
    460      : CheckingPolicy(std::forward<Args>(aArgs)...) {}
    461 
    462  ~SupportCheckedUnsafePtrImpl() {
    463    if (this->ShouldCheck()) {
    464      const auto ptrs = mPtrs.Lock();
    465      this->Check(*ptrs);
    466    }
    467  }
    468 };
    469 
    470 struct SupportsCheckedUnsafePtrTag {};
    471 }  // namespace detail
    472 
    473 template <typename Condition,
    474          typename CheckingPolicy = CrashOnDanglingCheckedUnsafePtr>
    475 using CheckIf = std::conditional_t<Condition::value, CheckingPolicy,
    476                                   DoNotCheckCheckedUnsafePtrs>;
    477 
    478 #ifdef DEBUG
    479 using AssertEnabled = std::true_type;
    480 #else
    481 using AssertEnabled = std::false_type;
    482 #endif
    483 
    484 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    485 using DiagnosticAssertEnabled = std::true_type;
    486 #else
    487 using DiagnosticAssertEnabled = std::false_type;
    488 #endif
    489 
    490 using ReleaseAssertEnabled = std::true_type;
    491 
    492 // A T class that publicly inherits from an instantiation of
    493 // SupportsCheckedUnsafePtr and its subclasses can be pointed to by smart
    494 // pointers of type CheckedUnsafePtr<T>. Whenever such a smart pointer is
    495 // created, its existence is tracked by the pointee according to its
    496 // CheckingPolicy. When the pointee goes out of scope it then uses the its
    497 // CheckingPolicy to verify that no CheckedUnsafePtr pointers are left pointing
    498 // to it.
    499 //
    500 // The CheckingPolicy type is used to control the kind of verification that
    501 // happen at the end of the object's lifetime. By default, debug builds always
    502 // check for dangling CheckedUnsafePtr pointers and assert that none are found,
    503 // while release builds forgo all checks. (Release builds incur no size or
    504 // runtime penalties compared to bare pointers.)
    505 template <typename CheckingPolicy>
    506 class SupportsCheckedUnsafePtr
    507    : public detail::SupportCheckedUnsafePtrImpl<CheckingPolicy>,
    508      public detail::SupportsCheckedUnsafePtrTag {
    509 public:
    510  template <typename... Args>
    511  explicit SupportsCheckedUnsafePtr(Args&&... aArgs)
    512      : detail::SupportCheckedUnsafePtrImpl<CheckingPolicy>(
    513            std::forward<Args>(aArgs)...) {}
    514 };
    515 
    516 // CheckedUnsafePtr<T> is a smart pointer class that helps detect dangling
    517 // pointers in cases where such pointers are not allowed. In order to use it,
    518 // the pointee T must publicly inherit from an instantiation of
    519 // SupportsCheckedUnsafePtr. An CheckedUnsafePtr<T> can be used anywhere a T*
    520 // can be used, has the same size, and imposes no additional thread-safety
    521 // restrictions.
    522 template <typename T>
    523 class CheckedUnsafePtr : public detail::CheckedUnsafePtrBase<T> {
    524  static_assert(
    525      std::is_base_of<detail::SupportsCheckedUnsafePtrTag, T>::value,
    526      "type T must be derived from instantiation of SupportsCheckedUnsafePtr");
    527 
    528 public:
    529  using detail::CheckedUnsafePtrBase<T>::CheckedUnsafePtrBase;
    530  using detail::CheckedUnsafePtrBase<T>::get;
    531 
    532  constexpr T* operator->() const { return get(); }
    533 
    534  constexpr T& operator*() const { return *get(); }
    535 
    536  MOZ_IMPLICIT constexpr operator T*() const { return get(); }
    537 
    538  template <typename U>
    539  constexpr bool operator==(
    540      detail::EnableIfCompatible<T, U, const U&> aRhs) const {
    541    return get() == aRhs.get();
    542  }
    543 
    544  template <typename U>
    545  friend constexpr bool operator==(
    546      detail::EnableIfCompatible<T, U, const U&> aLhs,
    547      const CheckedUnsafePtr& aRhs) {
    548    return aRhs == aLhs;
    549  }
    550 
    551  template <typename U>
    552  constexpr bool operator!=(
    553      detail::EnableIfCompatible<T, U, const U&> aRhs) const {
    554    return !(*this == aRhs);
    555  }
    556 
    557  template <typename U>
    558  friend constexpr bool operator!=(
    559      detail::EnableIfCompatible<T, U, const U&> aLhs,
    560      const CheckedUnsafePtr& aRhs) {
    561    return aRhs != aLhs;
    562  }
    563 };
    564 
    565 }  // namespace mozilla
    566 
    567 // nsTArray<T> requires by default that T can be safely moved with std::memmove.
    568 // Since CheckedUnsafePtr<T> has a non-trivial copy constructor, it has to opt
    569 // into nsTArray<T> using them.
    570 template <typename T>
    571 struct nsTArray_RelocationStrategy<mozilla::CheckedUnsafePtr<T>> {
    572  using Type = std::conditional_t<
    573      T::SupportsChecking::value == mozilla::CheckingSupport::Enabled,
    574      nsTArray_RelocateUsingMoveConstructor<mozilla::CheckedUnsafePtr<T>>,
    575      nsTArray_RelocateUsingMemutils>;
    576 };
    577 
    578 template <typename T>
    579 struct nsTArray_RelocationStrategy<
    580    mozilla::NotNull<mozilla::CheckedUnsafePtr<T>>> {
    581  using Type =
    582      std::conditional_t<T::SupportsChecking::value ==
    583                             mozilla::CheckingSupport::Enabled,
    584                         nsTArray_RelocateUsingMoveConstructor<
    585                             mozilla::NotNull<mozilla::CheckedUnsafePtr<T>>>,
    586                         nsTArray_RelocateUsingMemutils>;
    587 };
    588 
    589 #endif  // mozilla_CheckedUnsafePtr_h