File Explorer

/var/lang/include/node/cppgc

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

1 dir
27 files
member.h25.0 KB · 664 lines
// Copyright 2020 the V8 project authors. All rights reserved.// Use of this source code is governed by a BSD-style license that can be// found in the LICENSE file. #ifndef INCLUDE_CPPGC_MEMBER_H_#define INCLUDE_CPPGC_MEMBER_H_ #include <atomic>#include <cstddef>#include <type_traits> #include "cppgc/internal/api-constants.h"#include "cppgc/internal/member-storage.h"#include "cppgc/internal/pointer-policies.h"#include "cppgc/sentinel-pointer.h"#include "cppgc/type-traits.h"#include "v8config.h"  // NOLINT(build/include_directory) namespace cppgc { namespace subtle {class HeapConsistency;}  // namespace subtle class Visitor; namespace internal { // MemberBase always refers to the object as const object and defers to// BasicMember on casting to the right type as needed.template <typename StorageType>class V8_TRIVIAL_ABI MemberBase { public:  using RawStorage = StorageType;  protected:  struct AtomicInitializerTag {};   V8_INLINE MemberBase() = default;  V8_INLINE explicit MemberBase(const void* value) : raw_(value) {}  V8_INLINE MemberBase(const void* value, AtomicInitializerTag)      : raw_(value, typename RawStorage::AtomicInitializerTag{}) {}   V8_INLINE explicit MemberBase(RawStorage raw) : raw_(raw) {}  V8_INLINE explicit MemberBase(std::nullptr_t) : raw_(nullptr) {}  V8_INLINE explicit MemberBase(SentinelPointer s) : raw_(s) {}   V8_INLINE const void** GetRawSlot() const {    return reinterpret_cast<const void**>(const_cast<MemberBase*>(this));  }  V8_INLINE const void* GetRaw() const { return raw_.Load(); }  V8_INLINE void SetRaw(void* value) { raw_.Store(value); }   V8_INLINE const void* GetRawAtomic() const { return raw_.LoadAtomic(); }  V8_INLINE void SetRawAtomic(const void* value) { raw_.StoreAtomic(value); }   V8_INLINE RawStorage GetRawStorage() const { return raw_; }  V8_INLINE void SetRawStorageAtomic(RawStorage other) {    reinterpret_cast<std::atomic<RawStorage>&>(raw_).store(        other, std::memory_order_relaxed);  }   V8_INLINE bool IsCleared() const { return raw_.IsCleared(); }   V8_INLINE void ClearFromGC() const { raw_.Clear(); }  private:  friend class MemberDebugHelper;   mutable RawStorage raw_;}; // The basic class from which all Member classes are 'generated'.template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>class V8_TRIVIAL_ABI BasicMember final : private MemberBase<StorageType>,                                         private CheckingPolicy {  using Base = MemberBase<StorageType>;  public:  using PointeeType = T;  using RawStorage = typename Base::RawStorage;   V8_INLINE constexpr BasicMember() = default;  V8_INLINE constexpr BasicMember(std::nullptr_t) {}     // NOLINT  V8_INLINE BasicMember(SentinelPointer s) : Base(s) {}  // NOLINT  V8_INLINE BasicMember(T* raw) : Base(raw) {            // NOLINT    InitializingWriteBarrier(raw);    CheckPointer(raw);  }  V8_INLINE BasicMember(T& raw)  // NOLINT      : BasicMember(&raw) {}   // Atomic ctor. Using the AtomicInitializerTag forces BasicMember to  // initialize using atomic assignments. This is required for preventing  // data races with concurrent marking.  using AtomicInitializerTag = typename Base::AtomicInitializerTag;  V8_INLINE BasicMember(std::nullptr_t, AtomicInitializerTag atomic)      : Base(nullptr, atomic) {}  V8_INLINE BasicMember(SentinelPointer s, AtomicInitializerTag atomic)      : Base(s, atomic) {}  V8_INLINE BasicMember(T* raw, AtomicInitializerTag atomic)      : Base(raw, atomic) {    InitializingWriteBarrier(raw);    CheckPointer(raw);  }  V8_INLINE BasicMember(T& raw, AtomicInitializerTag atomic)      : BasicMember(&raw, atomic) {}   // Copy ctor.  V8_INLINE BasicMember(const BasicMember& other)      : BasicMember(other.GetRawStorage()) {}   // Heterogeneous copy constructors. When the source pointer have a different  // type, perform a compress-decompress round, because the source pointer may  // need to be adjusted.  template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,            typename OtherCheckingPolicy,            std::enable_if_t<IsDecayedSameV<T, U>>* = nullptr>  V8_INLINE BasicMember(  // NOLINT      const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,                        OtherCheckingPolicy, StorageType>& other)      : BasicMember(other.GetRawStorage()) {}   template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,            typename OtherCheckingPolicy,            std::enable_if_t<IsStrictlyBaseOfV<T, U>>* = nullptr>  V8_INLINE BasicMember(  // NOLINT      const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,                        OtherCheckingPolicy, StorageType>& other)      : BasicMember(other.Get()) {}   // Move ctor.  V8_INLINE BasicMember(BasicMember&& other) noexcept      : BasicMember(other.GetRawStorage()) {    other.Clear();  }   // Heterogeneous move constructors. When the source pointer have a different  // type, perform a compress-decompress round, because the source pointer may  // need to be adjusted.  template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,            typename OtherCheckingPolicy,            std::enable_if_t<IsDecayedSameV<T, U>>* = nullptr>  V8_INLINE BasicMember(      BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, OtherCheckingPolicy,                  StorageType>&& other) noexcept      : BasicMember(other.GetRawStorage()) {    other.Clear();  }   template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,            typename OtherCheckingPolicy,            std::enable_if_t<IsStrictlyBaseOfV<T, U>>* = nullptr>  V8_INLINE BasicMember(      BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, OtherCheckingPolicy,                  StorageType>&& other) noexcept      : BasicMember(other.Get()) {    other.Clear();  }   // Construction from Persistent.  template <typename U, typename PersistentWeaknessPolicy,            typename PersistentLocationPolicy,            typename PersistentCheckingPolicy,            typename = std::enable_if_t<std::is_base_of<T, U>::value>>  V8_INLINE BasicMember(const BasicPersistent<U, PersistentWeaknessPolicy,                                              PersistentLocationPolicy,                                              PersistentCheckingPolicy>& p)      : BasicMember(p.Get()) {}   // Copy assignment.  V8_INLINE BasicMember& operator=(const BasicMember& other) {    return operator=(other.GetRawStorage());  }   // Heterogeneous copy assignment. When the source pointer have a different  // type, perform a compress-decompress round, because the source pointer may  // need to be adjusted.  template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,            typename OtherCheckingPolicy>  V8_INLINE BasicMember& operator=(      const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,                        OtherCheckingPolicy, StorageType>& other) {    if constexpr (IsDecayedSameV<T, U>) {      return operator=(other.GetRawStorage());    } else {      static_assert(IsStrictlyBaseOfV<T, U>);      return operator=(other.Get());    }  }   // Move assignment.  V8_INLINE BasicMember& operator=(BasicMember&& other) noexcept {    operator=(other.GetRawStorage());    other.Clear();    return *this;  }   // Heterogeneous move assignment. When the source pointer have a different  // type, perform a compress-decompress round, because the source pointer may  // need to be adjusted.  template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,            typename OtherCheckingPolicy>  V8_INLINE BasicMember& operator=(      BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, OtherCheckingPolicy,                  StorageType>&& other) noexcept {    if constexpr (IsDecayedSameV<T, U>) {      operator=(other.GetRawStorage());    } else {      static_assert(IsStrictlyBaseOfV<T, U>);      operator=(other.Get());    }    other.Clear();    return *this;  }   // Assignment from Persistent.  template <typename U, typename PersistentWeaknessPolicy,            typename PersistentLocationPolicy,            typename PersistentCheckingPolicy,            typename = std::enable_if_t<std::is_base_of<T, U>::value>>  V8_INLINE BasicMember& operator=(      const BasicPersistent<U, PersistentWeaknessPolicy,                            PersistentLocationPolicy, PersistentCheckingPolicy>&          other) {    return operator=(other.Get());  }   V8_INLINE BasicMember& operator=(T* other) {    Base::SetRawAtomic(other);    AssigningWriteBarrier(other);    CheckPointer(other);    return *this;  }   V8_INLINE BasicMember& operator=(std::nullptr_t) {    Clear();    return *this;  }  V8_INLINE BasicMember& operator=(SentinelPointer s) {    Base::SetRawAtomic(s);    return *this;  }   template <typename OtherWeaknessTag, typename OtherBarrierPolicy,            typename OtherCheckingPolicy>  V8_INLINE void Swap(BasicMember<T, OtherWeaknessTag, OtherBarrierPolicy,                                  OtherCheckingPolicy, StorageType>& other) {    auto tmp = GetRawStorage();    *this = other;    other = tmp;  }   V8_INLINE explicit operator bool() const { return !Base::IsCleared(); }  V8_INLINE operator T*() const { return Get(); }  V8_INLINE T* operator->() const { return Get(); }  V8_INLINE T& operator*() const { return *Get(); }   // CFI cast exemption to allow passing SentinelPointer through T* and support  // heterogeneous assignments between different Member and Persistent handles  // based on their actual types.  V8_INLINE V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {    // Executed by the mutator, hence non atomic load.    //    // The const_cast below removes the constness from MemberBase storage. The    // following static_cast re-adds any constness if specified through the    // user-visible template parameter T.    return static_cast<T*>(const_cast<void*>(Base::GetRaw()));  }   V8_INLINE void Clear() { Base::SetRawStorageAtomic(RawStorage{}); }   V8_INLINE T* Release() {    T* result = Get();    Clear();    return result;  }   V8_INLINE const T** GetSlotForTesting() const {    return reinterpret_cast<const T**>(Base::GetRawSlot());  }   V8_INLINE RawStorage GetRawStorage() const { return Base::GetRawStorage(); }  private:  V8_INLINE explicit BasicMember(RawStorage raw) : Base(raw) {    InitializingWriteBarrier();    CheckPointer();  }   V8_INLINE BasicMember& operator=(RawStorage other) {    Base::SetRawStorageAtomic(other);    AssigningWriteBarrier();    CheckPointer();    return *this;  }   V8_INLINE const T* GetRawAtomic() const {    return static_cast<const T*>(Base::GetRawAtomic());  }   V8_INLINE void InitializingWriteBarrier(T* value) const {    WriteBarrierPolicy::InitializingBarrier(Base::GetRawSlot(), value);  }  V8_INLINE void InitializingWriteBarrier() const {    WriteBarrierPolicy::InitializingBarrier(Base::GetRawSlot(),                                            Base::GetRawStorage());  }  V8_INLINE void AssigningWriteBarrier(T* value) const {    WriteBarrierPolicy::template AssigningBarrier<        StorageType::kWriteBarrierSlotType>(Base::GetRawSlot(), value);  }  V8_INLINE void AssigningWriteBarrier() const {    WriteBarrierPolicy::template AssigningBarrier<        StorageType::kWriteBarrierSlotType>(Base::GetRawSlot(),                                            Base::GetRawStorage());  }  V8_INLINE void CheckPointer(T* value) {    CheckingPolicy::template CheckPointer<T>(value);  }  V8_INLINE void CheckPointer() {    CheckingPolicy::template CheckPointer<T>(Base::GetRawStorage());  }   V8_INLINE void ClearFromGC() const { Base::ClearFromGC(); }   V8_INLINE T* GetFromGC() const { return Get(); }   friend class cppgc::subtle::HeapConsistency;  friend class cppgc::Visitor;  template <typename U>  friend struct cppgc::TraceTrait;  template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,            typename CheckingPolicy1, typename StorageType1>  friend class BasicMember;}; // Member equality operators.template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,          typename CheckingPolicy1, typename T2, typename WeaknessTag2,          typename WriteBarrierPolicy2, typename CheckingPolicy2,          typename StorageType>V8_INLINE bool operator==(    const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1,                      StorageType>& member1,    const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2,                      StorageType>& member2) {  if constexpr (IsDecayedSameV<T1, T2>) {    // Check compressed pointers if types are the same.    return member1.GetRawStorage() == member2.GetRawStorage();  } else {    static_assert(IsStrictlyBaseOfV<T1, T2> || IsStrictlyBaseOfV<T2, T1>);    // Otherwise, check decompressed pointers.    return member1.Get() == member2.Get();  }} template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,          typename CheckingPolicy1, typename T2, typename WeaknessTag2,          typename WriteBarrierPolicy2, typename CheckingPolicy2,          typename StorageType>V8_INLINE bool operator!=(    const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1,                      StorageType>& member1,    const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2,                      StorageType>& member2) {  return !(member1 == member2);} // Equality with raw pointers.template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType, typename U>V8_INLINE bool operator==(    const BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy,                      StorageType>& member,    U* raw) {  // Never allow comparison with erased pointers.  static_assert(!IsDecayedSameV<void, U>);   if constexpr (IsDecayedSameV<T, U>) {    // Check compressed pointers if types are the same.    return member.GetRawStorage() == StorageType(raw);  } else if constexpr (IsStrictlyBaseOfV<T, U>) {    // Cast the raw pointer to T, which may adjust the pointer.    return member.GetRawStorage() == StorageType(static_cast<T*>(raw));  } else {    // Otherwise, decompressed the member.    return member.Get() == raw;  }} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType, typename U>V8_INLINE bool operator!=(    const BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy,                      StorageType>& member,    U* raw) {  return !(member == raw);} template <typename T, typename U, typename WeaknessTag,          typename WriteBarrierPolicy, typename CheckingPolicy,          typename StorageType>V8_INLINE bool operator==(    T* raw, const BasicMember<U, WeaknessTag, WriteBarrierPolicy,                              CheckingPolicy, StorageType>& member) {  return member == raw;} template <typename T, typename U, typename WeaknessTag,          typename WriteBarrierPolicy, typename CheckingPolicy,          typename StorageType>V8_INLINE bool operator!=(    T* raw, const BasicMember<U, WeaknessTag, WriteBarrierPolicy,                              CheckingPolicy, StorageType>& member) {  return !(raw == member);} // Equality with sentinel.template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator==(    const BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy,                      StorageType>& member,    SentinelPointer) {  return member.GetRawStorage().IsSentinel();} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator!=(    const BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy,                      StorageType>& member,    SentinelPointer s) {  return !(member == s);} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator==(    SentinelPointer s, const BasicMember<T, WeaknessTag, WriteBarrierPolicy,                                         CheckingPolicy, StorageType>& member) {  return member == s;} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator!=(    SentinelPointer s, const BasicMember<T, WeaknessTag, WriteBarrierPolicy,                                         CheckingPolicy, StorageType>& member) {  return !(s == member);} // Equality with nullptr.template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator==(    const BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy,                      StorageType>& member,    std::nullptr_t) {  return !static_cast<bool>(member);} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator!=(    const BasicMember<T, WeaknessTag, WriteBarrierPolicy, CheckingPolicy,                      StorageType>& member,    std::nullptr_t n) {  return !(member == n);} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator==(    std::nullptr_t n, const BasicMember<T, WeaknessTag, WriteBarrierPolicy,                                        CheckingPolicy, StorageType>& member) {  return member == n;} template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType>V8_INLINE bool operator!=(    std::nullptr_t n, const BasicMember<T, WeaknessTag, WriteBarrierPolicy,                                        CheckingPolicy, StorageType>& member) {  return !(n == member);} // Relational operators.template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,          typename CheckingPolicy1, typename T2, typename WeaknessTag2,          typename WriteBarrierPolicy2, typename CheckingPolicy2,          typename StorageType>V8_INLINE bool operator<(    const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1,                      StorageType>& member1,    const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2,                      StorageType>& member2) {  static_assert(      IsDecayedSameV<T1, T2>,      "Comparison works only for same pointer type modulo cv-qualifiers");  return member1.GetRawStorage() < member2.GetRawStorage();} template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,          typename CheckingPolicy1, typename T2, typename WeaknessTag2,          typename WriteBarrierPolicy2, typename CheckingPolicy2,          typename StorageType>V8_INLINE bool operator<=(    const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1,                      StorageType>& member1,    const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2,                      StorageType>& member2) {  static_assert(      IsDecayedSameV<T1, T2>,      "Comparison works only for same pointer type modulo cv-qualifiers");  return member1.GetRawStorage() <= member2.GetRawStorage();} template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,          typename CheckingPolicy1, typename T2, typename WeaknessTag2,          typename WriteBarrierPolicy2, typename CheckingPolicy2,          typename StorageType>V8_INLINE bool operator>(    const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1,                      StorageType>& member1,    const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2,                      StorageType>& member2) {  static_assert(      IsDecayedSameV<T1, T2>,      "Comparison works only for same pointer type modulo cv-qualifiers");  return member1.GetRawStorage() > member2.GetRawStorage();} template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,          typename CheckingPolicy1, typename T2, typename WeaknessTag2,          typename WriteBarrierPolicy2, typename CheckingPolicy2,          typename StorageType>V8_INLINE bool operator>=(    const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1,                      StorageType>& member1,    const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2,                      StorageType>& member2) {  static_assert(      IsDecayedSameV<T1, T2>,      "Comparison works only for same pointer type modulo cv-qualifiers");  return member1.GetRawStorage() >= member2.GetRawStorage();} template <typename T, typename WriteBarrierPolicy, typename CheckingPolicy,          typename StorageType>struct IsWeak<BasicMember<T, WeakMemberTag, WriteBarrierPolicy, CheckingPolicy,                          StorageType>> : std::true_type {}; }  // namespace internal /** * Members are used in classes to contain strong pointers to other garbage * collected objects. All Member fields of a class must be traced in the class' * trace method. */template <typename T>using Member = internal::BasicMember<    T, internal::StrongMemberTag, internal::DijkstraWriteBarrierPolicy,    internal::DefaultMemberCheckingPolicy, internal::DefaultMemberStorage>; /** * WeakMember is similar to Member in that it is used to point to other garbage * collected objects. However instead of creating a strong pointer to the * object, the WeakMember creates a weak pointer, which does not keep the * pointee alive. Hence if all pointers to to a heap allocated object are weak * the object will be garbage collected. At the time of GC the weak pointers * will automatically be set to null. */template <typename T>using WeakMember = internal::BasicMember<    T, internal::WeakMemberTag, internal::DijkstraWriteBarrierPolicy,    internal::DefaultMemberCheckingPolicy, internal::DefaultMemberStorage>; /** * UntracedMember is a pointer to an on-heap object that is not traced for some * reason. Do not use this unless you know what you are doing. Keeping raw * pointers to on-heap objects is prohibited unless used from stack. Pointee * must be kept alive through other means. */template <typename T>using UntracedMember = internal::BasicMember<    T, internal::UntracedMemberTag, internal::NoWriteBarrierPolicy,    internal::DefaultMemberCheckingPolicy, internal::DefaultMemberStorage>; namespace subtle { /** * UncompressedMember. Use with care in hot paths that would otherwise cause * many decompression cycles. */template <typename T>using UncompressedMember = internal::BasicMember<    T, internal::StrongMemberTag, internal::DijkstraWriteBarrierPolicy,    internal::DefaultMemberCheckingPolicy, internal::RawPointer>; #if defined(CPPGC_POINTER_COMPRESSION)/** * CompressedMember. Default implementation of cppgc::Member on builds with * pointer compression. */template <typename T>using CompressedMember = internal::BasicMember<    T, internal::StrongMemberTag, internal::DijkstraWriteBarrierPolicy,    internal::DefaultMemberCheckingPolicy, internal::CompressedPointer>;#endif  // defined(CPPGC_POINTER_COMPRESSION) }  // namespace subtle namespace internal { struct Dummy; static constexpr size_t kSizeOfMember = sizeof(Member<Dummy>);static constexpr size_t kSizeOfUncompressedMember =    sizeof(subtle::UncompressedMember<Dummy>);#if defined(CPPGC_POINTER_COMPRESSION)static constexpr size_t kSizeofCompressedMember =    sizeof(subtle::CompressedMember<Dummy>);#endif  // defined(CPPGC_POINTER_COMPRESSION) }  // namespace internal }  // namespace cppgc // Mark `BasicMember<T>` and `T*` as having a common reference type of `T*` (the// type to which both can be converted or bound). This makes them satisfy// `std::equality_comparable`, which allows usage like the following:// ```//   HeapVector<Member<T>> v;//   T* e;//   auto it = std::ranges::find(v, e);// ```// Without this, the `find()` call above would fail to compile with an error// about being unable to invoke `std::ranges::equal_to()`.template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType,          template <typename> typename TQ, template <typename> typename UQ>struct std::basic_common_reference<    cppgc::internal::BasicMember<T, WeaknessTag, WriteBarrierPolicy,                                 CheckingPolicy, StorageType>,    T*, TQ, UQ> {  using type = T*;}; template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,          typename CheckingPolicy, typename StorageType,          template <typename> typename TQ, template <typename> typename UQ>struct std::basic_common_reference<    T*,    cppgc::internal::BasicMember<T, WeaknessTag, WriteBarrierPolicy,                                 CheckingPolicy, StorageType>,    TQ, UQ> {  using type = T*;}; #endif  // INCLUDE_CPPGC_MEMBER_H_