MiraclePtr: Preparatory step

Prepare the source code to the MiraclePtr rewrite:
1. Add the full definition of the requiring elements:
  - raw_ptr.h
  - raw_ref.h
  - RAW_PTR_EXCLUSION
2. Adapt some templates to access raw_ptr<T>

Bug: chromium:1464560
Change-Id: I5a2026f57239951f209780489426b568b2cbadc2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/165500
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/common/Compiler.h b/src/dawn/common/Compiler.h
index 63e7064..21fead8 100644
--- a/src/dawn/common/Compiler.h
+++ b/src/dawn/common/Compiler.h
@@ -29,104 +29,195 @@
 #define SRC_DAWN_COMMON_COMPILER_H_
 
 // Defines macros for compiler-specific functionality
-//  - DAWN_COMPILER_IS(CLANG|GCC|MSVC): Compiler detection
-//  - DAWN_BUILTIN_UNREACHABLE(): Hints the compiler that a code path is unreachable
-//  - DAWN_(UN)?LIKELY(EXPR): Where available, hints the compiler that the expression will be true
-//      (resp. false) to help it generate code that leads to better branch prediction.
-//  - DAWN_UNUSED(EXPR): Prevents unused variable/expression warnings on EXPR.
-//  - DAWN_UNUSED_FUNC(FUNC): Prevents unused function warnings on FUNC.
-//  - DAWN_DECLARE_UNUSED:    Prevents unused function warnings a subsequent declaration.
-//  Both DAWN_UNUSED_FUNC and DAWN_DECLARE_UNUSED may be necessary, e.g. to suppress clang's
-//  unneeded-internal-declaration warning.
 
-// Clang and GCC, check for __clang__ too to catch clang-cl masquarading as MSVC
-#if defined(__GNUC__) || defined(__clang__)
+// DAWN_COMPILER_IS(CLANG|GCC|MSVC): Compiler detection
+//
+// Note: clang masquerades as GCC on POSIX and as MSVC on Windows. It must be checked first.
 #if defined(__clang__)
 #define DAWN_COMPILER_IS_CLANG 1
-#else
+#define DAWN_COMPILER_IS_GCC 0
+#define DAWN_COMPILER_IS_MSVC 0
+#elif defined(__GNUC__)
+#define DAWN_COMPILER_IS_CLANG 0
 #define DAWN_COMPILER_IS_GCC 1
-#endif
-
-#define DAWN_BUILTIN_UNREACHABLE() __builtin_unreachable()
-#define DAWN_LIKELY(x) __builtin_expect(!!(x), 1)
-#define DAWN_UNLIKELY(x) __builtin_expect(!!(x), 0)
-
-#if !defined(__has_cpp_attribute)
-#define __has_cpp_attribute(name) 0
-#endif
-
-#define DAWN_DECLARE_UNUSED __attribute__((unused))
-#if defined(NDEBUG)
-#define DAWN_FORCE_INLINE inline __attribute__((always_inline))
-#endif
-#define DAWN_NOINLINE __attribute__((noinline))
-
-// MSVC
+#define DAWN_COMPILER_IS_MSVC 0
 #elif defined(_MSC_VER)
+#define DAWN_COMPILER_IS_CLANG 0
+#define DAWN_COMPILER_IS_GCC 0
 #define DAWN_COMPILER_IS_MSVC 1
-
-#define DAWN_BUILTIN_UNREACHABLE() __assume(false)
-
-#define DAWN_DECLARE_UNUSED
-#if defined(NDEBUG)
-#define DAWN_FORCE_INLINE __forceinline
-#endif
-#define DAWN_NOINLINE __declspec(noinline)
-
 #else
 #error "Unsupported compiler"
 #endif
 
-// Attribute related macros based on Chromium's version in:
-//   base/compiler_specific.h
-#if defined(__has_attribute)
-#define DAWN_HAS_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define DAWN_HAS_ATTRIBUTE(x) 0
-#endif
-
-// Sanitizers annotations.
-#if DAWN_HAS_ATTRIBUTE(no_sanitize)
-#define DAWN_NO_SANITIZE(what) __attribute__((no_sanitize(what)))
-#endif
-#if !defined(DAWN_NO_SANITIZE)
-#define DAWN_NO_SANITIZE(what)
-#endif
-
-// This section defines other compiler macros to 0 to avoid undefined macro usage error.
-#if !defined(DAWN_COMPILER_IS_CLANG)
-#define DAWN_COMPILER_IS_CLANG 0
-#endif
-#if !defined(DAWN_COMPILER_IS_GCC)
-#define DAWN_COMPILER_IS_GCC 0
-#endif
-#if !defined(DAWN_COMPILER_IS_MSVC)
-#define DAWN_COMPILER_IS_MSVC 0
-#endif
-
 // Use #if DAWN_COMPILER_IS(XXX) for compiler specific code.
 // Do not use #ifdef or the naked macro DAWN_COMPILER_IS_XXX.
 // This can help avoid common mistakes like not including "Compiler.h" and falling into unwanted
 // code block as usage of undefined macro "function" will be blocked by the compiler.
 #define DAWN_COMPILER_IS(X) (1 == DAWN_COMPILER_IS_##X)
 
-// It seems that (void) EXPR works on all compilers to silence the unused variable warning.
+// DAWN_HAS_ATTRIBUTE
+//
+// A wrapper around `__has_attribute`. This test whether its operand is recognized by the compiler.
+#if defined(__has_attribute)
+#define DAWN_HAS_ATTRIBUTE(x) __has_attribute(x)
+#else
+#define DAWN_HAS_ATTRIBUTE(x) 0
+#endif
+
+// DAWN_BUILTIN_UNREACHABLE()
+//
+// Hints the compiler that a code path is unreachable.
+#if DAWN_COMPILER_IS(MSVC)
+#define DAWN_BUILTIN_UNREACHABLE() __assume(false)
+#else
+#define DAWN_BUILTIN_UNREACHABLE() __builtin_unreachable()
+#endif
+
+// DAWN_LIKELY(EXPR)
+//
+// Where available, hints the compiler that the expression will be true to help it generate code
+// that leads to better branch prediction.
+#if DAWN_COMPILER_IS(GCC) || DAWN_COMPILER_IS(CLANG)
+#define DAWN_LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define DAWN_LIKELY(x) (x)
+#endif
+
+// DAWN_UNLIKELY(EXPR)
+//
+// Where available, hints the compiler that the expression will be false to help it generate code
+// that leads to better branch prediction.
+#if DAWN_COMPILER_IS(GCC) || DAWN_COMPILER_IS(CLANG)
+#define DAWN_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define DAWN_UNLIKELY(x) (x)
+#endif
+
+// DAWN_DECLARE_UNUSED
+//
+// Prevents unused function warnings a subsequent declaration.
+#if DAWN_COMPILER_IS(GCC) || DAWN_COMPILER_IS(CLANG)
+#define DAWN_DECLARE_UNUSED __attribute__((unused))
+#else
+#define DAWN_DECLARE_UNUSED
+#endif
+
+// DAWN_NO_SANITIZE(instrumentation)
+//
+// Annotate a function or a global variable declaration to specify that a particular instrumentation
+// or set of instrumentations should not be applied.
+#if DAWN_HAS_ATTRIBUTE(no_sanitize)
+#define DAWN_NO_SANITIZE(instrumentation) __attribute__((no_sanitize(instrumentation)))
+#else
+#define DAWN_NO_SANITIZE(instrumentation)
+#endif
+
+// DAWN_UNUSED
+//
+// Prevents unused variable/expression warnings on EXPR.
 #define DAWN_UNUSED(EXPR) (void)EXPR
-// Likewise using static asserting on sizeof(&FUNC) seems to make it tagged as used
+
+// DAWN_UNUSED_FUNC
+//
+// Prevents unused variable/expression warnings on FUNC.
 #define DAWN_UNUSED_FUNC(FUNC) static_assert(sizeof(&FUNC) == sizeof(void (*)()))
 
-// Add noop replacements for macros for features that aren't supported by the compiler.
-#if !defined(DAWN_LIKELY)
-#define DAWN_LIKELY(X) X
+// DAWN_NOINLINE
+//
+// Annotate a function indicating it should not be inlined. Use like:
+// DAWN_NOINLINE void DoStuff() { ... }
+#if defined(__clang__) && DAWN_HAS_ATTRIBUTE(noinline)
+#define DAWN_NOINLINE [[clang::noinline]]
+#elif defined(__GNUC__) && DAWN_HAS_ATTRIBUTE(noinline)
+#define DAWN_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER)
+#define DAWN_NOINLINE __declspec(noinline)
+#else
+#define DAWN_NOINLINE
 #endif
-#if !defined(DAWN_UNLIKELY)
-#define DAWN_UNLIKELY(X) X
-#endif
-#if !defined(DAWN_FORCE_INLINE)
+
+// DAWN_FORCE_INLINE
+//
+// Annotate a function indicating it should really never be inline, even in debug mode.
+#if DAWN_COMPILER_IS(CLANG) && defined(NDEBUG) && DAWN_HAS_ATTRIBUTE(always_inline)
+#define DAWN_FORCE_INLINE [[clang::always_inline]] inline
+#elif DAWN_COMPILER_IS(GCC) && defined(NDEBUG) && DAWN_HAS_ATTRIBUTE(always_inline)
+#define DAWN_FORCE_INLINE inline __attribute__((__always_inline__))
+#elif DAWN_COMPILER_IS(MSVC) && defined(NDEBUG)
+#define DAWN_FORCE_INLINE __forceinline
+#else
 #define DAWN_FORCE_INLINE inline
 #endif
-#if !defined(DAWN_NOINLINE)
-#define DAWN_NOINLINE
+
+// DAWN_TRIVIAL_ABI
+//
+// Marks a type as being eligible for the "trivial" ABI despite having a
+// non-trivial destructor or copy/move constructor. Such types can be relocated
+// after construction by simply copying their memory, which makes them eligible
+// to be passed in registers. The canonical example is std::unique_ptr.
+//
+// Use with caution; this has some subtle effects on constructor/destructor
+// ordering and will be very incorrect if the type relies on its address
+// remaining constant. When used as a function argument (by value), the value
+// may be constructed in the caller's stack frame, passed in a register, and
+// then used and destructed in the callee's stack frame. A similar thing can
+// occur when values are returned.
+//
+// TRIVIAL_ABI is not needed for types which have a trivial destructor and
+// copy/move constructors, such as base::TimeTicks and other POD.
+//
+// It is also not likely to be effective on types too large to be passed in one
+// or two registers on typical target ABIs.
+//
+// See also:
+//   https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
+//   https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html
+#if DAWN_COMPILER_IS(CLANG) && DAWN_HAS_ATTRIBUTE(trivial_abi)
+#define DAWN_TRIVIAL_ABI [[clang::trivial_abi]]
+#else
+#define DAWN_TRIVIAL_ABI
+#endif
+
+// DAWN_CONSTINIT
+//
+// Requires constant initialization. See constinit in C++20. Allows to rely on a variable being
+// initialized before execution, and not requiring a global constructor.
+#if DAWN_HAS_ATTRIBUTE(require_constant_initialization)
+#define DAWN_CONSTINIT __attribute__((require_constant_initialization))
+#else
+#define DAWN_CONSTINIT
+#endif
+
+// DAWN_GSL_POINTER
+//
+// In [lifetime safety], If you write a custom class wrapping a pointer, the [[gsl::Owner]] and
+// [[gsl::Pointer]] can help the compiler to know if it acquired ownership over the pointee, or not.
+// The compiler is then able to emit useful warning.
+//
+// [lifetime safety]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1179r0.pdf
+#if DAWN_COMPILER_IS(CLANG)
+#define DAWN_GSL_POINTER [[gsl::Pointer]]
+#else
+#define DAWN_GSL_POINTER
+#endif
+
+// DAWN_CONSTEXPR_DTOR
+//
+// Constexpr destructors were introduced in C++20. Dawn's minimum supported C++ version is C++17.
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L
+#define DAWN_CONSTEXPR_DTOR constexpr
+#else
+#define DAWN_CONSTEXPR_DTOR
+#endif
+
+// DAWN_ATTRIBUTE_RETURNS_NONNULL
+//
+// Tells the compiler that a function never returns a null pointer. Sourced from Abseil's
+// `attributes.h`.
+#if DAWN_HAS_ATTRIBUTE(returns_nonnull)
+#define DAWN_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
+#else
+#define DAWN_ATTRIBUTE_RETURNS_NONNULL
 #endif
 
 #endif  // SRC_DAWN_COMMON_COMPILER_H_
diff --git a/src/dawn/native/ToBackend.h b/src/dawn/native/ToBackend.h
index d1c8599..a28e3f4 100644
--- a/src/dawn/native/ToBackend.h
+++ b/src/dawn/native/ToBackend.h
@@ -29,6 +29,7 @@
 #define SRC_DAWN_NATIVE_TOBACKEND_H_
 
 #include "dawn/native/Forward.h"
+#include "partition_alloc/pointers/raw_ptr.h"
 
 namespace dawn::native {
 
@@ -166,6 +167,11 @@
 }
 
 template <typename BackendTraits, typename T>
+typename ToBackendTraits<T, BackendTraits>::BackendType* ToBackendBase(raw_ptr<T> common) {
+    return reinterpret_cast<typename ToBackendTraits<T, BackendTraits>::BackendType*>(common.get());
+}
+
+template <typename BackendTraits, typename T>
 const typename ToBackendTraits<T, BackendTraits>::BackendType* ToBackendBase(const T* common) {
     return reinterpret_cast<const typename ToBackendTraits<T, BackendTraits>::BackendType*>(common);
 }
diff --git a/src/dawn/partition_alloc/BUILD.gn b/src/dawn/partition_alloc/BUILD.gn
index b962325..fdd2db8 100644
--- a/src/dawn/partition_alloc/BUILD.gn
+++ b/src/dawn/partition_alloc/BUILD.gn
@@ -35,7 +35,11 @@
 
   source_set("raw_ptr") {
     public_configs = [ ":public_includes" ]
-    public = [ "partition_alloc/pointers/raw_ptr.h" ]
+    public = [
+      "partition_alloc/pointers/raw_ptr.h",
+      "partition_alloc/pointers/raw_ptr_exclusion.h",
+      "partition_alloc/pointers/raw_ref.h",
+    ]
   }
 
   source_set("buildflags") {
diff --git a/src/dawn/partition_alloc/README.md b/src/dawn/partition_alloc/README.md
index 985442f..76e450e 100644
--- a/src/dawn/partition_alloc/README.md
+++ b/src/dawn/partition_alloc/README.md
@@ -9,15 +9,3 @@
 Moreover, partition_alloc is still depending on chromium's //build for now. It
 means it can only be used inside Dawn who has the same dependency. It can't be
 used outside.
-
-# TODO(arthursonzogni): https://crbug.com/1464560
-
-Provide additional files:
-- partition_alloc/pointers/raw_ptr_cast.h
-- partition_alloc/pointers/raw_ptr_exclusion.h
-- partition_alloc/pointers/raw_ref.h
-
-Expand the raw_ptr implementation:
-- RawPtrTraits
-- raw_ptr::* extra functions.
-- etc...
diff --git a/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr.h b/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr.h
index fd92983..f9dd87a 100644
--- a/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr.h
+++ b/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr.h
@@ -34,14 +34,369 @@
 //
 // Here, dawn provides a "no-op" implementation, because partition_alloc
 // dependendency is missing.
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <type_traits>
+#include <utility>
+#include "build/build_config.h"
+#include "build/buildflag.h"
+#include "dawn/common/Compiler.h"
+#include "partition_alloc/pointers/raw_ptr_exclusion.h"
+
+namespace partition_alloc::internal {
+using RawPtrTraits = int;
+
+// This type trait verifies a type can be used as a pointer offset.
 //
-// TODO(arthursonzogni): https://crbug.com/1464560, Complete the "no-op"
-// implementation.
+// We support pointer offsets in signed (ptrdiff_t) or unsigned (size_t) values.
+// Smaller types are also allowed.
+template <typename Z>
+static constexpr bool is_offset_type = std::is_integral_v<Z> && sizeof(Z) <= sizeof(ptrdiff_t);
 
-constexpr int DanglingUntriaged = 0;
-constexpr int DisableDanglingPtrDetection = 0;
+// `raw_ptr<T>` is a non-owning smart pointer that has improved memory-safety
+// over raw pointers. See the documentation for details:
+// https://source.chromium.org/chromium/chromium/src/+/main:base/memory/raw_ptr.md
+//
+// raw_ptr<T> is marked as [[gsl::Pointer]] which allows the compiler to catch
+// some bugs where the raw_ptr holds a dangling pointer to a temporary object.
+// However the [[gsl::Pointer]] analysis expects that such types do not have a
+// non-default move constructor/assignment. Thus, it's possible to get an error
+// where the pointer is not actually dangling, and have to work around the
+// compiler. We have not managed to construct such an example in Chromium yet.
+template <typename T, RawPtrTraits Traits = 0>
+class DAWN_TRIVIAL_ABI DAWN_GSL_POINTER raw_ptr {
+  public:
+    DAWN_FORCE_INLINE constexpr raw_ptr() noexcept = default;
 
-template <typename T, int Traits = 0>
-using raw_ptr = T*;
+    // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr.
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr raw_ptr(std::nullptr_t) noexcept {}
+
+    // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr.
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr raw_ptr(T* p) noexcept : wrapped_ptr_(p) {}
+
+    // Deliberately implicit in order to support implicit upcast.
+    template <typename U,
+              typename Unused = std::enable_if_t<std::is_convertible_v<U*, T*> &&
+                                                 !std::is_void_v<typename std::remove_cv<T>::type>>>
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr raw_ptr(const raw_ptr<U, Traits>& ptr) noexcept
+        : wrapped_ptr_(ptr.get()) {}
+
+    // Deliberately implicit in order to support implicit upcast.
+    template <typename U,
+              typename Unused = std::enable_if_t<std::is_convertible_v<U*, T*> &&
+                                                 !std::is_void_v<typename std::remove_cv<T>::type>>>
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr raw_ptr(raw_ptr<U, Traits>&& ptr) noexcept
+        : wrapped_ptr_(ptr.wrapped_ptr_) {
+        // Contrary to T*, we do implement "zero on move". This avoids the behavior to diverge
+        // depending on whether this implementation or PartitionAlloc's one is used.
+        ptr.wrapped_ptr_ = nullptr;
+    }
+
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator=(std::nullptr_t) noexcept {
+        wrapped_ptr_ = nullptr;
+        return *this;
+    }
+
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator=(T* p) noexcept {
+        wrapped_ptr_ = p;
+        return *this;
+    }
+
+    // Upcast assignment
+    template <typename U,
+              typename Unused = std::enable_if_t<std::is_convertible_v<U*, T*> &&
+                                                 !std::is_void_v<typename std::remove_cv<T>::type>>>
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator=(const raw_ptr<U, Traits>& ptr) noexcept {
+        wrapped_ptr_ = ptr.wrapped_ptr_;
+        return *this;
+    }
+
+    template <typename U,
+              typename Unused = std::enable_if_t<std::is_convertible_v<U*, T*> &&
+                                                 !std::is_void_v<typename std::remove_cv<T>::type>>>
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator=(raw_ptr<U, Traits>&& ptr) noexcept {
+        wrapped_ptr_ = ptr.wrapped_ptr_;
+        ptr.wrapped_ptr_ = nullptr;
+        return *this;
+    }
+
+    DAWN_FORCE_INLINE constexpr explicit operator bool() const {
+        return static_cast<bool>(wrapped_ptr_);
+    }
+
+    template <typename U = T,
+              typename Unused = std::enable_if_t<!std::is_void_v<typename std::remove_cv<U>::type>>>
+    DAWN_FORCE_INLINE constexpr U& operator*() const {
+        return *wrapped_ptr_;
+    }
+    DAWN_FORCE_INLINE constexpr T* operator->() const { return wrapped_ptr_; }
+
+    // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr.
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr operator T*() const { return wrapped_ptr_; }
+
+    template <typename U>
+    DAWN_FORCE_INLINE constexpr explicit operator U*() const {
+        // This operator may be invoked from static_cast, meaning the types may not be implicitly
+        // convertible, hence the need for static_cast here.
+        return static_cast<U*>(wrapped_ptr_);
+    }
+
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator++() {
+        wrapped_ptr_++;
+        return *this;
+    }
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator--() {
+        wrapped_ptr_--;
+        return *this;
+    }
+    DAWN_FORCE_INLINE constexpr raw_ptr operator++(int /* post_increment */) {
+        return ++wrapped_ptr_;
+    }
+    DAWN_FORCE_INLINE constexpr raw_ptr operator--(int /* post_decrement */) {
+        return --wrapped_ptr_;
+    }
+    template <typename Z, typename = std::enable_if_t<partition_alloc::internal::is_offset_type<Z>>>
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator+=(Z delta) {
+        wrapped_ptr_ += delta;
+        return *this;
+    }
+    template <typename Z, typename = std::enable_if_t<partition_alloc::internal::is_offset_type<Z>>>
+    DAWN_FORCE_INLINE constexpr raw_ptr& operator-=(Z delta) {
+        wrapped_ptr_ -= delta;
+        return *this;
+    }
+
+    template <
+        typename Z,
+        typename U = T,
+        typename Unused = std::enable_if_t<!std::is_void_v<typename std::remove_cv<U>::type> &&
+                                           partition_alloc::internal::is_offset_type<Z>>>
+    U& operator[](Z delta) const {
+        return wrapped_ptr_[delta];
+    }
+
+    // Stop referencing the underlying pointer and free its memory. Compared to raw delete calls,
+    // this avoids the raw_ptr to be temporarily dangling during the free operation, which will lead
+    // to taking the slower path that involves quarantine.
+    DAWN_FORCE_INLINE constexpr void ClearAndDelete() noexcept { delete ExtractAsDangling(); }
+    DAWN_FORCE_INLINE constexpr void ClearAndDeleteArray() noexcept {
+        delete[] ExtractAsDangling();
+    }
+
+    // Clear the underlying pointer and return a temporary raw_ptr instance allowed to dangle.
+    // This can be useful in cases such as:
+    // ```
+    //  ptr.ExtractAsDangling()->SelfDestroy();
+    // ```
+    // ```
+    //  c_style_api_do_something_and_destroy(ptr.ExtractAsDangling());
+    // ```
+    // NOTE, avoid using this method as it indicates an error-prone memory ownership pattern. If
+    // possible, use smart pointers like std::unique_ptr<> instead of raw_ptr<>. If you have to use
+    // it, avoid saving the return value in a long-lived variable (or worse, a field)! It's meant to
+    // be used as a temporary, to be passed into a cleanup & freeing function, and destructed at the
+    // end of the statement.
+    DAWN_FORCE_INLINE constexpr raw_ptr<T, Traits> ExtractAsDangling() noexcept {
+        T* ptr = wrapped_ptr_;
+        wrapped_ptr_ = nullptr;
+        return raw_ptr(ptr);
+    }
+
+    // Comparison operators between raw_ptr and raw_ptr<U>/U*/std::nullptr_t.  Strictly speaking,
+    // it is not necessary to provide these: the compiler can use the conversion operator implicitly
+    // to allow comparisons to fall back to comparisons between raw pointers. However, `operator
+    // T*`/`operator U*` may perform safety checks with a higher runtime cost, so to avoid this,
+    // provide explicit comparison operators for all combinations of parameters.
+
+    // Comparisons between `raw_ptr`s. This unusual declaration and separate definition below is
+    // because `GetForComparison()` is a private method. The more conventional approach of defining
+    // a comparison operator between `raw_ptr` and `raw_ptr<U>` in the friend declaration itself
+    // does not work, because a comparison operator defined inline would not be allowed to call
+    // `raw_ptr<U>`'s private `GetForComparison()` method.
+    template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2>
+    friend bool operator==(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs);
+    template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2>
+    friend bool operator!=(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs);
+    template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2>
+    friend bool operator<(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs);
+    template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2>
+    friend bool operator>(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs);
+    template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2>
+    friend bool operator<=(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs);
+    template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2>
+    friend bool operator>=(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs);
+
+    // Comparisons with U*. These operators also handle the case where the RHS is T*.
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator==(const raw_ptr& lhs, U* rhs) {
+        return lhs.wrapped_ptr_ == rhs;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator!=(const raw_ptr& lhs, U* rhs) {
+        return !(lhs == rhs);
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator==(U* lhs, const raw_ptr& rhs) {
+        return rhs == lhs;  // Reverse order to call the operator above.
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator!=(U* lhs, const raw_ptr& rhs) {
+        return rhs != lhs;  // Reverse order to call the operator above.
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator<(const raw_ptr& lhs, U* rhs) {
+        return lhs.wrapped_ptr_ < rhs;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator<=(const raw_ptr& lhs, U* rhs) {
+        return lhs.wrapped_ptr_ <= rhs;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator>(const raw_ptr& lhs, U* rhs) {
+        return lhs.wrapped_ptr_ > rhs;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator>=(const raw_ptr& lhs, U* rhs) {
+        return lhs.wrapped_ptr_ >= rhs;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator<(U* lhs, const raw_ptr& rhs) {
+        return lhs < rhs.wrapped_ptr_;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator<=(U* lhs, const raw_ptr& rhs) {
+        return lhs <= rhs.wrapped_ptr_;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator>(U* lhs, const raw_ptr& rhs) {
+        return lhs > rhs.wrapped_ptr_;
+    }
+    template <typename U>
+    DAWN_FORCE_INLINE friend bool operator>=(U* lhs, const raw_ptr& rhs) {
+        return lhs >= rhs.wrapped_ptr_;
+    }
+
+    // Comparisons with `std::nullptr_t`.
+    DAWN_FORCE_INLINE friend bool operator==(const raw_ptr& lhs, std::nullptr_t) { return !lhs; }
+    DAWN_FORCE_INLINE friend bool operator!=(const raw_ptr& lhs, std::nullptr_t) {
+        return !!lhs;  // Use !! otherwise the costly implicit cast will be used.
+    }
+    DAWN_FORCE_INLINE friend bool operator==(std::nullptr_t, const raw_ptr& rhs) { return !rhs; }
+    DAWN_FORCE_INLINE friend bool operator!=(std::nullptr_t, const raw_ptr& rhs) {
+        return !!rhs;  // Use !! otherwise the costly implicit cast will be used.
+    }
+
+    DAWN_FORCE_INLINE friend constexpr void swap(raw_ptr& lhs, raw_ptr& rhs) noexcept {
+        std::swap(lhs.wrapped_ptr_, rhs.wrapped_ptr_);
+    }
+
+    // This getter is meant *only* for situations where the pointer is meant to be compared
+    // (guaranteeing no dereference or extraction outside of this class). Any verifications can and
+    // should be skipped for performance reasons.
+    DAWN_FORCE_INLINE constexpr T* get() const { return wrapped_ptr_; }
+
+  private:
+    // This field is not a raw_ptr<> because, because we are implementing it.
+    //
+    // Please note we do initialize the pointers on construction. It means we don't have
+    // uninitialized raw_ptr<T>. This is important if we don't want Chrome and Dawn standalone to
+    // behave differently than other Dawn embedders.
+    RAW_PTR_EXCLUSION T* wrapped_ptr_ = nullptr;
+
+    template <typename U, RawPtrTraits R>
+    friend class raw_ptr;
+};
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator==(const raw_ptr<U, Traits1>& lhs, const raw_ptr<V, Traits2>& rhs) {
+    return lhs.wrapped_ptr_ == rhs.wrapped_ptr_;
+}
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator!=(const raw_ptr<U, Traits1>& lhs, const raw_ptr<V, Traits2>& rhs) {
+    return !(lhs == rhs);
+}
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator<(const raw_ptr<U, Traits1>& lhs, const raw_ptr<V, Traits2>& rhs) {
+    return lhs.wrapped_ptr_ < rhs.wrapped_ptr_;
+}
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator>(const raw_ptr<U, Traits1>& lhs, const raw_ptr<V, Traits2>& rhs) {
+    return lhs.wrapped_ptr_ > rhs.wrapped_ptr_;
+}
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator<=(const raw_ptr<U, Traits1>& lhs, const raw_ptr<V, Traits2>& rhs) {
+    return lhs.wrapped_ptr_ <= rhs.wrapped_ptr_;
+}
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator>=(const raw_ptr<U, Traits1>& lhs, const raw_ptr<V, Traits2>& rhs) {
+    return lhs.wrapped_ptr_ >= rhs.wrapped_ptr_;
+}
+
+}  // namespace partition_alloc::internal
+
+using partition_alloc::internal::raw_ptr;
+using partition_alloc::internal::RawPtrTraits;
+constexpr RawPtrTraits DisableDanglingPtrDetection = 0;
+constexpr RawPtrTraits DanglingUntriaged = 0;
+constexpr RawPtrTraits AllowPtrArithmetic = 0;
+
+namespace std {
+
+// Override so set/map lookups do not create extra raw_ptr. This also allows dangling pointers to be
+// used for lookup.
+template <typename T, RawPtrTraits Traits>
+struct less<raw_ptr<T, Traits>> {
+    using Impl = typename raw_ptr<T, Traits>::Impl;
+    using is_transparent = void;
+
+    bool operator()(const raw_ptr<T, Traits>& lhs, const raw_ptr<T, Traits>& rhs) const {
+        return lhs < rhs;
+    }
+    bool operator()(T* lhs, const raw_ptr<T, Traits>& rhs) const { return lhs < rhs; }
+    bool operator()(const raw_ptr<T, Traits>& lhs, T* rhs) const { return lhs < rhs; }
+};
+
+// Define for cases where raw_ptr<T> holds a pointer to an array of type T. This is consistent with
+// definition of std::iterator_traits<T*>. Algorithms like std::binary_search need that.
+template <typename T, RawPtrTraits Traits>
+struct iterator_traits<raw_ptr<T, Traits>> {
+    using difference_type = ptrdiff_t;
+    using value_type = std::remove_cv_t<T>;
+    using pointer = T*;
+    using reference = T&;
+    using iterator_category = std::random_access_iterator_tag;
+};
+
+// Specialize std::pointer_traits. The latter is required to obtain the underlying raw pointer in
+// the std::to_address(pointer) overload. Implementing the pointer_traits is the standard blessed
+// way to customize `std::to_address(pointer)` in C++20 [1].
+//
+// [1] https://wg21.link/pointer.traits.optmem
+template <typename T, RawPtrTraits Traits>
+struct pointer_traits<raw_ptr<T, Traits>> {
+    using pointer = raw_ptr<T, Traits>;
+    using element_type = T;
+    using difference_type = ptrdiff_t;
+
+    template <typename U>
+    using rebind = ::raw_ptr<U, Traits>;
+
+    static constexpr pointer pointer_to(element_type& r) noexcept { return pointer(&r); }
+    static constexpr element_type* to_address(pointer p) noexcept { return p.get(); }
+};
+
+}  // namespace std
 
 #endif  // SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_PTR_H_
diff --git a/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr_exclusion.h b/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr_exclusion.h
new file mode 100644
index 0000000..d86625d
--- /dev/null
+++ b/src/dawn/partition_alloc/partition_alloc/pointers/raw_ptr_exclusion.h
@@ -0,0 +1,43 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_
+#define SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_
+
+// Marks a field as excluded from the `raw_ptr<T>` usage enforcement via
+// Chromium Clang plugin.
+//
+// Example:
+//     RAW_PTR_EXCLUSION Foo* foo_;
+//
+// `RAW_PTR_EXCLUSION` should be avoided, as exclusions makes it significantly easier for any bug
+// involving the pointer to become a security vulnerability. For additional guidance please see the
+// "When to use raw_ptr<T>" section of:
+// https://source.chromium.org/chromium/chromium/src/+/main:base/memory/raw_ptr.md
+#define RAW_PTR_EXCLUSION
+
+#endif  // SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_
diff --git a/src/dawn/partition_alloc/partition_alloc/pointers/raw_ref.h b/src/dawn/partition_alloc/partition_alloc/pointers/raw_ref.h
new file mode 100644
index 0000000..ae0ad66
--- /dev/null
+++ b/src/dawn/partition_alloc/partition_alloc/pointers/raw_ref.h
@@ -0,0 +1,308 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_REF_H_
+#define SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_REF_H_
+
+// `raw_ref<T>` is a non-owning smart pointer that has improved memory-safety over raw reference.
+// See the documentation for details:
+// https://source.chromium.org/chromium/chromium/src/+/main:base/memory/raw_ptr.md
+//
+// Here, dawn provides a "no-op" implementation, because partition_alloc dependendency is missing.
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "dawn/common/Compiler.h"
+#include "partition_alloc/pointers/raw_ptr.h"
+
+namespace partition_alloc::internal {
+
+template <class T, RawPtrTraits Traits>
+class raw_ref;
+
+template <class T>
+struct is_raw_ref : std::false_type {};
+
+template <class T, RawPtrTraits Traits>
+struct is_raw_ref<raw_ref<T, Traits>> : std::true_type {};
+
+template <class T>
+constexpr inline bool is_raw_ref_v = is_raw_ref<T>::value;
+
+// A smart pointer for a pointer which can not be null, and which provides Use-after-Free protection
+// in the same ways as raw_ptr. This class acts like a combination of std::reference_wrapper and
+// raw_ptr.
+//
+// See raw_ptr and Chrome's //base/memory/raw_ptr.md for more details on the Use-after-Free
+// protection.
+//
+// # Use after move
+//
+// The raw_ref type will abort if used after being moved.
+//
+// # Constness
+//
+// Use a `const raw_ref<T>` when the smart pointer should not be able to rebind to a new reference.
+// Use a `const raw_ref<const T>` do the same for a const reference, which is like `const T&`.
+//
+// Unlike a native `T&` reference, a mutable `raw_ref<T>` can be changed independent of the
+// underlying `T`, similar to `std::reference_wrapper`. That means the reference inside it can be
+// moved and reassigned.
+template <class T, RawPtrTraits Traits = 0>
+class DAWN_TRIVIAL_ABI DAWN_GSL_POINTER raw_ref {
+  public:
+    // Construct a raw_ref from a pointer, which must not be null.
+    DAWN_FORCE_INLINE constexpr static raw_ref from_ptr(T* ptr) noexcept { return raw_ref(*ptr); }
+
+    // Construct a raw_ref from a reference.
+    DAWN_FORCE_INLINE constexpr explicit raw_ref(T& p) noexcept : inner_(std::addressof(p)) {}
+
+    // Assign a new reference to the raw_ref, replacing the existing reference.
+    DAWN_FORCE_INLINE constexpr raw_ref& operator=(T& p) noexcept {
+        inner_.operator=(&p);
+        return *this;
+    }
+
+    // Disallow holding references to temporaries.
+    explicit raw_ref(const T&& p) = delete;
+    raw_ref& operator=(const T&& p) = delete;
+
+    DAWN_FORCE_INLINE constexpr raw_ref(const raw_ref& p) noexcept : inner_(p.inner_) {}
+
+    DAWN_FORCE_INLINE constexpr raw_ref(raw_ref&& p) noexcept : inner_(std::move(p.inner_)) {
+        p.inner_ = nullptr;
+    }
+
+    DAWN_FORCE_INLINE constexpr raw_ref& operator=(const raw_ref& p) noexcept {
+        inner_.operator=(p.inner_);
+        return *this;
+    }
+
+    DAWN_FORCE_INLINE constexpr raw_ref& operator=(raw_ref&& p) noexcept {
+        inner_.operator=(std::move(p.inner_));
+        p.inner_ = nullptr;
+        return *this;
+    }
+
+    // Deliberately implicit in order to support implicit upcast.
+    // Delegate cross-kind conversion to the inner raw_ptr, which decides when to allow it.
+    template <class U,
+              RawPtrTraits PassedTraits,
+              class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr raw_ref(const raw_ref<U, PassedTraits>& p) noexcept
+        : inner_(p.inner_) {}
+    // Deliberately implicit in order to support implicit upcast.
+    // Delegate cross-kind conversion to the inner raw_ptr, which decides when to allow it.
+    template <class U,
+              RawPtrTraits PassedTraits,
+              class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
+    // NOLINTNEXTLINE
+    DAWN_FORCE_INLINE constexpr raw_ref(raw_ref<U, PassedTraits>&& p) noexcept
+        : inner_(std::move(p.inner_)) {
+        p.inner_ = nullptr;
+    }
+
+    // Upcast assignment
+    // Delegate cross-kind conversion to the inner raw_ptr, which decides when to allow it.
+    template <class U,
+              RawPtrTraits PassedTraits,
+              class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
+    DAWN_FORCE_INLINE constexpr raw_ref& operator=(const raw_ref<U, PassedTraits>& p) noexcept {
+        inner_.operator=(p.inner_);
+        return *this;
+    }
+    // Delegate cross-kind conversion to the inner raw_ptr, which decides when to
+    // allow it.
+    template <class U,
+              RawPtrTraits PassedTraits,
+              class = std::enable_if_t<std::is_convertible_v<U&, T&>>>
+    DAWN_FORCE_INLINE constexpr raw_ref& operator=(raw_ref<U, PassedTraits>&& p) noexcept {
+        inner_.operator=(std::move(p.inner_));
+        p.inner_ = nullptr;
+        return *this;
+    }
+
+    DAWN_FORCE_INLINE constexpr T& operator*() const { return inner_.operator*(); }
+    DAWN_FORCE_INLINE constexpr T& get() const { return *inner_.get(); }
+    DAWN_FORCE_INLINE constexpr T* operator->() const DAWN_ATTRIBUTE_RETURNS_NONNULL {
+        return inner_.operator->();
+    }
+
+    DAWN_FORCE_INLINE friend constexpr void swap(raw_ref& lhs, raw_ref& rhs) noexcept {
+        swap(lhs.inner_, rhs.inner_);
+    }
+
+    template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+    friend bool operator==(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs);
+    template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+    friend bool operator!=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs);
+    template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+    friend bool operator<(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs);
+    template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+    friend bool operator>(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs);
+    template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+    friend bool operator<=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs);
+    template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+    friend bool operator>=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs);
+
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator==(const raw_ref& lhs, const U& rhs) {
+        return lhs.inner_ == &rhs;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator!=(const raw_ref& lhs, const U& rhs) {
+        return lhs.inner_ != &rhs;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator<(const raw_ref& lhs, const U& rhs) {
+        return lhs.inner_ < &rhs;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator>(const raw_ref& lhs, const U& rhs) {
+        return lhs.inner_ > &rhs;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator<=(const raw_ref& lhs, const U& rhs) {
+        return lhs.inner_ <= &rhs;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator>=(const raw_ref& lhs, const U& rhs) {
+        return lhs.inner_ >= &rhs;
+    }
+
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator==(const U& lhs, const raw_ref& rhs) {
+        return &lhs == rhs.inner_;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator!=(const U& lhs, const raw_ref& rhs) {
+        return &lhs != rhs.inner_;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator<(const U& lhs, const raw_ref& rhs) {
+        return &lhs < rhs.inner_;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator>(const U& lhs, const raw_ref& rhs) {
+        return &lhs > rhs.inner_;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator<=(const U& lhs, const raw_ref& rhs) {
+        return &lhs <= rhs.inner_;
+    }
+    template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>>
+    DAWN_FORCE_INLINE friend bool operator>=(const U& lhs, const raw_ref& rhs) {
+        return &lhs >= rhs.inner_;
+    }
+
+  private:
+    template <class U, RawPtrTraits R>
+    friend class raw_ref;
+
+    raw_ptr<T, Traits> inner_;
+};
+
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator==(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) {
+    return lhs.inner_ == rhs.inner_;
+}
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator!=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) {
+    return lhs.inner_ != rhs.inner_;
+}
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator<(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) {
+    return lhs.inner_ < rhs.inner_;
+}
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator>(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) {
+    return lhs.inner_ > rhs.inner_;
+}
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator<=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) {
+    return lhs.inner_ <= rhs.inner_;
+}
+template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2>
+DAWN_FORCE_INLINE bool operator>=(const raw_ref<U, Traits1>& lhs, const raw_ref<V, Traits2>& rhs) {
+    return lhs.inner_ >= rhs.inner_;
+}
+
+// CTAD deduction guide.
+template <class T>
+raw_ref(T&) -> raw_ref<T>;
+template <class T>
+raw_ref(const T&) -> raw_ref<const T>;
+
+}  // namespace partition_alloc::internal
+
+using partition_alloc::internal::raw_ref;
+
+namespace std {
+
+// Override so set/map lookups do not create extra raw_ref. This also allows C++ references to be
+// used for lookup.
+template <typename T, RawPtrTraits Traits>
+struct less<raw_ref<T, Traits>> {
+    using is_transparent = void;
+
+    bool operator()(const raw_ref<T, Traits>& lhs, const raw_ref<T, Traits>& rhs) const {
+        return lhs < rhs;
+    }
+    bool operator()(T& lhs, const raw_ref<T, Traits>& rhs) const { return lhs < rhs; }
+    bool operator()(const raw_ref<T, Traits>& lhs, T& rhs) const { return lhs < rhs; }
+};
+
+// Specialize std::pointer_traits. The latter is required to obtain the underlying raw pointer in
+// the std::to_address(pointer) overload. Implementing the pointer_traits is the standard blessed
+// way to customize `std::to_address(pointer)` in C++20 [3].
+//
+// [1] https://wg21.link/pointer.traits.optmem
+template <typename T, ::RawPtrTraits Traits>
+struct pointer_traits<::raw_ref<T, Traits>> {
+    using pointer = ::raw_ref<T, Traits>;
+    using element_type = T;
+    using difference_type = ptrdiff_t;
+
+    template <typename U>
+    using rebind = ::raw_ref<U, Traits>;
+
+    static constexpr pointer pointer_to(element_type& r) noexcept { return pointer(r); }
+
+    static constexpr element_type* to_address(pointer p) noexcept {
+        // `raw_ref::get` is used instead of raw_ref::operator*`. It provides GetForExtraction
+        // rather rather than GetForDereference semantics (see raw_ptr.h). This should be used when
+        // we we don't know the memory will be accessed.
+        return &(p.get());
+    }
+};
+
+}  // namespace std
+
+#endif  // SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_REF_H_