Lift Pointer's requirement for the pointer to be thin

fat pointers rule!
This commit is contained in:
Maybe Waffle 2023-04-12 11:00:35 +00:00
parent 26232f1ff5
commit 9051331dd7
5 changed files with 58 additions and 33 deletions

View File

@ -0,0 +1,31 @@
use std::mem;
/// Returns the ABI-required minimum alignment of a type in bytes.
///
/// This is equivalent to [`mem::align_of`], but also works for some unsized
/// types (e.g. slices or rustc's `List`s).
pub const fn align_of<T: ?Sized + Aligned>() -> usize {
T::ALIGN
}
/// A type with a statically known alignment.
///
/// # Safety
///
/// `Self::ALIGN` must be equal to the alignment of `Self`. For sized types it
/// is [`mem::align_of<Self>()`], for unsized types it depends on the type, for
/// example `[T]` has alignment of `T`.
///
/// [`mem::align_of<Self>()`]: mem::align_of
pub unsafe trait Aligned {
/// Alignment of `Self`.
const ALIGN: usize;
}
unsafe impl<T> Aligned for T {
const ALIGN: usize = mem::align_of::<Self>();
}
unsafe impl<T> Aligned for [T] {
const ALIGN: usize = mem::align_of::<T>();
}

View File

@ -83,6 +83,7 @@ pub mod transitive_relation;
pub mod vec_linked_list; pub mod vec_linked_list;
pub mod work_queue; pub mod work_queue;
pub use atomic_ref::AtomicRef; pub use atomic_ref::AtomicRef;
pub mod aligned;
pub mod frozen; pub mod frozen;
pub mod owned_slice; pub mod owned_slice;
pub mod sso; pub mod sso;

View File

@ -13,12 +13,14 @@
//! The tag must implement the `Tag` trait. We assert that the tag and `Pointer` //! The tag must implement the `Tag` trait. We assert that the tag and `Pointer`
//! are compatible at compile time. //! are compatible at compile time.
use std::mem::{self, ManuallyDrop}; use std::mem::ManuallyDrop;
use std::ops::Deref; use std::ops::Deref;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::aligned::Aligned;
mod copy; mod copy;
mod drop; mod drop;
@ -31,8 +33,7 @@ pub use drop::TaggedPtr;
/// # Safety /// # Safety
/// ///
/// The pointer returned from [`into_ptr`] must be a [valid], pointer to /// The pointer returned from [`into_ptr`] must be a [valid], pointer to
/// [`<Self as Deref>::Target`]. Note that pointers to [`Self::Target`] must be /// [`<Self as Deref>::Target`].
/// thin, even though [`Self::Target`] may not be `Sized`.
/// ///
/// Note that if `Self` implements [`DerefMut`] the pointer returned from /// Note that if `Self` implements [`DerefMut`] the pointer returned from
/// [`into_ptr`] must be valid for writes (and thus calling [`NonNull::as_mut`] /// [`into_ptr`] must be valid for writes (and thus calling [`NonNull::as_mut`]
@ -110,7 +111,7 @@ pub unsafe trait Tag: Copy {
unsafe fn from_usize(tag: usize) -> Self; unsafe fn from_usize(tag: usize) -> Self;
} }
unsafe impl<T> Pointer for Box<T> { unsafe impl<T: ?Sized + Aligned> Pointer for Box<T> {
const BITS: usize = bits_for::<Self::Target>(); const BITS: usize = bits_for::<Self::Target>();
#[inline] #[inline]
@ -130,7 +131,7 @@ unsafe impl<T> Pointer for Box<T> {
} }
} }
unsafe impl<T> Pointer for Rc<T> { unsafe impl<T: ?Sized + Aligned> Pointer for Rc<T> {
const BITS: usize = bits_for::<Self::Target>(); const BITS: usize = bits_for::<Self::Target>();
#[inline] #[inline]
@ -149,7 +150,7 @@ unsafe impl<T> Pointer for Rc<T> {
} }
} }
unsafe impl<T> Pointer for Arc<T> { unsafe impl<T: ?Sized + Aligned> Pointer for Arc<T> {
const BITS: usize = bits_for::<Self::Target>(); const BITS: usize = bits_for::<Self::Target>();
#[inline] #[inline]
@ -168,7 +169,7 @@ unsafe impl<T> Pointer for Arc<T> {
} }
} }
unsafe impl<'a, T: 'a> Pointer for &'a T { unsafe impl<'a, T: 'a + ?Sized + Aligned> Pointer for &'a T {
const BITS: usize = bits_for::<Self::Target>(); const BITS: usize = bits_for::<Self::Target>();
#[inline] #[inline]
@ -186,7 +187,7 @@ unsafe impl<'a, T: 'a> Pointer for &'a T {
} }
} }
unsafe impl<'a, T: 'a> Pointer for &'a mut T { unsafe impl<'a, T: 'a + ?Sized + Aligned> Pointer for &'a mut T {
const BITS: usize = bits_for::<Self::Target>(); const BITS: usize = bits_for::<Self::Target>();
#[inline] #[inline]
@ -206,8 +207,8 @@ unsafe impl<'a, T: 'a> Pointer for &'a mut T {
/// Returns the number of bits available for use for tags in a pointer to `T` /// Returns the number of bits available for use for tags in a pointer to `T`
/// (this is based on `T`'s alignment). /// (this is based on `T`'s alignment).
pub const fn bits_for<T>() -> usize { pub const fn bits_for<T: ?Sized + Aligned>() -> usize {
let bits = mem::align_of::<T>().trailing_zeros(); let bits = crate::aligned::align_of::<T>().trailing_zeros();
// This is a replacement for `.try_into().unwrap()` unavailable in `const` // This is a replacement for `.try_into().unwrap()` unavailable in `const`
// (it's fine to make an assert here, since this is only called in compile time) // (it's fine to make an assert here, since this is only called in compile time)

View File

@ -55,12 +55,7 @@ where
} }
const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS; const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS;
const ASSERTION: () = { const ASSERTION: () = { assert!(T::BITS <= P::BITS) };
assert!(T::BITS <= P::BITS);
// Used for the transmute_copy's below
// TODO(waffle): do we need this assert anymore?
assert!(std::mem::size_of::<&P::Target>() == std::mem::size_of::<usize>());
};
/// Pack pointer `ptr` that comes from [`P::into_ptr`] with a `tag`. /// Pack pointer `ptr` that comes from [`P::into_ptr`] with a `tag`.
/// ///

View File

@ -1,5 +1,5 @@
use crate::arena::Arena; use crate::arena::Arena;
use rustc_data_structures::tagged_ptr::bits_for; use rustc_data_structures::aligned::Aligned;
use rustc_serialize::{Encodable, Encoder}; use rustc_serialize::{Encodable, Encoder};
use std::alloc::Layout; use std::alloc::Layout;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -8,7 +8,7 @@ use std::hash::{Hash, Hasher};
use std::iter; use std::iter;
use std::mem; use std::mem;
use std::ops::Deref; use std::ops::Deref;
use std::ptr::{self, NonNull}; use std::ptr;
use std::slice; use std::slice;
/// `List<T>` is a bit like `&[T]`, but with some critical differences. /// `List<T>` is a bit like `&[T]`, but with some critical differences.
@ -199,20 +199,17 @@ impl<'a, T: Copy> IntoIterator for &'a List<T> {
unsafe impl<T: Sync> Sync for List<T> {} unsafe impl<T: Sync> Sync for List<T> {}
unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List<T> { // Safety:
const BITS: usize = bits_for::<usize>(); // Layouts of `Equivalent<T>` and `List<T>` are the same, modulo opaque tail,
// thus aligns of `Equivalent<T>` and `List<T>` must be the same.
#[inline] unsafe impl<T> Aligned for List<T> {
fn into_ptr(self) -> NonNull<List<T>> { const ALIGN: usize = {
NonNull::from(self) #[repr(C)]
struct Equivalent<T> {
_len: usize,
_data: [T; 0],
} }
#[inline] mem::align_of::<Equivalent<T>>()
unsafe fn from_ptr(ptr: NonNull<List<T>>) -> &'a List<T> { };
ptr.as_ref()
}
unsafe fn with_ref<R, F: FnOnce(&Self) -> R>(ptr: NonNull<List<T>>, f: F) -> R {
f(&ptr.as_ref())
}
} }