-use super::{Pointer, Tag};
-use crate::stable_hasher::{HashStable, StableHasher};
use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::ops::{Deref, DerefMut};
use super::CopyTaggedPtr;
+use super::{Pointer, Tag};
+use crate::stable_hasher::{HashStable, StableHasher};
-/// A TaggedPtr implementing `Drop`.
+/// A tagged pointer that supports pointers that implement [`Drop`].
+///
+/// This is essentially `{ pointer: P, tag: T }` packed in a single pointer.
+///
+/// You should use [`CopyTaggedPtr`] instead of the this 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/Hash; if you want that,
-/// wrap the TaggedPtr.
+/// unpacking. Otherwise we don't implement [`PartialEq`], [`Eq`] and [`Hash`];
+/// if you want that, wrap the [`TaggedPtr`].
pub struct TaggedPtr<P, T, const COMPARE_PACKED: bool>
where
P: Pointer,
raw: CopyTaggedPtr<P, T, COMPARE_PACKED>,
}
-impl<P, T, const COMPARE_PACKED: bool> Clone for TaggedPtr<P, T, COMPARE_PACKED>
-where
- P: Pointer + Clone,
- T: Tag,
-{
- fn clone(&self) -> Self {
- unsafe { Self::new(P::with_ref(self.raw.pointer_raw(), |p| p.clone()), self.raw.tag()) }
- }
-}
-
-// We pack the tag into the *upper* bits of the pointer to ease retrieval of the
-// value; a right shift is a multiplication and those are embeddable in
-// instruction encoding.
-impl<P, T, const COMPARE_PACKED: bool> TaggedPtr<P, T, COMPARE_PACKED>
+impl<P, T, const CP: bool> TaggedPtr<P, T, CP>
where
P: Pointer,
T: Tag,
{
+ /// Tags `pointer` with `tag`.
+ #[inline]
pub fn new(pointer: P, tag: T) -> Self {
TaggedPtr { raw: CopyTaggedPtr::new(pointer, tag) }
}
- pub fn pointer_ref(&self) -> &P::Target {
- self.raw.pointer_ref()
- }
+ /// Retrieves the tag.
+ #[inline]
pub fn tag(&self) -> T {
self.raw.tag()
}
+
+ /// Sets the tag to a new value.
+ #[inline]
+ pub fn set_tag(&mut self, tag: T) {
+ self.raw.set_tag(tag)
+ }
+}
+
+impl<P, T, const CP: bool> Clone for TaggedPtr<P, T, CP>
+where
+ P: Pointer + Clone,
+ T: Tag,
+{
+ fn clone(&self) -> Self {
+ let ptr = self.raw.with_pointer_ref(P::clone);
+
+ Self::new(ptr, self.tag())
+ }
}
-impl<P, T, const COMPARE_PACKED: bool> std::ops::Deref for TaggedPtr<P, T, COMPARE_PACKED>
+impl<P, T, const CP: bool> Deref for TaggedPtr<P, T, CP>
where
P: Pointer,
T: Tag,
{
type Target = P::Target;
+
+ #[inline]
fn deref(&self) -> &Self::Target {
- self.raw.pointer_ref()
+ self.raw.deref()
}
}
-impl<P, T, const COMPARE_PACKED: bool> std::ops::DerefMut for TaggedPtr<P, T, COMPARE_PACKED>
+impl<P, T, const CP: bool> DerefMut for TaggedPtr<P, T, CP>
where
- P: Pointer + std::ops::DerefMut,
+ P: Pointer + DerefMut,
T: Tag,
{
+ #[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
- self.raw.pointer_mut()
+ self.raw.deref_mut()
}
}
-impl<P, T, const COMPARE_PACKED: bool> Drop for TaggedPtr<P, T, COMPARE_PACKED>
+impl<P, T, const CP: bool> Drop for TaggedPtr<P, T, CP>
where
P: Pointer,
T: Tag,
fn drop(&mut self) {
// No need to drop the tag, as it's Copy
unsafe {
- drop(P::from_usize(self.raw.pointer_raw()));
+ drop(P::from_ptr(self.raw.pointer_raw()));
}
}
}
-impl<P, T, const COMPARE_PACKED: bool> fmt::Debug for TaggedPtr<P, T, COMPARE_PACKED>
+impl<P, T, const CP: bool> fmt::Debug for TaggedPtr<P, T, CP>
where
- P: Pointer,
- P::Target: fmt::Debug,
+ P: Pointer + fmt::Debug,
T: Tag + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("TaggedPtr")
- .field("pointer", &self.pointer_ref())
- .field("tag", &self.tag())
- .finish()
+ self.raw.with_pointer_ref(|ptr| {
+ f.debug_struct("TaggedPtr").field("pointer", ptr).field("tag", &self.tag()).finish()
+ })
}
}
P: Pointer,
T: Tag,
{
+ #[inline]
fn eq(&self, other: &Self) -> bool {
self.raw.eq(&other.raw)
}
{
}
-impl<P, T> std::hash::Hash for TaggedPtr<P, T, true>
+impl<P, T> Hash for TaggedPtr<P, T, true>
where
P: Pointer,
T: Tag,
{
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ #[inline]
+ fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.hash(state);
}
}
-impl<P, T, HCX, const COMPARE_PACKED: bool> HashStable<HCX> for TaggedPtr<P, T, COMPARE_PACKED>
+impl<P, T, HCX, const CP: bool> HashStable<HCX> for TaggedPtr<P, T, CP>
where
P: Pointer + HashStable<HCX>,
T: Tag + HashStable<HCX>,
self.raw.hash_stable(hcx, hasher);
}
}
+
+/// 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::{TaggedPtr, 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 = TaggedPtr::<_, _, 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;