Open
Description
This is a tracking issue for the RFC 2580 "Pointer metadata & VTable" (rust-lang/rfcs#2580).
The feature gate for the issue is #![feature(ptr_metadata)]
.
About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
- Implement the RFC (cc @rust-lang/libs @rust-lang/lang -- can anyone write up mentoring
instructions?) - Adjust documentation (see instructions on rustc-dev-guide)
- Stabilization PR (see instructions on rustc-dev-guide)
Unresolved Questions
Language-level:
- Is it, or should it be UB (through validity or safety invariants) to have a raw trait object wide pointer with an dangling vtable pointer? A null vtable pointer? If not,
DynMetadata
methods likesize
may need to beunsafe fn
. Or maybe something like*const ()
should be metadata of trait objects instead ofDynMetadata
.
Right now, there is some inconsistency here:size_of_val_raw(ptr)
is unsafe, butmetadta(ptr).size_of()
does the same thing and is safe.
Update (2024-10-04): It is definitely the case that the safety invariant for raw trait objects requires a valid vtable. Sometadta(ptr).size_of()
being safe is fine.size_of_val_raw(ptr)
must be unsafe because of slices, so there is no inconsistency here. - should
Metadata
be required to beFreeze
API level:
- Is
*const ()
appropriate for the data component of pointers? Or should it be*const u8
? Or*const Opaque
with some newOpaque
type? (Respectively*mut ()
andNonNull<()>
) - Should
ptr::from_raw_parts
and friends beunsafe fn
? - Should
Thin
be added as a supertrait ofSized
? Or could it ever make sense to have fat pointers to statically-sized types? - Should
DynMetadata
not have a type parameter? This might reduce monomorphization cost, but would force that the size, alignment, and destruction pointers be in the same location (offset) for every vtable. But keeping them in the same location is probaly desirable anyway to keep code size small. - ACP: replace use of
Pointee
trait with aptr::Metadata
type libs-team#246 DynMetadata::size_of
does not always return the same value assize_of_val
since the former only reads the size from the vtable, but the latter computes the size of the entire type. That seems like a pretty bad footgun?
API bikesheds:
- Name of new items:
Pointee
(v.s. Referent?),Thin
(ThinPointee
?),DynMetadata
(VTablePtr
?), etc - Location of new items in
core::ptr
. For example: shouldThin
be incore::marker
instead?
Implementation history
- Implement RFC 2580: Pointer metadata & VTable #81172 Initial implementation
- [ACP 362] genericize
ptr::from_raw_parts
#125701 - Generalize
NonNull::from_raw_parts
per ACP362 #132895
Tracked APIs
Last updated for #81172.
pub trait Pointee {
/// One of `()`, `usize`, or `DynMetadata<dyn SomeTrait>`
type Metadata;
}
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 impl Thin, <T as Pointee>::Metadata) -> *const T {}
pub const fn from_raw_parts_mut<T: ?Sized>(*mut impl Thin, <T as Pointee>::Metadata) -> *mut T {}
impl<T: ?Sized> NonNull<T> {
pub const fn from_raw_parts(NonNull<impl Thin>, <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> {}
Activity