core/ptr/metadata.rs
1#![unstable(feature = "ptr_metadata", issue = "81513")]
2
3#[cfg(not(feature = "ferrocene_certified"))]
4use crate::fmt;
5#[cfg(not(feature = "ferrocene_certified"))]
6use crate::hash::{Hash, Hasher};
7#[cfg(not(feature = "ferrocene_certified"))]
8use crate::intrinsics::{aggregate_raw_ptr, ptr_metadata};
9#[cfg(not(feature = "ferrocene_certified"))]
10use crate::marker::{Freeze, PointeeSized};
11#[cfg(not(feature = "ferrocene_certified"))]
12use crate::ptr::NonNull;
13#[cfg(feature = "ferrocene_certified")]
14use crate::{intrinsics::aggregate_raw_ptr, marker::PointeeSized};
15
16/// Provides the pointer metadata type of any pointed-to type.
17///
18/// # Pointer metadata
19///
20/// Raw pointer types and reference types in Rust can be thought of as made of two parts:
21/// a data pointer that contains the memory address of the value, and some metadata.
22///
23/// For statically-sized types (that implement the `Sized` traits)
24/// as well as for `extern` types,
25/// pointers are said to be “thin”: metadata is zero-sized and its type is `()`.
26///
27/// Pointers to [dynamically-sized types][dst] are said to be “wide” or “fat”,
28/// they have non-zero-sized metadata:
29///
30/// * For structs whose last field is a DST, metadata is the metadata for the last field
31/// * For the `str` type, metadata is the length in bytes as `usize`
32/// * For slice types like `[T]`, metadata is the length in items as `usize`
33/// * For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata<Self>`][DynMetadata]
34/// (e.g. `DynMetadata<dyn SomeTrait>`)
35///
36/// In the future, the Rust language may gain new kinds of types
37/// that have different pointer metadata.
38///
39/// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts
40///
41///
42/// # The `Pointee` trait
43///
44/// The point of this trait is its `Metadata` associated type,
45/// which is `()` or `usize` or `DynMetadata<_>` as described above.
46/// It is automatically implemented for every type.
47/// It can be assumed to be implemented in a generic context, even without a corresponding bound.
48///
49///
50/// # Usage
51///
52/// Raw pointers can be decomposed into the data pointer and metadata components
53/// with their [`to_raw_parts`] method.
54///
55/// Alternatively, metadata alone can be extracted with the [`metadata`] function.
56/// A reference can be passed to [`metadata`] and implicitly coerced.
57///
58/// A (possibly-wide) pointer can be put back together from its data pointer and metadata
59/// with [`from_raw_parts`] or [`from_raw_parts_mut`].
60///
61/// [`to_raw_parts`]: *const::to_raw_parts
62#[lang = "pointee_trait"]
63#[rustc_deny_explicit_impl]
64#[rustc_do_not_implement_via_object]
65pub trait Pointee: PointeeSized {
66 /// The type for metadata in pointers and references to `Self`.
67 #[lang = "metadata_type"]
68 // NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata`
69 // in `library/core/src/ptr/metadata.rs`
70 // in sync with those here:
71 // NOTE: The metadata of `dyn Trait + 'a` is `DynMetadata<dyn Trait + 'a>`
72 // so a `'static` bound must not be added.
73 #[cfg(not(feature = "ferrocene_certified"))]
74 type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin + Freeze;
75 /// The type for metadata in pointers and references to `Self`.
76 #[lang = "metadata_type"]
77 #[cfg(feature = "ferrocene_certified")]
78 type Metadata: Copy + Send + Sync + Ord;
79}
80
81/// Pointers to types implementing this trait alias are “thin”.
82///
83/// This includes statically-`Sized` types and `extern` types.
84///
85/// # Example
86///
87/// ```rust
88/// #![feature(ptr_metadata)]
89///
90/// fn this_never_panics<T: std::ptr::Thin>() {
91/// assert_eq!(size_of::<&T>(), size_of::<usize>())
92/// }
93/// ```
94#[unstable(feature = "ptr_metadata", issue = "81513")]
95// NOTE: don’t stabilize this before trait aliases are stable in the language?
96pub trait Thin = Pointee<Metadata = ()> + PointeeSized;
97
98/// Extracts the metadata component of a pointer.
99///
100/// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function
101/// as they implicitly coerce to `*const T`.
102///
103/// # Example
104///
105/// ```
106/// #![feature(ptr_metadata)]
107///
108/// assert_eq!(std::ptr::metadata("foo"), 3_usize);
109/// ```
110#[inline]
111#[cfg(not(feature = "ferrocene_certified"))]
112pub const fn metadata<T: PointeeSized>(ptr: *const T) -> <T as Pointee>::Metadata {
113 ptr_metadata(ptr)
114}
115
116/// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
117///
118/// This function is safe but the returned pointer is not necessarily safe to dereference.
119/// For slices, see the documentation of [`slice::from_raw_parts`] for safety requirements.
120/// For trait objects, the metadata must come from a pointer to the same underlying erased type.
121///
122/// [`slice::from_raw_parts`]: crate::slice::from_raw_parts
123#[unstable(feature = "ptr_metadata", issue = "81513")]
124#[inline]
125pub const fn from_raw_parts<T: PointeeSized>(
126 data_pointer: *const impl Thin,
127 metadata: <T as Pointee>::Metadata,
128) -> *const T {
129 aggregate_raw_ptr(data_pointer, metadata)
130}
131
132/// Performs the same functionality as [`from_raw_parts`], except that a
133/// raw `*mut` pointer is returned, as opposed to a raw `*const` pointer.
134///
135/// See the documentation of [`from_raw_parts`] for more details.
136#[unstable(feature = "ptr_metadata", issue = "81513")]
137#[inline]
138pub const fn from_raw_parts_mut<T: PointeeSized>(
139 data_pointer: *mut impl Thin,
140 metadata: <T as Pointee>::Metadata,
141) -> *mut T {
142 aggregate_raw_ptr(data_pointer, metadata)
143}
144
145/// The metadata for a `Dyn = dyn SomeTrait` trait object type.
146///
147/// It is a pointer to a vtable (virtual call table)
148/// that represents all the necessary information
149/// to manipulate the concrete type stored inside a trait object.
150/// The vtable notably contains:
151///
152/// * type size
153/// * type alignment
154/// * a pointer to the type’s `drop_in_place` impl (may be a no-op for plain-old-data)
155/// * pointers to all the methods for the type’s implementation of the trait
156///
157/// Note that the first three are special because they’re necessary to allocate, drop,
158/// and deallocate any trait object.
159///
160/// It is possible to name this struct with a type parameter that is not a `dyn` trait object
161/// (for example `DynMetadata<u64>`) but not to obtain a meaningful value of that struct.
162///
163/// Note that while this type implements `PartialEq`, comparing vtable pointers is unreliable:
164/// pointers to vtables of the same type for the same trait can compare inequal (because vtables are
165/// duplicated in multiple codegen units), and pointers to vtables of *different* types/traits can
166/// compare equal (since identical vtables can be deduplicated within a codegen unit).
167#[lang = "dyn_metadata"]
168#[cfg(not(feature = "ferrocene_certified"))]
169pub struct DynMetadata<Dyn: PointeeSized> {
170 _vtable_ptr: NonNull<VTable>,
171 _phantom: crate::marker::PhantomData<Dyn>,
172}
173
174#[cfg(not(feature = "ferrocene_certified"))]
175unsafe extern "C" {
176 /// Opaque type for accessing vtables.
177 ///
178 /// Private implementation detail of `DynMetadata::size_of` etc.
179 /// There is conceptually not actually any Abstract Machine memory behind this pointer.
180 type VTable;
181}
182
183#[cfg(not(feature = "ferrocene_certified"))]
184impl<Dyn: PointeeSized> DynMetadata<Dyn> {
185 /// When `DynMetadata` appears as the metadata field of a wide pointer, the rustc_middle layout
186 /// computation does magic and the resulting layout is *not* a `FieldsShape::Aggregate`, instead
187 /// it is a `FieldsShape::Primitive`. This means that the same type can have different layout
188 /// depending on whether it appears as the metadata field of a wide pointer or as a stand-alone
189 /// type, which understandably confuses codegen and leads to ICEs when trying to project to a
190 /// field of `DynMetadata`. To work around that issue, we use `transmute` instead of using a
191 /// field projection.
192 #[inline]
193 fn vtable_ptr(self) -> *const VTable {
194 // SAFETY: this layout assumption is hard-coded into the compiler.
195 // If it's somehow not a size match, the transmute will error.
196 unsafe { crate::mem::transmute::<Self, *const VTable>(self) }
197 }
198
199 /// Returns the size of the type associated with this vtable.
200 #[inline]
201 pub fn size_of(self) -> usize {
202 // Note that "size stored in vtable" is *not* the same as "result of size_of_val_raw".
203 // Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the
204 // `Send` part!
205 // SAFETY: DynMetadata always contains a valid vtable pointer
206 unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) }
207 }
208
209 /// Returns the alignment of the type associated with this vtable.
210 #[inline]
211 pub fn align_of(self) -> usize {
212 // SAFETY: DynMetadata always contains a valid vtable pointer
213 unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) }
214 }
215
216 /// Returns the size and alignment together as a `Layout`
217 #[inline]
218 pub fn layout(self) -> crate::alloc::Layout {
219 // SAFETY: the compiler emitted this vtable for a concrete Rust type which
220 // is known to have a valid layout. Same rationale as in `Layout::for_value`.
221 unsafe { crate::alloc::Layout::from_size_align_unchecked(self.size_of(), self.align_of()) }
222 }
223}
224
225#[cfg(not(feature = "ferrocene_certified"))]
226unsafe impl<Dyn: PointeeSized> Send for DynMetadata<Dyn> {}
227#[cfg(not(feature = "ferrocene_certified"))]
228unsafe impl<Dyn: PointeeSized> Sync for DynMetadata<Dyn> {}
229
230#[cfg(not(feature = "ferrocene_certified"))]
231impl<Dyn: PointeeSized> fmt::Debug for DynMetadata<Dyn> {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 f.debug_tuple("DynMetadata").field(&self.vtable_ptr()).finish()
234 }
235}
236
237// Manual impls needed to avoid `Dyn: $Trait` bounds.
238
239#[cfg(not(feature = "ferrocene_certified"))]
240impl<Dyn: PointeeSized> Unpin for DynMetadata<Dyn> {}
241
242#[cfg(not(feature = "ferrocene_certified"))]
243impl<Dyn: PointeeSized> Copy for DynMetadata<Dyn> {}
244
245#[cfg(not(feature = "ferrocene_certified"))]
246impl<Dyn: PointeeSized> Clone for DynMetadata<Dyn> {
247 #[inline]
248 fn clone(&self) -> Self {
249 *self
250 }
251}
252
253#[cfg(not(feature = "ferrocene_certified"))]
254impl<Dyn: PointeeSized> Eq for DynMetadata<Dyn> {}
255
256#[cfg(not(feature = "ferrocene_certified"))]
257impl<Dyn: PointeeSized> PartialEq for DynMetadata<Dyn> {
258 #[inline]
259 fn eq(&self, other: &Self) -> bool {
260 crate::ptr::eq::<VTable>(self.vtable_ptr(), other.vtable_ptr())
261 }
262}
263
264#[cfg(not(feature = "ferrocene_certified"))]
265impl<Dyn: PointeeSized> Ord for DynMetadata<Dyn> {
266 #[inline]
267 #[allow(ambiguous_wide_pointer_comparisons)]
268 fn cmp(&self, other: &Self) -> crate::cmp::Ordering {
269 <*const VTable>::cmp(&self.vtable_ptr(), &other.vtable_ptr())
270 }
271}
272
273#[cfg(not(feature = "ferrocene_certified"))]
274impl<Dyn: PointeeSized> PartialOrd for DynMetadata<Dyn> {
275 #[inline]
276 fn partial_cmp(&self, other: &Self) -> Option<crate::cmp::Ordering> {
277 Some(self.cmp(other))
278 }
279}
280
281#[cfg(not(feature = "ferrocene_certified"))]
282impl<Dyn: PointeeSized> Hash for DynMetadata<Dyn> {
283 #[inline]
284 fn hash<H: Hasher>(&self, hasher: &mut H) {
285 crate::ptr::hash::<VTable, _>(self.vtable_ptr(), hasher)
286 }
287}