Skip to content

Commit 103879f

Browse files
committed
Auto merge of rust-lang#81172 - SimonSapin:ptr-metadata, r=oli-obk
Implement RFC 2580: Pointer metadata & VTable RFC: rust-lang/rfcs#2580 ~~Before merging this PR:~~ * [x] Wait for the end of the RFC’s [FCP to merge](rust-lang/rfcs#2580 (comment)). * [x] Open a tracking issue: rust-lang#81513 * [x] Update `#[unstable]` attributes in the PR with the tracking issue number ---- This PR extends the language with a new lang item for the `Pointee` trait which is special-cased in trait resolution to implement it for all types. Even in generic contexts, parameters can be assumed to implement it without a corresponding bound. For this I mostly imitated what the compiler was already doing for the `DiscriminantKind` trait. I’m very unfamiliar with compiler internals, so careful review is appreciated. This PR also extends the standard library with new unstable APIs in `core::ptr` and `std::ptr`: ```rust pub trait Pointee { /// One of `()`, `usize`, or `DynMetadata<dyn SomeTrait>` type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } pub trait Thin = Pointee<Metadata = ()>; pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {} pub const fn from_raw_parts<T: ?Sized>(*const (), <T as Pointee>::Metadata) -> *const T {} pub const fn from_raw_parts_mut<T: ?Sized>(*mut (),<T as Pointee>::Metadata) -> *mut T {} impl<T: ?Sized> NonNull<T> { pub const fn from_raw_parts(NonNull<()>, <T as Pointee>::Metadata) -> NonNull<T> {} /// Convenience for `(ptr.cast(), metadata(ptr))` pub const fn to_raw_parts(self) -> (NonNull<()>, <T as Pointee>::Metadata) {} } impl<T: ?Sized> *const T { pub const fn to_raw_parts(self) -> (*const (), <T as Pointee>::Metadata) {} } impl<T: ?Sized> *mut T { pub const fn to_raw_parts(self) -> (*mut (), <T as Pointee>::Metadata) {} } /// `<dyn SomeTrait as Pointee>::Metadata == DynMetadata<dyn SomeTrait>` pub struct DynMetadata<Dyn: ?Sized> { // Private pointer to vtable } impl<Dyn: ?Sized> DynMetadata<Dyn> { pub fn size_of(self) -> usize {} pub fn align_of(self) -> usize {} pub fn layout(self) -> crate::alloc::Layout {} } unsafe impl<Dyn: ?Sized> Send for DynMetadata<Dyn> {} unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Debug for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Unpin for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Copy for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Clone for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {} impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {} impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {} impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {} ``` API differences from the RFC, in areas noted as unresolved questions in the RFC: * Module-level functions instead of associated `from_raw_parts` functions on `*const T` and `*mut T`, following the precedent of `null`, `slice_from_raw_parts`, etc. * Added `to_raw_parts`
2 parents 9a64c89 + 1e855b4 commit 103879f

File tree

11 files changed

+686
-42
lines changed

11 files changed

+686
-42
lines changed

core/src/hash/mod.rs

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -673,39 +673,57 @@ mod impls {
673673
#[stable(feature = "rust1", since = "1.0.0")]
674674
impl<T: ?Sized> Hash for *const T {
675675
fn hash<H: Hasher>(&self, state: &mut H) {
676-
if mem::size_of::<Self>() == mem::size_of::<usize>() {
677-
// Thin pointer
678-
state.write_usize(*self as *const () as usize);
679-
} else {
680-
// Fat pointer
681-
// SAFETY: we are accessing the memory occupied by `self`
682-
// which is guaranteed to be valid.
683-
// This assumes a fat pointer can be represented by a `(usize, usize)`,
684-
// which is safe to do in `std` because it is shipped and kept in sync
685-
// with the implementation of fat pointers in `rustc`.
686-
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
687-
state.write_usize(a);
688-
state.write_usize(b);
676+
#[cfg(not(bootstrap))]
677+
{
678+
let (address, metadata) = self.to_raw_parts();
679+
state.write_usize(address as usize);
680+
metadata.hash(state);
681+
}
682+
#[cfg(bootstrap)]
683+
{
684+
if mem::size_of::<Self>() == mem::size_of::<usize>() {
685+
// Thin pointer
686+
state.write_usize(*self as *const () as usize);
687+
} else {
688+
// Fat pointer
689+
// SAFETY: we are accessing the memory occupied by `self`
690+
// which is guaranteed to be valid.
691+
// This assumes a fat pointer can be represented by a `(usize, usize)`,
692+
// which is safe to do in `std` because it is shipped and kept in sync
693+
// with the implementation of fat pointers in `rustc`.
694+
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
695+
state.write_usize(a);
696+
state.write_usize(b);
697+
}
689698
}
690699
}
691700
}
692701

693702
#[stable(feature = "rust1", since = "1.0.0")]
694703
impl<T: ?Sized> Hash for *mut T {
695704
fn hash<H: Hasher>(&self, state: &mut H) {
696-
if mem::size_of::<Self>() == mem::size_of::<usize>() {
697-
// Thin pointer
698-
state.write_usize(*self as *const () as usize);
699-
} else {
700-
// Fat pointer
701-
// SAFETY: we are accessing the memory occupied by `self`
702-
// which is guaranteed to be valid.
703-
// This assumes a fat pointer can be represented by a `(usize, usize)`,
704-
// which is safe to do in `std` because it is shipped and kept in sync
705-
// with the implementation of fat pointers in `rustc`.
706-
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
707-
state.write_usize(a);
708-
state.write_usize(b);
705+
#[cfg(not(bootstrap))]
706+
{
707+
let (address, metadata) = self.to_raw_parts();
708+
state.write_usize(address as usize);
709+
metadata.hash(state);
710+
}
711+
#[cfg(bootstrap)]
712+
{
713+
if mem::size_of::<Self>() == mem::size_of::<usize>() {
714+
// Thin pointer
715+
state.write_usize(*self as *const () as usize);
716+
} else {
717+
// Fat pointer
718+
// SAFETY: we are accessing the memory occupied by `self`
719+
// which is guaranteed to be valid.
720+
// This assumes a fat pointer can be represented by a `(usize, usize)`,
721+
// which is safe to do in `std` because it is shipped and kept in sync
722+
// with the implementation of fat pointers in `rustc`.
723+
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
724+
state.write_usize(a);
725+
state.write_usize(b);
726+
}
709727
}
710728
}
711729
}

core/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
#![feature(extended_key_value_attributes)]
112112
#![feature(extern_types)]
113113
#![feature(fundamental)]
114+
#![cfg_attr(not(bootstrap), feature(intra_doc_pointers))]
114115
#![feature(intrinsics)]
115116
#![feature(lang_items)]
116117
#![feature(link_llvm_intrinsics)]
@@ -123,6 +124,7 @@
123124
#![feature(auto_traits)]
124125
#![feature(or_patterns)]
125126
#![feature(prelude_import)]
127+
#![cfg_attr(not(bootstrap), feature(ptr_metadata))]
126128
#![feature(repr_simd, platform_intrinsics)]
127129
#![feature(rustc_attrs)]
128130
#![feature(simd_ffi)]
@@ -133,6 +135,7 @@
133135
#![feature(stmt_expr_attributes)]
134136
#![feature(str_split_as_str)]
135137
#![feature(str_split_inclusive_as_str)]
138+
#![feature(trait_alias)]
136139
#![feature(transparent_unions)]
137140
#![feature(try_blocks)]
138141
#![feature(unboxed_closures)]

core/src/ptr/const_ptr.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ impl<T: ?Sized> *const T {
4848
self as _
4949
}
5050

51+
/// Decompose a (possibly wide) pointer into is address and metadata components.
52+
///
53+
/// The pointer can be later reconstructed with [`from_raw_parts`].
54+
#[cfg(not(bootstrap))]
55+
#[unstable(feature = "ptr_metadata", issue = "81513")]
56+
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
57+
#[inline]
58+
pub const fn to_raw_parts(self) -> (*const (), <T as super::Pointee>::Metadata) {
59+
(self.cast(), metadata(self))
60+
}
61+
5162
/// Returns `None` if the pointer is null, or else returns a shared reference to
5263
/// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`]
5364
/// must be used instead.
@@ -905,9 +916,14 @@ impl<T> *const [T] {
905916
#[unstable(feature = "slice_ptr_len", issue = "71146")]
906917
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
907918
pub const fn len(self) -> usize {
908-
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
909-
// Only `std` can make this guarantee.
910-
unsafe { Repr { rust: self }.raw }.len
919+
#[cfg(bootstrap)]
920+
{
921+
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
922+
// Only `std` can make this guarantee.
923+
unsafe { Repr { rust: self }.raw }.len
924+
}
925+
#[cfg(not(bootstrap))]
926+
metadata(self)
911927
}
912928

913929
/// Returns a raw pointer to the slice's buffer.

0 commit comments

Comments
 (0)