Skip to content

Commit 493527b

Browse files
committed
Add ptr::Pointee trait (for all types) and ptr::metadata function
RFC: rust-lang/rfcs#2580
1 parent 6f82698 commit 493527b

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
#![feature(stmt_expr_attributes)]
134134
#![feature(str_split_as_str)]
135135
#![feature(str_split_inclusive_as_str)]
136+
#![feature(trait_alias)]
136137
#![feature(transparent_unions)]
137138
#![feature(try_blocks)]
138139
#![feature(unboxed_closures)]

core/src/ptr/metadata.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#![unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")]
2+
3+
use crate::fmt;
4+
use crate::hash::Hash;
5+
use crate::ptr::NonNull;
6+
7+
/// FIXME docs
8+
#[lang = "pointee_trait"]
9+
pub trait Pointee {
10+
/// The type for metadata in pointers and references to `Self`.
11+
#[lang = "metadata_type"]
12+
// NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata`
13+
// in `library/core/src/ptr/metadata.rs`
14+
// in sync with those here:
15+
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
16+
}
17+
18+
/// Pointers to types implementing this trait alias are “thin”
19+
///
20+
/// ```rust
21+
/// #![feature(ptr_metadata)]
22+
///
23+
/// fn this_never_panics<T: std::ptr::Thin>() {
24+
/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::<usize>())
25+
/// }
26+
/// ```
27+
#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")]
28+
// NOTE: don’t stabilize this before trait aliases are stable in the language?
29+
pub trait Thin = Pointee<Metadata = ()>;
30+
31+
/// Extract the metadata component of a pointer.
32+
#[inline]
33+
pub fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
34+
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
35+
// and PtrComponents<T> have the same memory layouts. Only std can make this
36+
// guarantee.
37+
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
38+
}
39+
40+
#[repr(C)]
41+
union PtrRepr<T: ?Sized> {
42+
const_ptr: *const T,
43+
components: PtrComponents<T>,
44+
}
45+
46+
#[repr(C)]
47+
struct PtrComponents<T: ?Sized> {
48+
data_address: usize,
49+
metadata: <T as Pointee>::Metadata,
50+
}
51+
52+
// Manual impl needed to avoid `T: Copy` bound.
53+
impl<T: ?Sized> Copy for PtrComponents<T> {}
54+
55+
// Manual impl needed to avoid `T: Clone` bound.
56+
impl<T: ?Sized> Clone for PtrComponents<T> {
57+
fn clone(&self) -> Self {
58+
*self
59+
}
60+
}
61+
62+
/// The metadata for a `dyn SomeTrait` trait object type.
63+
#[lang = "dyn_metadata"]
64+
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
65+
pub struct DynMetadata {
66+
#[allow(unused)]
67+
vtable_ptr: NonNull<()>,
68+
}
69+
70+
unsafe impl Send for DynMetadata {}
71+
unsafe impl Sync for DynMetadata {}
72+
73+
impl fmt::Debug for DynMetadata {
74+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75+
f.write_str("DynMetadata { … }")
76+
}
77+
}

core/src/ptr/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ pub use crate::intrinsics::copy;
8282
#[doc(inline)]
8383
pub use crate::intrinsics::write_bytes;
8484

85+
#[cfg(not(bootstrap))]
86+
mod metadata;
87+
#[cfg(not(bootstrap))]
88+
#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")]
89+
pub use metadata::{metadata, DynMetadata, Pointee, Thin};
90+
8591
mod non_null;
8692
#[stable(feature = "nonnull", since = "1.25.0")]
8793
pub use non_null::NonNull;

core/tests/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#![feature(duration_saturating_ops)]
2828
#![feature(duration_zero)]
2929
#![feature(exact_size_is_empty)]
30+
#![feature(extern_types)]
3031
#![feature(fixed_size_array)]
3132
#![feature(flt2dec)]
3233
#![feature(fmt_internals)]
@@ -67,8 +68,10 @@
6768
#![feature(option_result_unwrap_unchecked)]
6869
#![feature(option_unwrap_none)]
6970
#![feature(peekable_peek_mut)]
71+
#![feature(ptr_metadata)]
7072
#![feature(once_cell)]
7173
#![feature(unsafe_block_in_unsafe_fn)]
74+
#![feature(unsized_tuple_coercion)]
7275
#![feature(int_bits_const)]
7376
#![feature(nonzero_leading_trailing_zeros)]
7477
#![feature(const_option)]

core/tests/ptr.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::cell::RefCell;
22
use core::ptr::*;
3+
use std::fmt::Display;
34

45
#[test]
56
fn test_const_from_raw_parts() {
@@ -413,3 +414,89 @@ fn offset_from() {
413414
assert_eq!(ptr2.offset(-2), ptr1);
414415
}
415416
}
417+
418+
#[test]
419+
#[cfg(not(bootstrap))]
420+
fn ptr_metadata() {
421+
struct Unit;
422+
struct Pair<A, B: ?Sized>(A, B);
423+
extern "C" {
424+
type Extern;
425+
}
426+
let () = metadata(&());
427+
let () = metadata(&Unit);
428+
let () = metadata(&4_u32);
429+
let () = metadata(&String::new());
430+
let () = metadata(&Some(4_u32));
431+
let () = metadata(&ptr_metadata);
432+
let () = metadata(&|| {});
433+
let () = metadata(&[4, 7]);
434+
let () = metadata(&(4, String::new()));
435+
let () = metadata(&Pair(4, String::new()));
436+
let () = metadata(0 as *const Extern);
437+
let () = metadata(0 as *const <&u32 as std::ops::Deref>::Target);
438+
439+
assert_eq!(metadata("foo"), 3_usize);
440+
assert_eq!(metadata(&[4, 7][..]), 2_usize);
441+
442+
let dst_tuple: &(bool, [u8]) = &(true, [0x66, 0x6F, 0x6F]);
443+
let dst_struct: &Pair<bool, [u8]> = &Pair(true, [0x66, 0x6F, 0x6F]);
444+
assert_eq!(metadata(dst_tuple), 3_usize);
445+
assert_eq!(metadata(dst_struct), 3_usize);
446+
unsafe {
447+
let dst_tuple: &(bool, str) = std::mem::transmute(dst_tuple);
448+
let dst_struct: &Pair<bool, str> = std::mem::transmute(dst_struct);
449+
assert_eq!(&dst_tuple.1, "foo");
450+
assert_eq!(&dst_struct.1, "foo");
451+
assert_eq!(metadata(dst_tuple), 3_usize);
452+
assert_eq!(metadata(dst_struct), 3_usize);
453+
}
454+
455+
let vtable_1: DynMetadata = metadata(&4_u32 as &dyn Display);
456+
let vtable_2: DynMetadata = metadata(&(true, 7_u32) as &(bool, dyn Display));
457+
let vtable_3: DynMetadata = metadata(&Pair(true, 7_u32) as &Pair<bool, dyn Display>);
458+
let vtable_4: DynMetadata = metadata(&4_u16 as &dyn Display);
459+
unsafe {
460+
let address_1: usize = std::mem::transmute(vtable_1);
461+
let address_2: usize = std::mem::transmute(vtable_2);
462+
let address_3: usize = std::mem::transmute(vtable_3);
463+
let address_4: usize = std::mem::transmute(vtable_4);
464+
// Same erased type and same trait: same vtable pointer
465+
assert_eq!(address_1, address_2);
466+
assert_eq!(address_1, address_3);
467+
// Different erased type: different vtable pointer
468+
assert_ne!(address_1, address_4);
469+
}
470+
}
471+
472+
#[test]
473+
#[cfg(not(bootstrap))]
474+
fn ptr_metadata_bounds() {
475+
fn metadata_eq_method_address<T: ?Sized>() -> usize {
476+
// The `Metadata` associated type has an `Ord` bound, so this is valid:
477+
<<T as Pointee>::Metadata as PartialEq>::eq as usize
478+
}
479+
// "Synthetic" trait impls generated by the compiler like those of `Pointee`
480+
// are not checked for bounds of associated type.
481+
// So with a buggy libcore we could have both:
482+
// * `<dyn Display as Pointee>::Metadata == DynMetadata`
483+
// * `DynMetadata: !PartialEq`
484+
// … and cause an ICE here:
485+
metadata_eq_method_address::<dyn Display>();
486+
487+
// For this reason, let’s check here that bounds are satisfied:
488+
489+
static_assert_expected_bounds_for_metadata::<()>();
490+
static_assert_expected_bounds_for_metadata::<usize>();
491+
static_assert_expected_bounds_for_metadata::<DynMetadata>();
492+
fn static_assert_associated_type<T: ?Sized>() {
493+
static_assert_expected_bounds_for_metadata::<<T as Pointee>::Metadata>()
494+
}
495+
496+
fn static_assert_expected_bounds_for_metadata<Meta>()
497+
where
498+
// Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs`
499+
Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin,
500+
{
501+
}
502+
}

0 commit comments

Comments
 (0)