blob: ae0ad66cf387dd0a18b207441d79600d96c75bdf [file] [log] [blame]
// 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_