about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/ptr/non_null.rs48
-rw-r--r--library/core/tests/lib.rs1
-rw-r--r--library/core/tests/ptr.rs78
3 files changed, 127 insertions, 0 deletions
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index c1b19895f00..7516d4bba4c 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -4,6 +4,7 @@ use crate::fmt;
 use crate::hash;
 use crate::marker::Unsize;
 use crate::mem::{self, MaybeUninit};
+use crate::num::NonZeroUsize;
 use crate::ops::{CoerceUnsized, DispatchFromDyn};
 use crate::ptr::Unique;
 use crate::slice::{self, SliceIndex};
@@ -253,6 +254,53 @@ impl<T: ?Sized> NonNull<T> {
         (self.cast(), super::metadata(self.as_ptr()))
     }
 
+    /// Gets the "address" portion of the pointer.
+    ///
+    /// This API and its claimed semantics are part of the Strict Provenance experiment,
+    /// see the [module documentation][crate::ptr] for details.
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "strict_provenance", issue = "95228")]
+    pub fn addr(self) -> NonZeroUsize
+    where
+        T: Sized,
+    {
+        // SAFETY: The pointer is guaranteed by the type to be non-null,
+        // meaning that the address will be non-zero.
+        unsafe { NonZeroUsize::new_unchecked(self.pointer.addr()) }
+    }
+
+    /// Creates a new pointer with the given address.
+    ///
+    /// This API and its claimed semantics are part of the Strict Provenance experiment,
+    /// see the [module documentation][crate::ptr] for details.
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "strict_provenance", issue = "95228")]
+    pub fn with_addr(self, addr: NonZeroUsize) -> Self
+    where
+        T: Sized,
+    {
+        // SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
+        unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
+    }
+
+    /// Creates a new pointer by mapping `self`'s address to a new one.
+    ///
+    /// This is a convenience for [`with_addr`][Self::with_addr], see that method for details.
+    ///
+    /// This API and its claimed semantics are part of the Strict Provenance experiment,
+    /// see the [module documentation][crate::ptr] for details.
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "strict_provenance", issue = "95228")]
+    pub fn map_addr(self, f: impl FnOnce(NonZeroUsize) -> NonZeroUsize) -> Self
+    where
+        T: Sized,
+    {
+        self.with_addr(f(self.addr()))
+    }
+
     /// Acquires the underlying `*mut` pointer.
     ///
     /// # Examples
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 0d88a88376a..3af277a556b 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -86,6 +86,7 @@
 #![feature(int_roundings)]
 #![feature(slice_group_by)]
 #![feature(split_array)]
+#![feature(strict_provenance)]
 #![feature(trusted_random_access)]
 #![feature(unsize)]
 #![feature(unzip_option)]
diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs
index 6a39ab79f49..03fe56022b0 100644
--- a/library/core/tests/ptr.rs
+++ b/library/core/tests/ptr.rs
@@ -1,4 +1,5 @@
 use core::cell::RefCell;
+use core::num::NonZeroUsize;
 use core::ptr;
 use core::ptr::*;
 use std::fmt::{Debug, Display};
@@ -691,3 +692,80 @@ fn thin_box() {
         }
     }
 }
+
+#[test]
+fn nonnull_tagged_pointer_with_provenance() {
+    let raw_pointer = Box::into_raw(Box::new(10));
+
+    let mut p = TaggedPointer::new(raw_pointer).unwrap();
+    assert_eq!(p.tag(), 0);
+
+    p.set_tag(1);
+    assert_eq!(p.tag(), 1);
+    assert_eq!(unsafe { *p.pointer().as_ptr() }, 10);
+
+    p.set_tag(3);
+    assert_eq!(p.tag(), 3);
+    assert_eq!(unsafe { *p.pointer().as_ptr() }, 10);
+
+    unsafe { Box::from_raw(p.pointer().as_ptr()) };
+
+    /// A non-null pointer type which carries several bits of metadata and maintains provenance.
+    #[repr(transparent)]
+    pub struct TaggedPointer<T>(NonNull<T>);
+
+    impl<T> Clone for TaggedPointer<T> {
+        fn clone(&self) -> Self {
+            Self(self.0)
+        }
+    }
+
+    impl<T> Copy for TaggedPointer<T> {}
+
+    impl<T> TaggedPointer<T> {
+        /// The ABI-required minimum alignment of the `P` type.
+        pub const ALIGNMENT: usize = core::mem::align_of::<T>();
+        /// A mask for data-carrying bits of the address.
+        pub const DATA_MASK: usize = !Self::ADDRESS_MASK;
+        /// Number of available bits of storage in the address.
+        pub const NUM_BITS: u32 = Self::ALIGNMENT.trailing_zeros();
+        /// A mask for the non-data-carrying bits of the address.
+        pub const ADDRESS_MASK: usize = usize::MAX << Self::NUM_BITS;
+
+        /// Create a new tagged pointer from a possibly null pointer.
+        pub fn new(pointer: *mut T) -> Option<TaggedPointer<T>> {
+            Some(TaggedPointer(NonNull::new(pointer)?))
+        }
+
+        /// Consume this tagged pointer and produce a raw mutable pointer to the
+        /// memory location.
+        pub fn pointer(self) -> NonNull<T> {
+            // SAFETY: The `addr` guaranteed to have bits set in the Self::ADDRESS_MASK, so the result will be non-null.
+            self.0.map_addr(|addr| unsafe {
+                NonZeroUsize::new_unchecked(addr.get() & Self::ADDRESS_MASK)
+            })
+        }
+
+        /// Consume this tagged pointer and produce the data it carries.
+        pub fn tag(&self) -> usize {
+            self.0.addr().get() & Self::DATA_MASK
+        }
+
+        /// Update the data this tagged pointer carries to a new value.
+        pub fn set_tag(&mut self, data: usize) {
+            assert_eq!(
+                data & Self::ADDRESS_MASK,
+                0,
+                "cannot set more data beyond the lowest NUM_BITS"
+            );
+            let data = data & Self::DATA_MASK;
+
+            // SAFETY: This value will always be non-zero because the upper bits (from
+            // ADDRESS_MASK) will always be non-zero. This a property of the type and its
+            // construction.
+            self.0 = self.0.map_addr(|addr| unsafe {
+                NonZeroUsize::new_unchecked((addr.get() & Self::ADDRESS_MASK) | data)
+            })
+        }
+    }
+}