use super::{Pointer, Tag}; use crate::stable_hasher::{HashStable, StableHasher}; use std::fmt; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::num::NonZeroUsize; use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; /// A [`Copy`] tagged pointer. /// /// This is essentially `{ pointer: P, tag: T }` packed in a single pointer. /// /// You should use this instead of the [`TaggedPtr`] type in all cases where /// `P` implements [`Copy`]. /// /// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without /// unpacking. Otherwise we don't implement [`PartialEq`], [`Eq`] and [`Hash`]; /// if you want that, wrap the [`CopyTaggedPtr`]. /// /// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr pub struct CopyTaggedPtr
where
P: Pointer,
T: Tag,
{
/// This is semantically a pair of `pointer: P` and `tag: T` fields,
/// however we pack them in a single pointer, to save space.
///
/// We pack the tag into the **most**-significant bits of the pointer to
/// ease retrieval of the value. A left shift is a multiplication and
/// those are embeddable in instruction encoding, for example:
///
/// ```asm
/// // ( CopyTaggedPtr
where
P: Pointer,
T: Tag,
{
/// Tags `pointer` with `tag`.
///
/// Note that this leaks `pointer`: it won't be dropped when
/// `CopyTaggedPtr` is dropped. If you have a pointer with a significant
/// drop, use [`TaggedPtr`] instead.
///
/// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr
#[inline]
pub fn new(pointer: P, tag: T) -> Self {
Self { packed: Self::pack(P::into_ptr(pointer), tag), tag_ghost: PhantomData }
}
/// Retrieves the pointer.
#[inline]
pub fn pointer(self) -> P
where
P: Copy,
{
// SAFETY: pointer_raw returns the original pointer
//
// Note that this isn't going to double-drop or anything because we have
// P: Copy
unsafe { P::from_ptr(self.pointer_raw()) }
}
/// Retrieves the tag.
#[inline]
pub fn tag(&self) -> T {
// Unpack the tag, according to the `self.packed` encoding scheme
let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT;
// Safety:
// The shift retrieves the original value from `T::into_usize`,
// satisfying `T::from_usize`'s preconditions.
unsafe { T::from_usize(tag) }
}
/// Sets the tag to a new value.
#[inline]
pub fn set_tag(&mut self, tag: T) {
self.packed = Self::pack(self.pointer_raw(), tag);
}
const TAG_BIT_SHIFT: u32 = usize::BITS - T::BITS;
const ASSERTION: () = { assert!(T::BITS <= P::BITS) };
/// Pack pointer `ptr` that comes from [`P::into_ptr`] with a `tag`,
/// according to `self.packed` encoding scheme.
///
/// [`P::into_ptr`]: Pointer::into_ptr
#[inline]
fn pack(ptr: NonNull Copy for CopyTaggedPtr
where
P: Pointer + Copy,
T: Tag,
{
}
impl Clone for CopyTaggedPtr
where
P: Pointer + Copy,
T: Tag,
{
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl Deref for CopyTaggedPtr
where
P: Pointer,
T: Tag,
{
type Target = P::Target;
#[inline]
fn deref(&self) -> &Self::Target {
// Safety:
// `pointer_raw` returns the original pointer from `P::into_ptr` which,
// by the `Pointer`'s contract, must be valid.
unsafe { self.pointer_raw().as_ref() }
}
}
impl DerefMut for CopyTaggedPtr
where
P: Pointer + DerefMut,
T: Tag,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// Safety:
// `pointer_raw` returns the original pointer from `P::into_ptr` which,
// by the `Pointer`'s contract, must be valid for writes if
// `P: DerefMut`.
unsafe { self.pointer_raw().as_mut() }
}
}
impl fmt::Debug for CopyTaggedPtr
where
P: Pointer + fmt::Debug,
T: Tag + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.with_pointer_ref(|ptr| {
f.debug_struct("CopyTaggedPtr").field("pointer", ptr).field("tag", &self.tag()).finish()
})
}
}
impl PartialEq for CopyTaggedPtr
where
P: Pointer,
T: Tag,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.packed == other.packed
}
}
impl Eq for CopyTaggedPtr
where
P: Pointer,
T: Tag,
{
}
impl Hash for CopyTaggedPtr
where
P: Pointer,
T: Tag,
{
#[inline]
fn hash HashStable
where
P: Pointer + HashStable ` is semantically just `{ ptr: P, tag: T }`, as such
// it's ok to implement `Sync` as long as `P: Sync, T: Sync`
unsafe impl Sync for CopyTaggedPtr
where
P: Sync + Pointer,
T: Sync + Tag,
{
}
// Safety:
// `CopyTaggedPtr ` is semantically just `{ ptr: P, tag: T }`, as such
// it's ok to implement `Send` as long as `P: Send, T: Send`
unsafe impl Send for CopyTaggedPtr
where
P: Send + Pointer,
T: Send + Tag,
{
}
/// Test that `new` does not compile if there is not enough alignment for the
/// tag in the pointer.
///
/// ```compile_fail,E0080
/// use rustc_data_structures::tagged_ptr::{CopyTaggedPtr, Tag};
///
/// #[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, B11 = 0b11 };
///
/// unsafe impl Tag for Tag2 {
/// const BITS: u32 = 2;
///
/// fn into_usize(self) -> usize { todo!() }
/// unsafe fn from_usize(tag: usize) -> Self { todo!() }
/// }
///
/// let value = 12u16;
/// let reference = &value;
/// let tag = Tag2::B01;
///
/// let _ptr = CopyTaggedPtr::<_, _, true>::new(reference, tag);
/// ```
// For some reason miri does not get the compile error
// probably it `check`s instead of `build`ing?
#[cfg(not(miri))]
const _: () = ();
#[cfg(test)]
mod tests;