1
#![unstable(feature = "ptr_metadata", issue = "81513")]
2

            
3
#[cfg(not(feature = "ferrocene_certified"))]
4
use crate::fmt;
5
#[cfg(not(feature = "ferrocene_certified"))]
6
use crate::hash::{Hash, Hasher};
7
#[cfg(not(feature = "ferrocene_certified"))]
8
use crate::intrinsics::{aggregate_raw_ptr, ptr_metadata};
9
#[cfg(not(feature = "ferrocene_certified"))]
10
use crate::marker::{Freeze, PointeeSized};
11
#[cfg(not(feature = "ferrocene_certified"))]
12
use crate::ptr::NonNull;
13
#[cfg(feature = "ferrocene_certified")]
14
use 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]
65
pub 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?
96
pub 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"))]
112
pub 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
/// If you are attempting to deconstruct a DST in a generic context to be reconstructed later,
123
/// a thin pointer can always be obtained by casting `*const T` to `*const ()`.
124
///
125
/// [`slice::from_raw_parts`]: crate::slice::from_raw_parts
126
#[unstable(feature = "ptr_metadata", issue = "81513")]
127
#[inline]
128
62766
pub const fn from_raw_parts<T: PointeeSized>(
129
62766
    data_pointer: *const impl Thin,
130
62766
    metadata: <T as Pointee>::Metadata,
131
62766
) -> *const T {
132
62766
    aggregate_raw_ptr(data_pointer, metadata)
133
62766
}
134

            
135
/// Performs the same functionality as [`from_raw_parts`], except that a
136
/// raw `*mut` pointer is returned, as opposed to a raw `*const` pointer.
137
///
138
/// See the documentation of [`from_raw_parts`] for more details.
139
#[unstable(feature = "ptr_metadata", issue = "81513")]
140
#[inline]
141
56737
pub const fn from_raw_parts_mut<T: PointeeSized>(
142
56737
    data_pointer: *mut impl Thin,
143
56737
    metadata: <T as Pointee>::Metadata,
144
56737
) -> *mut T {
145
56737
    aggregate_raw_ptr(data_pointer, metadata)
146
56737
}
147

            
148
/// The metadata for a `Dyn = dyn SomeTrait` trait object type.
149
///
150
/// It is a pointer to a vtable (virtual call table)
151
/// that represents all the necessary information
152
/// to manipulate the concrete type stored inside a trait object.
153
/// The vtable notably contains:
154
///
155
/// * type size
156
/// * type alignment
157
/// * a pointer to the type’s `drop_in_place` impl (may be a no-op for plain-old-data)
158
/// * pointers to all the methods for the type’s implementation of the trait
159
///
160
/// Note that the first three are special because they’re necessary to allocate, drop,
161
/// and deallocate any trait object.
162
///
163
/// It is possible to name this struct with a type parameter that is not a `dyn` trait object
164
/// (for example `DynMetadata<u64>`) but not to obtain a meaningful value of that struct.
165
///
166
/// Note that while this type implements `PartialEq`, comparing vtable pointers is unreliable:
167
/// pointers to vtables of the same type for the same trait can compare inequal (because vtables are
168
/// duplicated in multiple codegen units), and pointers to vtables of *different* types/traits can
169
/// compare equal (since identical vtables can be deduplicated within a codegen unit).
170
#[lang = "dyn_metadata"]
171
#[cfg(not(feature = "ferrocene_certified"))]
172
pub struct DynMetadata<Dyn: PointeeSized> {
173
    _vtable_ptr: NonNull<VTable>,
174
    _phantom: crate::marker::PhantomData<Dyn>,
175
}
176

            
177
#[cfg(not(feature = "ferrocene_certified"))]
178
unsafe extern "C" {
179
    /// Opaque type for accessing vtables.
180
    ///
181
    /// Private implementation detail of `DynMetadata::size_of` etc.
182
    /// There is conceptually not actually any Abstract Machine memory behind this pointer.
183
    type VTable;
184
}
185

            
186
#[cfg(not(feature = "ferrocene_certified"))]
187
impl<Dyn: PointeeSized> DynMetadata<Dyn> {
188
    /// When `DynMetadata` appears as the metadata field of a wide pointer, the rustc_middle layout
189
    /// computation does magic and the resulting layout is *not* a `FieldsShape::Aggregate`, instead
190
    /// it is a `FieldsShape::Primitive`. This means that the same type can have different layout
191
    /// depending on whether it appears as the metadata field of a wide pointer or as a stand-alone
192
    /// type, which understandably confuses codegen and leads to ICEs when trying to project to a
193
    /// field of `DynMetadata`. To work around that issue, we use `transmute` instead of using a
194
    /// field projection.
195
    #[inline]
196
    fn vtable_ptr(self) -> *const VTable {
197
        // SAFETY: this layout assumption is hard-coded into the compiler.
198
        // If it's somehow not a size match, the transmute will error.
199
        unsafe { crate::mem::transmute::<Self, *const VTable>(self) }
200
    }
201

            
202
    /// Returns the size of the type associated with this vtable.
203
    #[inline]
204
    pub fn size_of(self) -> usize {
205
        // Note that "size stored in vtable" is *not* the same as "result of size_of_val_raw".
206
        // Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the
207
        // `Send` part!
208
        // SAFETY: DynMetadata always contains a valid vtable pointer
209
        unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) }
210
    }
211

            
212
    /// Returns the alignment of the type associated with this vtable.
213
    #[inline]
214
    pub fn align_of(self) -> usize {
215
        // SAFETY: DynMetadata always contains a valid vtable pointer
216
        unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) }
217
    }
218

            
219
    /// Returns the size and alignment together as a `Layout`
220
    #[inline]
221
    pub fn layout(self) -> crate::alloc::Layout {
222
        // SAFETY: the compiler emitted this vtable for a concrete Rust type which
223
        // is known to have a valid layout. Same rationale as in `Layout::for_value`.
224
        unsafe { crate::alloc::Layout::from_size_align_unchecked(self.size_of(), self.align_of()) }
225
    }
226
}
227

            
228
#[cfg(not(feature = "ferrocene_certified"))]
229
unsafe impl<Dyn: PointeeSized> Send for DynMetadata<Dyn> {}
230
#[cfg(not(feature = "ferrocene_certified"))]
231
unsafe impl<Dyn: PointeeSized> Sync for DynMetadata<Dyn> {}
232

            
233
#[cfg(not(feature = "ferrocene_certified"))]
234
impl<Dyn: PointeeSized> fmt::Debug for DynMetadata<Dyn> {
235
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236
        f.debug_tuple("DynMetadata").field(&self.vtable_ptr()).finish()
237
    }
238
}
239

            
240
// Manual impls needed to avoid `Dyn: $Trait` bounds.
241

            
242
#[cfg(not(feature = "ferrocene_certified"))]
243
impl<Dyn: PointeeSized> Unpin for DynMetadata<Dyn> {}
244

            
245
#[cfg(not(feature = "ferrocene_certified"))]
246
impl<Dyn: PointeeSized> Copy for DynMetadata<Dyn> {}
247

            
248
#[cfg(not(feature = "ferrocene_certified"))]
249
impl<Dyn: PointeeSized> Clone for DynMetadata<Dyn> {
250
    #[inline]
251
    fn clone(&self) -> Self {
252
        *self
253
    }
254
}
255

            
256
#[cfg(not(feature = "ferrocene_certified"))]
257
impl<Dyn: PointeeSized> Eq for DynMetadata<Dyn> {}
258

            
259
#[cfg(not(feature = "ferrocene_certified"))]
260
impl<Dyn: PointeeSized> PartialEq for DynMetadata<Dyn> {
261
    #[inline]
262
    fn eq(&self, other: &Self) -> bool {
263
        crate::ptr::eq::<VTable>(self.vtable_ptr(), other.vtable_ptr())
264
    }
265
}
266

            
267
#[cfg(not(feature = "ferrocene_certified"))]
268
impl<Dyn: PointeeSized> Ord for DynMetadata<Dyn> {
269
    #[inline]
270
    #[allow(ambiguous_wide_pointer_comparisons)]
271
    fn cmp(&self, other: &Self) -> crate::cmp::Ordering {
272
        <*const VTable>::cmp(&self.vtable_ptr(), &other.vtable_ptr())
273
    }
274
}
275

            
276
#[cfg(not(feature = "ferrocene_certified"))]
277
impl<Dyn: PointeeSized> PartialOrd for DynMetadata<Dyn> {
278
    #[inline]
279
    fn partial_cmp(&self, other: &Self) -> Option<crate::cmp::Ordering> {
280
        Some(self.cmp(other))
281
    }
282
}
283

            
284
#[cfg(not(feature = "ferrocene_certified"))]
285
impl<Dyn: PointeeSized> Hash for DynMetadata<Dyn> {
286
    #[inline]
287
    fn hash<H: Hasher>(&self, hasher: &mut H) {
288
        crate::ptr::hash::<VTable, _>(self.vtable_ptr(), hasher)
289
    }
290
}