blob: f61f04f369ee6971622c82bda5a0fba0df8e1a71 [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_PTR_H_
#define SRC_DAWN_PARTITION_ALLOC_PARTITION_ALLOC_POINTERS_RAW_PTR_H_
// `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
//
// 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 "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.
//
// 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);
// `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;
// 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 LeakedDanglingUntriaged = 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_