From 87b9f244814d03f0308a4e558b0b16efb527522d Mon Sep 17 00:00:00 2001
From: Scott McMurray <scottmcm@users.noreply.github.com>
Date: Sun, 21 Apr 2024 16:11:01 -0700
Subject: [PATCH] Add an intrinsic for `ptr::metadata`

---
 core/src/intrinsics.rs   | 15 +++++++++++++++
 core/src/ptr/metadata.rs | 21 +++++++++++++++++----
 core/tests/ptr.rs        | 12 ++++++++++++
 3 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/core/src/intrinsics.rs b/core/src/intrinsics.rs
index 5a2a4c5ae6ebe..89e0b67099519 100644
--- a/core/src/intrinsics.rs
+++ b/core/src/intrinsics.rs
@@ -2821,6 +2821,21 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
     type Metadata = <P as ptr::Pointee>::Metadata;
 }
 
+/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`.
+///
+/// This is used to implement functions like `ptr::metadata`.
+#[rustc_nounwind]
+#[unstable(feature = "core_intrinsics", issue = "none")]
+#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
+#[rustc_intrinsic]
+#[rustc_intrinsic_must_be_overridden]
+#[cfg(not(bootstrap))]
+pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *const P) -> M {
+    // To implement a fallback we'd have to assume the layout of the pointer,
+    // but the whole point of this intrinsic is that we shouldn't do that.
+    unreachable!()
+}
+
 // Some functions are defined here because they accidentally got made
 // available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
 // (`transmute` also falls into this category, but it cannot be wrapped due to the
diff --git a/core/src/ptr/metadata.rs b/core/src/ptr/metadata.rs
index e501970b580de..84287ec3f5142 100644
--- a/core/src/ptr/metadata.rs
+++ b/core/src/ptr/metadata.rs
@@ -3,6 +3,8 @@
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
 use crate::intrinsics::aggregate_raw_ptr;
+#[cfg(not(bootstrap))]
+use crate::intrinsics::ptr_metadata;
 use crate::marker::Freeze;
 
 /// Provides the pointer metadata type of any pointed-to type.
@@ -94,10 +96,17 @@ pub trait Thin = Pointee<Metadata = ()>;
 #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
 #[inline]
 pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
-    // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
-    // and PtrComponents<T> have the same memory layouts. Only std can make this
-    // guarantee.
-    unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
+    #[cfg(bootstrap)]
+    {
+        // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
+        // and PtrComponents<T> have the same memory layouts. Only std can make this
+        // guarantee.
+        unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
+    }
+    #[cfg(not(bootstrap))]
+    {
+        ptr_metadata(ptr)
+    }
 }
 
 /// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
@@ -132,6 +141,7 @@ pub const fn from_raw_parts_mut<T: ?Sized>(
 }
 
 #[repr(C)]
+#[cfg(bootstrap)]
 union PtrRepr<T: ?Sized> {
     const_ptr: *const T,
     mut_ptr: *mut T,
@@ -139,15 +149,18 @@ union PtrRepr<T: ?Sized> {
 }
 
 #[repr(C)]
+#[cfg(bootstrap)]
 struct PtrComponents<T: ?Sized> {
     data_pointer: *const (),
     metadata: <T as Pointee>::Metadata,
 }
 
 // Manual impl needed to avoid `T: Copy` bound.
+#[cfg(bootstrap)]
 impl<T: ?Sized> Copy for PtrComponents<T> {}
 
 // Manual impl needed to avoid `T: Clone` bound.
+#[cfg(bootstrap)]
 impl<T: ?Sized> Clone for PtrComponents<T> {
     fn clone(&self) -> Self {
         *self
diff --git a/core/tests/ptr.rs b/core/tests/ptr.rs
index 7b55c2bf8a813..e8d05c2483de2 100644
--- a/core/tests/ptr.rs
+++ b/core/tests/ptr.rs
@@ -1171,3 +1171,15 @@ fn test_ptr_from_raw_parts_in_const() {
     assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
     assert_eq!(EMPTY_SLICE_PTR.len(), 456);
 }
+
+#[test]
+fn test_ptr_metadata_in_const() {
+    use std::fmt::Debug;
+
+    const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]);
+    const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]);
+    const DYN_META: DynMetadata<dyn Debug> = std::ptr::metadata::<dyn Debug>(&[0_u8; 42]);
+    assert_eq!(ARRAY_META, ());
+    assert_eq!(SLICE_META, 3);
+    assert_eq!(DYN_META.size_of(), 42);
+}