
# Objective The clippy lint `type_complexity` is known not to play well with bevy. It frequently triggers when writing complex queries, and taking the lint's advice of using a type alias almost always just obfuscates the code with no benefit. Because of this, this lint is currently ignored in CI, but unfortunately it still shows up when viewing bevy code in an IDE. As someone who's made a fair amount of pull requests to this repo, I will say that this issue has been a consistent thorn in my side. Since bevy code is filled with spurious, ignorable warnings, it can be very difficult to spot the *real* warnings that must be fixed -- most of the time I just ignore all warnings, only to later find out that one of them was real after I'm done when CI runs. ## Solution Suppress this lint in all bevy crates. This was previously attempted in #7050, but the review process ended up making it more complicated than it needs to be and landed on a subpar solution. The discussion in https://github.com/rust-lang/rust-clippy/pull/10571 explores some better long-term solutions to this problem. Since there is no timeline on when these solutions may land, we should resolve this issue in the meantime by locally suppressing these lints. ### Unresolved issues Currently, these lints are not suppressed in our examples, since that would require suppressing the lint in every single source file. They are still ignored in CI.
482 lines
18 KiB
Rust
482 lines
18 KiB
Rust
#![doc = include_str!("../README.md")]
|
|
#![no_std]
|
|
#![warn(missing_docs)]
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
use core::fmt::{self, Formatter, Pointer};
|
|
use core::{
|
|
cell::UnsafeCell, marker::PhantomData, mem::ManuallyDrop, num::NonZeroUsize, ptr::NonNull,
|
|
};
|
|
|
|
#[derive(Copy, Clone)]
|
|
/// Used as a type argument to [`Ptr`], [`PtrMut`] and [`OwningPtr`] to specify that the pointer is aligned.
|
|
pub struct Aligned;
|
|
#[derive(Copy, Clone)]
|
|
/// Used as a type argument to [`Ptr`], [`PtrMut`] and [`OwningPtr`] to specify that the pointer is not aligned.
|
|
pub struct Unaligned;
|
|
|
|
/// Trait that is only implemented for [`Aligned`] and [`Unaligned`] to work around the lack of ability
|
|
/// to have const generics of an enum.
|
|
pub trait IsAligned: sealed::Sealed {}
|
|
impl IsAligned for Aligned {}
|
|
impl IsAligned for Unaligned {}
|
|
|
|
mod sealed {
|
|
pub trait Sealed {}
|
|
impl Sealed for super::Aligned {}
|
|
impl Sealed for super::Unaligned {}
|
|
}
|
|
|
|
/// Type-erased borrow of some unknown type chosen when constructing this type.
|
|
///
|
|
/// This type tries to act "borrow-like" which means that:
|
|
/// - It should be considered immutable: its target must not be changed while this pointer is alive.
|
|
/// - It must always points to a valid value of whatever the pointee type is.
|
|
/// - The lifetime `'a` accurately represents how long the pointer is valid for.
|
|
/// - Must be sufficiently aligned for the unknown pointee type.
|
|
///
|
|
/// It may be helpful to think of this type as similar to `&'a dyn Any` but without
|
|
/// the metadata and able to point to data that does not correspond to a Rust type.
|
|
#[derive(Copy, Clone)]
|
|
pub struct Ptr<'a, A: IsAligned = Aligned>(NonNull<u8>, PhantomData<(&'a u8, A)>);
|
|
|
|
/// Type-erased mutable borrow of some unknown type chosen when constructing this type.
|
|
///
|
|
/// This type tries to act "borrow-like" which means that:
|
|
/// - Pointer is considered exclusive and mutable. It cannot be cloned as this would lead to
|
|
/// aliased mutability.
|
|
/// - It must always points to a valid value of whatever the pointee type is.
|
|
/// - The lifetime `'a` accurately represents how long the pointer is valid for.
|
|
/// - Must be sufficiently aligned for the unknown pointee type.
|
|
///
|
|
/// It may be helpful to think of this type as similar to `&'a mut dyn Any` but without
|
|
/// the metadata and able to point to data that does not correspond to a Rust type.
|
|
pub struct PtrMut<'a, A: IsAligned = Aligned>(NonNull<u8>, PhantomData<(&'a mut u8, A)>);
|
|
|
|
/// Type-erased Box-like pointer to some unknown type chosen when constructing this type.
|
|
/// Conceptually represents ownership of whatever data is being pointed to and so is
|
|
/// responsible for calling its `Drop` impl. This pointer is _not_ responsible for freeing
|
|
/// the memory pointed to by this pointer as it may be pointing to an element in a `Vec` or
|
|
/// to a local in a function etc.
|
|
///
|
|
/// This type tries to act "borrow-like" like which means that:
|
|
/// - Pointer should be considered exclusive and mutable. It cannot be cloned as this would lead
|
|
/// to aliased mutability and potentially use after free bugs.
|
|
/// - It must always points to a valid value of whatever the pointee type is.
|
|
/// - The lifetime `'a` accurately represents how long the pointer is valid for.
|
|
/// - Must be sufficiently aligned for the unknown pointee type.
|
|
///
|
|
/// It may be helpful to think of this type as similar to `&'a mut ManuallyDrop<dyn Any>` but
|
|
/// without the metadata and able to point to data that does not correspond to a Rust type.
|
|
pub struct OwningPtr<'a, A: IsAligned = Aligned>(NonNull<u8>, PhantomData<(&'a mut u8, A)>);
|
|
|
|
macro_rules! impl_ptr {
|
|
($ptr:ident) => {
|
|
impl<'a> $ptr<'a, Aligned> {
|
|
/// Removes the alignment requirement of this pointer
|
|
pub fn to_unaligned(self) -> $ptr<'a, Unaligned> {
|
|
$ptr(self.0, PhantomData)
|
|
}
|
|
}
|
|
|
|
impl<'a, A: IsAligned> From<$ptr<'a, A>> for NonNull<u8> {
|
|
fn from(ptr: $ptr<'a, A>) -> Self {
|
|
ptr.0
|
|
}
|
|
}
|
|
|
|
impl<A: IsAligned> $ptr<'_, A> {
|
|
/// Calculates the offset from a pointer.
|
|
/// As the pointer is type-erased, there is no size information available. The provided
|
|
/// `count` parameter is in raw bytes.
|
|
///
|
|
/// *See also: [`ptr::offset`][ptr_offset]*
|
|
///
|
|
/// # Safety
|
|
/// - The offset cannot make the existing ptr null, or take it out of bounds for its allocation.
|
|
/// - If the `A` type parameter is [`Aligned`] then the offset must not make the resulting pointer
|
|
/// be unaligned for the pointee type.
|
|
///
|
|
/// [ptr_offset]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
|
|
#[inline]
|
|
pub unsafe fn byte_offset(self, count: isize) -> Self {
|
|
Self(
|
|
NonNull::new_unchecked(self.as_ptr().offset(count)),
|
|
PhantomData,
|
|
)
|
|
}
|
|
|
|
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
|
|
/// As the pointer is type-erased, there is no size information available. The provided
|
|
/// `count` parameter is in raw bytes.
|
|
///
|
|
/// *See also: [`ptr::add`][ptr_add]*
|
|
///
|
|
/// # Safety
|
|
/// - The offset cannot make the existing ptr null, or take it out of bounds for its allocation.
|
|
/// - If the `A` type parameter is [`Aligned`] then the offset must not make the resulting pointer
|
|
/// be unaligned for the pointee type.
|
|
///
|
|
/// [ptr_add]: https://doc.rust-lang.org/std/primitive.pointer.html#method.add
|
|
#[inline]
|
|
pub unsafe fn byte_add(self, count: usize) -> Self {
|
|
Self(
|
|
NonNull::new_unchecked(self.as_ptr().add(count)),
|
|
PhantomData,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<A: IsAligned> Pointer for $ptr<'_, A> {
|
|
#[inline]
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
Pointer::fmt(&self.0, f)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_ptr!(Ptr);
|
|
impl_ptr!(PtrMut);
|
|
impl_ptr!(OwningPtr);
|
|
|
|
impl<'a, A: IsAligned> Ptr<'a, A> {
|
|
/// Creates a new instance from a raw pointer.
|
|
///
|
|
/// # Safety
|
|
/// - `inner` must point to valid value of whatever the pointee type is.
|
|
/// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the pointee type.
|
|
/// - `inner` must have correct provenance to allow reads of the pointee type.
|
|
/// - The lifetime `'a` must be constrained such that this [`Ptr`] will stay valid and nothing
|
|
/// can mutate the pointee while this [`Ptr`] is live except through an `UnsafeCell`.
|
|
#[inline]
|
|
pub unsafe fn new(inner: NonNull<u8>) -> Self {
|
|
Self(inner, PhantomData)
|
|
}
|
|
|
|
/// Transforms this [`Ptr`] into an [`PtrMut`]
|
|
///
|
|
/// # Safety
|
|
/// Another [`PtrMut`] for the same [`Ptr`] must not be created until the first is dropped.
|
|
#[inline]
|
|
pub unsafe fn assert_unique(self) -> PtrMut<'a, A> {
|
|
PtrMut(self.0, PhantomData)
|
|
}
|
|
|
|
/// Transforms this [`Ptr<T>`] into a `&T` with the same lifetime
|
|
///
|
|
/// # Safety
|
|
/// - `T` must be the erased pointee type for this [`Ptr`].
|
|
/// - If the type parameter `A` is `Unaligned` then this pointer must be sufficiently aligned
|
|
/// for the pointee type `T`.
|
|
#[inline]
|
|
pub unsafe fn deref<T>(self) -> &'a T {
|
|
&*self.as_ptr().cast::<T>().debug_ensure_aligned()
|
|
}
|
|
|
|
/// Gets the underlying pointer, erasing the associated lifetime.
|
|
///
|
|
/// If possible, it is strongly encouraged to use [`deref`](Self::deref) over this function,
|
|
/// as it retains the lifetime.
|
|
#[inline]
|
|
#[allow(clippy::wrong_self_convention)]
|
|
pub fn as_ptr(self) -> *mut u8 {
|
|
self.0.as_ptr()
|
|
}
|
|
}
|
|
|
|
impl<'a, T> From<&'a T> for Ptr<'a> {
|
|
#[inline]
|
|
fn from(val: &'a T) -> Self {
|
|
// SAFETY: The returned pointer has the same lifetime as the passed reference.
|
|
// Access is immutable.
|
|
unsafe { Self::new(NonNull::from(val).cast()) }
|
|
}
|
|
}
|
|
|
|
impl<'a, A: IsAligned> PtrMut<'a, A> {
|
|
/// Creates a new instance from a raw pointer.
|
|
///
|
|
/// # Safety
|
|
/// - `inner` must point to valid value of whatever the pointee type is.
|
|
/// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the pointee type.
|
|
/// - `inner` must have correct provenance to allow read and writes of the pointee type.
|
|
/// - The lifetime `'a` must be constrained such that this [`PtrMut`] will stay valid and nothing
|
|
/// else can read or mutate the pointee while this [`PtrMut`] is live.
|
|
#[inline]
|
|
pub unsafe fn new(inner: NonNull<u8>) -> Self {
|
|
Self(inner, PhantomData)
|
|
}
|
|
|
|
/// Transforms this [`PtrMut`] into an [`OwningPtr`]
|
|
///
|
|
/// # Safety
|
|
/// Must have right to drop or move out of [`PtrMut`].
|
|
#[inline]
|
|
pub unsafe fn promote(self) -> OwningPtr<'a, A> {
|
|
OwningPtr(self.0, PhantomData)
|
|
}
|
|
|
|
/// Transforms this [`PtrMut<T>`] into a `&mut T` with the same lifetime
|
|
///
|
|
/// # Safety
|
|
/// - `T` must be the erased pointee type for this [`PtrMut`].
|
|
/// - If the type parameter `A` is [`Unaligned`] then this pointer must be sufficiently aligned
|
|
/// for the pointee type `T`.
|
|
#[inline]
|
|
pub unsafe fn deref_mut<T>(self) -> &'a mut T {
|
|
&mut *self.as_ptr().cast::<T>().debug_ensure_aligned()
|
|
}
|
|
|
|
/// Gets the underlying pointer, erasing the associated lifetime.
|
|
///
|
|
/// If possible, it is strongly encouraged to use [`deref_mut`](Self::deref_mut) over
|
|
/// this function, as it retains the lifetime.
|
|
#[inline]
|
|
#[allow(clippy::wrong_self_convention)]
|
|
pub fn as_ptr(&self) -> *mut u8 {
|
|
self.0.as_ptr()
|
|
}
|
|
|
|
/// Gets a [`PtrMut`] from this with a smaller lifetime.
|
|
#[inline]
|
|
pub fn reborrow(&mut self) -> PtrMut<'_, A> {
|
|
// SAFE: the ptrmut we're borrowing from is assumed to be valid
|
|
unsafe { PtrMut::new(self.0) }
|
|
}
|
|
|
|
/// Gets an immutable reference from this mutable reference
|
|
#[inline]
|
|
pub fn as_ref(&self) -> Ptr<'_, A> {
|
|
// SAFE: The `PtrMut` type's guarantees about the validity of this pointer are a superset of `Ptr` s guarantees
|
|
unsafe { Ptr::new(self.0) }
|
|
}
|
|
}
|
|
|
|
impl<'a, T> From<&'a mut T> for PtrMut<'a> {
|
|
#[inline]
|
|
fn from(val: &'a mut T) -> Self {
|
|
// SAFETY: The returned pointer has the same lifetime as the passed reference.
|
|
// The reference is mutable, and thus will not alias.
|
|
unsafe { Self::new(NonNull::from(val).cast()) }
|
|
}
|
|
}
|
|
|
|
impl<'a> OwningPtr<'a> {
|
|
/// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen.
|
|
#[inline]
|
|
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
|
|
let mut temp = ManuallyDrop::new(val);
|
|
// SAFETY: The value behind the pointer will not get dropped or observed later,
|
|
// so it's safe to promote it to an owning pointer.
|
|
f(unsafe { PtrMut::from(&mut *temp).promote() })
|
|
}
|
|
}
|
|
impl<'a, A: IsAligned> OwningPtr<'a, A> {
|
|
/// Creates a new instance from a raw pointer.
|
|
///
|
|
/// # Safety
|
|
/// - `inner` must point to valid value of whatever the pointee type is.
|
|
/// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the pointee type.
|
|
/// - `inner` must have correct provenance to allow read and writes of the pointee type.
|
|
/// - The lifetime `'a` must be constrained such that this [`OwningPtr`] will stay valid and nothing
|
|
/// else can read or mutate the pointee while this [`OwningPtr`] is live.
|
|
#[inline]
|
|
pub unsafe fn new(inner: NonNull<u8>) -> Self {
|
|
Self(inner, PhantomData)
|
|
}
|
|
|
|
/// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`.
|
|
///
|
|
/// # Safety
|
|
/// - `T` must be the erased pointee type for this [`OwningPtr`].
|
|
/// - If the type parameter `A` is `Unaligned` then this pointer must be sufficiently aligned
|
|
/// for the pointee type `T`.
|
|
#[inline]
|
|
pub unsafe fn read<T>(self) -> T {
|
|
self.as_ptr().cast::<T>().debug_ensure_aligned().read()
|
|
}
|
|
|
|
/// Consumes the [`OwningPtr`] to drop the underlying data of type `T`.
|
|
///
|
|
/// # Safety
|
|
/// - `T` must be the erased pointee type for this [`OwningPtr`].
|
|
/// - If the type parameter `A` is `Unaligned` then this pointer must be sufficiently aligned
|
|
/// for the pointee type `T`.
|
|
#[inline]
|
|
pub unsafe fn drop_as<T>(self) {
|
|
self.as_ptr()
|
|
.cast::<T>()
|
|
.debug_ensure_aligned()
|
|
.drop_in_place();
|
|
}
|
|
|
|
/// Gets the underlying pointer, erasing the associated lifetime.
|
|
///
|
|
/// If possible, it is strongly encouraged to use the other more type-safe functions
|
|
/// over this function.
|
|
#[inline]
|
|
#[allow(clippy::wrong_self_convention)]
|
|
pub fn as_ptr(&self) -> *mut u8 {
|
|
self.0.as_ptr()
|
|
}
|
|
|
|
/// Gets an immutable pointer from this owned pointer.
|
|
#[inline]
|
|
pub fn as_ref(&self) -> Ptr<'_, A> {
|
|
// SAFE: The `Owning` type's guarantees about the validity of this pointer are a superset of `Ptr` s guarantees
|
|
unsafe { Ptr::new(self.0) }
|
|
}
|
|
|
|
/// Gets a mutable pointer from this owned pointer.
|
|
#[inline]
|
|
pub fn as_mut(&mut self) -> PtrMut<'_, A> {
|
|
// SAFE: The `Owning` type's guarantees about the validity of this pointer are a superset of `Ptr` s guarantees
|
|
unsafe { PtrMut::new(self.0) }
|
|
}
|
|
}
|
|
impl<'a> OwningPtr<'a, Unaligned> {
|
|
/// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`.
|
|
///
|
|
/// # Safety
|
|
/// - `T` must be the erased pointee type for this [`OwningPtr`].
|
|
pub unsafe fn read_unaligned<T>(self) -> T {
|
|
self.as_ptr().cast::<T>().read_unaligned()
|
|
}
|
|
}
|
|
|
|
/// Conceptually equivalent to `&'a [T]` but with length information cut out for performance reasons
|
|
pub struct ThinSlicePtr<'a, T> {
|
|
ptr: NonNull<T>,
|
|
#[cfg(debug_assertions)]
|
|
len: usize,
|
|
_marker: PhantomData<&'a [T]>,
|
|
}
|
|
|
|
impl<'a, T> ThinSlicePtr<'a, T> {
|
|
#[inline]
|
|
/// Indexes the slice without doing bounds checks
|
|
///
|
|
/// # Safety
|
|
/// `index` must be in-bounds.
|
|
pub unsafe fn get(self, index: usize) -> &'a T {
|
|
#[cfg(debug_assertions)]
|
|
debug_assert!(index < self.len);
|
|
|
|
&*self.ptr.as_ptr().add(index)
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Clone for ThinSlicePtr<'a, T> {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Copy for ThinSlicePtr<'a, T> {}
|
|
|
|
impl<'a, T> From<&'a [T]> for ThinSlicePtr<'a, T> {
|
|
#[inline]
|
|
fn from(slice: &'a [T]) -> Self {
|
|
let ptr = slice.as_ptr() as *mut T;
|
|
Self {
|
|
// SAFETY: a reference can never be null
|
|
ptr: unsafe { NonNull::new_unchecked(ptr.debug_ensure_aligned()) },
|
|
#[cfg(debug_assertions)]
|
|
len: slice.len(),
|
|
_marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Creates a dangling pointer with specified alignment.
|
|
/// See [`NonNull::dangling`].
|
|
pub fn dangling_with_align(align: NonZeroUsize) -> NonNull<u8> {
|
|
debug_assert!(align.is_power_of_two(), "Alignment must be power of two.");
|
|
// SAFETY: The pointer will not be null, since it was created
|
|
// from the address of a `NonZeroUsize`.
|
|
unsafe { NonNull::new_unchecked(align.get() as *mut u8) }
|
|
}
|
|
|
|
mod private {
|
|
use core::cell::UnsafeCell;
|
|
|
|
pub trait SealedUnsafeCell {}
|
|
impl<'a, T> SealedUnsafeCell for &'a UnsafeCell<T> {}
|
|
}
|
|
|
|
/// Extension trait for helper methods on [`UnsafeCell`]
|
|
pub trait UnsafeCellDeref<'a, T>: private::SealedUnsafeCell {
|
|
/// # Safety
|
|
/// - The returned value must be unique and not alias any mutable or immutable references to the contents of the [`UnsafeCell`].
|
|
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
|
|
unsafe fn deref_mut(self) -> &'a mut T;
|
|
|
|
/// # Safety
|
|
/// - For the lifetime `'a` of the returned value you must not construct a mutable reference to the contents of the [`UnsafeCell`].
|
|
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
|
|
unsafe fn deref(self) -> &'a T;
|
|
|
|
/// Returns a copy of the contained value.
|
|
///
|
|
/// # Safety
|
|
/// - The [`UnsafeCell`] must not currently have a mutable reference to its content.
|
|
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
|
|
unsafe fn read(self) -> T
|
|
where
|
|
T: Copy;
|
|
}
|
|
|
|
impl<'a, T> UnsafeCellDeref<'a, T> for &'a UnsafeCell<T> {
|
|
#[inline]
|
|
unsafe fn deref_mut(self) -> &'a mut T {
|
|
&mut *self.get()
|
|
}
|
|
#[inline]
|
|
unsafe fn deref(self) -> &'a T {
|
|
&*self.get()
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn read(self) -> T
|
|
where
|
|
T: Copy,
|
|
{
|
|
self.get().read()
|
|
}
|
|
}
|
|
|
|
trait DebugEnsureAligned {
|
|
fn debug_ensure_aligned(self) -> Self;
|
|
}
|
|
|
|
// Disable this for miri runs as it already checks if pointer to reference
|
|
// casts are properly aligned.
|
|
#[cfg(all(debug_assertions, not(miri)))]
|
|
impl<T: Sized> DebugEnsureAligned for *mut T {
|
|
#[track_caller]
|
|
fn debug_ensure_aligned(self) -> Self {
|
|
let align = core::mem::align_of::<T>();
|
|
// Implementation shamelessly borrowed from the currently unstable
|
|
// ptr.is_aligned_to.
|
|
//
|
|
// Replace once https://github.com/rust-lang/rust/issues/96284 is stable.
|
|
assert!(
|
|
self as usize & (align - 1) == 0,
|
|
"pointer is not aligned. Address {:p} does not have alignment {} for type {}",
|
|
self,
|
|
align,
|
|
core::any::type_name::<T>(),
|
|
);
|
|
self
|
|
}
|
|
}
|
|
|
|
#[cfg(any(not(debug_assertions), miri))]
|
|
impl<T: Sized> DebugEnsureAligned for *mut T {
|
|
#[inline(always)]
|
|
fn debug_ensure_aligned(self) -> Self {
|
|
self
|
|
}
|
|
}
|