Auto merge of #81172 - SimonSapin:ptr-metadata, r=oli-obk
Implement RFC 2580: Pointer metadata & VTable RFC: https://github.com/rust-lang/rfcs/pull/2580 ~~Before merging this PR:~~ * [x] Wait for the end of the RFC’s [FCP to merge](https://github.com/rust-lang/rfcs/pull/2580#issuecomment-759145278). * [x] Open a tracking issue: https://github.com/rust-lang/rust/issues/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`
This commit is contained in:
commit
d1462d8558
@ -201,6 +201,10 @@ language_item_table! {
|
||||
// The associated item of `trait DiscriminantKind`.
|
||||
Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy;
|
||||
|
||||
PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait;
|
||||
Metadata, sym::metadata_type, metadata_type, Target::AssocTy;
|
||||
DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct;
|
||||
|
||||
Freeze, sym::freeze, freeze_trait, Target::Trait;
|
||||
|
||||
Drop, sym::drop, drop_trait, Target::Trait;
|
||||
|
@ -479,6 +479,9 @@ pub enum ImplSource<'tcx, N> {
|
||||
/// ImplSource for a builtin `DeterminantKind` trait implementation.
|
||||
DiscriminantKind(ImplSourceDiscriminantKindData),
|
||||
|
||||
/// ImplSource for a builtin `Pointee` trait implementation.
|
||||
Pointee(ImplSourcePointeeData),
|
||||
|
||||
/// ImplSource automatically generated for a generator.
|
||||
Generator(ImplSourceGeneratorData<'tcx, N>),
|
||||
|
||||
@ -497,7 +500,8 @@ impl<'tcx, N> ImplSource<'tcx, N> {
|
||||
ImplSource::Generator(c) => c.nested,
|
||||
ImplSource::Object(d) => d.nested,
|
||||
ImplSource::FnPointer(d) => d.nested,
|
||||
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) => Vec::new(),
|
||||
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
|
||||
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
|
||||
ImplSource::TraitAlias(d) => d.nested,
|
||||
}
|
||||
}
|
||||
@ -512,7 +516,8 @@ impl<'tcx, N> ImplSource<'tcx, N> {
|
||||
ImplSource::Generator(c) => &c.nested[..],
|
||||
ImplSource::Object(d) => &d.nested[..],
|
||||
ImplSource::FnPointer(d) => &d.nested[..],
|
||||
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) => &[],
|
||||
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
|
||||
| ImplSource::Pointee(ImplSourcePointeeData) => &[],
|
||||
ImplSource::TraitAlias(d) => &d.nested[..],
|
||||
}
|
||||
}
|
||||
@ -557,6 +562,9 @@ impl<'tcx, N> ImplSource<'tcx, N> {
|
||||
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) => {
|
||||
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
|
||||
}
|
||||
ImplSource::Pointee(ImplSourcePointeeData) => {
|
||||
ImplSource::Pointee(ImplSourcePointeeData)
|
||||
}
|
||||
ImplSource::TraitAlias(d) => ImplSource::TraitAlias(ImplSourceTraitAliasData {
|
||||
alias_def_id: d.alias_def_id,
|
||||
substs: d.substs,
|
||||
@ -635,6 +643,9 @@ pub struct ImplSourceFnPointerData<'tcx, N> {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
|
||||
pub struct ImplSourceDiscriminantKindData;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
|
||||
pub struct ImplSourcePointeeData;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
|
||||
pub struct ImplSourceTraitAliasData<'tcx, N> {
|
||||
pub alias_def_id: DefId,
|
||||
|
@ -125,6 +125,9 @@ pub enum SelectionCandidate<'tcx> {
|
||||
/// Builtin implementation of `DiscriminantKind`.
|
||||
DiscriminantKindCandidate,
|
||||
|
||||
/// Builtin implementation of `Pointee`.
|
||||
PointeeCandidate,
|
||||
|
||||
TraitAliasCandidate(DefId),
|
||||
|
||||
/// Matching `dyn Trait` with a supertrait of `Trait`. The index is the
|
||||
|
@ -19,6 +19,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
|
||||
|
||||
super::ImplSource::DiscriminantKind(ref d) => write!(f, "{:?}", d),
|
||||
|
||||
super::ImplSource::Pointee(ref d) => write!(f, "{:?}", d),
|
||||
|
||||
super::ImplSource::Object(ref d) => write!(f, "{:?}", d),
|
||||
|
||||
super::ImplSource::Param(ref n, ct) => {
|
||||
@ -110,4 +112,5 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx,
|
||||
TrivialTypeFoldableAndLiftImpls! {
|
||||
super::IfExpressionCause,
|
||||
super::ImplSourceDiscriminantKindData,
|
||||
super::ImplSourcePointeeData,
|
||||
}
|
||||
|
@ -2133,6 +2133,54 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type of metadata for (potentially fat) pointers to this type.
|
||||
pub fn ptr_metadata_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
|
||||
// FIXME: should this normalize?
|
||||
let tail = tcx.struct_tail_without_normalization(self);
|
||||
match tail.kind() {
|
||||
// Sized types
|
||||
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Uint(_)
|
||||
| ty::Int(_)
|
||||
| ty::Bool
|
||||
| ty::Float(_)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Char
|
||||
| ty::Ref(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_)
|
||||
| ty::Foreign(..)
|
||||
// If returned by `struct_tail_without_normalization` this is a unit struct
|
||||
// without any fields, or not a struct, and therefore is Sized.
|
||||
| ty::Adt(..)
|
||||
// If returned by `struct_tail_without_normalization` this is the empty tuple,
|
||||
// a.k.a. unit type, which is Sized
|
||||
| ty::Tuple(..) => tcx.types.unit,
|
||||
|
||||
ty::Str | ty::Slice(_) => tcx.types.usize,
|
||||
ty::Dynamic(..) => {
|
||||
let dyn_metadata = tcx.lang_items().dyn_metadata().unwrap();
|
||||
tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()])
|
||||
},
|
||||
|
||||
ty::Projection(_)
|
||||
| ty::Param(_)
|
||||
| ty::Opaque(..)
|
||||
| ty::Infer(ty::TyVar(_))
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("`ptr_metadata_ty` applied to unexpected type: {:?}", tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When we create a closure, we record its kind (i.e., what trait
|
||||
/// it implements) into its `ClosureSubsts` using a type
|
||||
/// parameter. This is kind of a phantom type, except that the
|
||||
|
@ -475,6 +475,7 @@ symbols! {
|
||||
dropck_eyepatch,
|
||||
dropck_parametricity,
|
||||
dylib,
|
||||
dyn_metadata,
|
||||
dyn_trait,
|
||||
edition_macro_pats,
|
||||
eh_catch_typeinfo,
|
||||
@ -709,6 +710,7 @@ symbols! {
|
||||
memory,
|
||||
message,
|
||||
meta,
|
||||
metadata_type,
|
||||
min_align_of,
|
||||
min_align_of_val,
|
||||
min_const_fn,
|
||||
@ -831,6 +833,7 @@ symbols! {
|
||||
plugin,
|
||||
plugin_registrar,
|
||||
plugins,
|
||||
pointee_trait,
|
||||
pointer,
|
||||
pointer_trait,
|
||||
pointer_trait_fmt,
|
||||
|
@ -12,7 +12,7 @@ use super::SelectionContext;
|
||||
use super::SelectionError;
|
||||
use super::{
|
||||
ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
|
||||
ImplSourceGeneratorData, ImplSourceUserDefinedData,
|
||||
ImplSourceGeneratorData, ImplSourcePointeeData, ImplSourceUserDefinedData,
|
||||
};
|
||||
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
|
||||
|
||||
@ -1069,6 +1069,51 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
||||
| ty::Error(_) => false,
|
||||
}
|
||||
}
|
||||
super::ImplSource::Pointee(..) => {
|
||||
// While `Pointee` is automatically implemented for every type,
|
||||
// the concrete metadata type may not be known yet.
|
||||
//
|
||||
// Any type with multiple potential metadata types is therefore not eligible.
|
||||
let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
|
||||
|
||||
// FIXME: should this normalize?
|
||||
let tail = selcx.tcx().struct_tail_without_normalization(self_ty);
|
||||
match tail.kind() {
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Foreign(_)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Never
|
||||
// If returned by `struct_tail_without_normalization` this is a unit struct
|
||||
// without any fields, or not a struct, and therefore is Sized.
|
||||
| ty::Adt(..)
|
||||
// If returned by `struct_tail_without_normalization` this is the empty tuple.
|
||||
| ty::Tuple(..)
|
||||
// Integers and floats are always Sized, and so have unit type metadata.
|
||||
| ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
|
||||
|
||||
ty::Projection(..)
|
||||
| ty::Opaque(..)
|
||||
| ty::Param(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Infer(..)
|
||||
| ty::Error(_) => false,
|
||||
}
|
||||
}
|
||||
super::ImplSource::Param(..) => {
|
||||
// This case tell us nothing about the value of an
|
||||
// associated type. Consider:
|
||||
@ -1169,6 +1214,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
|
||||
super::ImplSource::DiscriminantKind(data) => {
|
||||
confirm_discriminant_kind_candidate(selcx, obligation, data)
|
||||
}
|
||||
super::ImplSource::Pointee(data) => confirm_pointee_candidate(selcx, obligation, data),
|
||||
super::ImplSource::Object(_)
|
||||
| super::ImplSource::AutoImpl(..)
|
||||
| super::ImplSource::Param(..)
|
||||
@ -1256,6 +1302,26 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>(
|
||||
confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
|
||||
}
|
||||
|
||||
fn confirm_pointee_candidate<'cx, 'tcx>(
|
||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
obligation: &ProjectionTyObligation<'tcx>,
|
||||
_: ImplSourcePointeeData,
|
||||
) -> Progress<'tcx> {
|
||||
let tcx = selcx.tcx();
|
||||
|
||||
let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
|
||||
let substs = tcx.mk_substs([self_ty.into()].iter());
|
||||
|
||||
let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
|
||||
|
||||
let predicate = ty::ProjectionPredicate {
|
||||
projection_ty: ty::ProjectionTy { substs, item_def_id: metadata_def_id },
|
||||
ty: self_ty.ptr_metadata_ty(tcx),
|
||||
};
|
||||
|
||||
confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate), false)
|
||||
}
|
||||
|
||||
fn confirm_fn_pointer_candidate<'cx, 'tcx>(
|
||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
obligation: &ProjectionTyObligation<'tcx>,
|
||||
|
@ -267,6 +267,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
} else if lang_items.discriminant_kind_trait() == Some(def_id) {
|
||||
// `DiscriminantKind` is automatically implemented for every type.
|
||||
candidates.vec.push(DiscriminantKindCandidate);
|
||||
} else if lang_items.pointee_trait() == Some(def_id) {
|
||||
// `Pointee` is automatically implemented for every type.
|
||||
candidates.vec.push(PointeeCandidate);
|
||||
} else if lang_items.sized_trait() == Some(def_id) {
|
||||
// Sized is never implementable by end-users, it is
|
||||
// always automatically computed.
|
||||
|
@ -30,7 +30,8 @@ use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation};
|
||||
use crate::traits::{
|
||||
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
|
||||
ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData,
|
||||
ImplSourceObjectData, ImplSourceTraitAliasData, ImplSourceUserDefinedData,
|
||||
ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData,
|
||||
ImplSourceUserDefinedData,
|
||||
};
|
||||
use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation};
|
||||
use crate::traits::{Obligation, ObligationCause};
|
||||
@ -99,6 +100,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
Ok(ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData))
|
||||
}
|
||||
|
||||
PointeeCandidate => Ok(ImplSource::Pointee(ImplSourcePointeeData)),
|
||||
|
||||
TraitAliasCandidate(alias_def_id) => {
|
||||
let data = self.confirm_trait_alias_candidate(obligation, alias_def_id);
|
||||
Ok(ImplSource::TraitAlias(data))
|
||||
|
@ -1318,8 +1318,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
let is_global =
|
||||
|cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions();
|
||||
|
||||
// (*) Prefer `BuiltinCandidate { has_nested: false }` and `DiscriminantKindCandidate`
|
||||
// to anything else.
|
||||
// (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
|
||||
// and `DiscriminantKindCandidate` to anything else.
|
||||
//
|
||||
// This is a fix for #53123 and prevents winnowing from accidentally extending the
|
||||
// lifetime of a variable.
|
||||
@ -1332,8 +1332,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
}
|
||||
|
||||
// (*)
|
||||
(BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate, _) => true,
|
||||
(_, BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate) => false,
|
||||
(
|
||||
BuiltinCandidate { has_nested: false }
|
||||
| DiscriminantKindCandidate
|
||||
| PointeeCandidate,
|
||||
_,
|
||||
) => true,
|
||||
(
|
||||
_,
|
||||
BuiltinCandidate { has_nested: false }
|
||||
| DiscriminantKindCandidate
|
||||
| PointeeCandidate,
|
||||
) => false,
|
||||
|
||||
(ParamCandidate(other), ParamCandidate(victim)) => {
|
||||
if other.value == victim.value && victim.constness == Constness::NotConst {
|
||||
|
@ -275,7 +275,8 @@ fn resolve_associated_item<'tcx>(
|
||||
traits::ImplSource::AutoImpl(..)
|
||||
| traits::ImplSource::Param(..)
|
||||
| traits::ImplSource::TraitAlias(..)
|
||||
| traits::ImplSource::DiscriminantKind(..) => None,
|
||||
| traits::ImplSource::DiscriminantKind(..)
|
||||
| traits::ImplSource::Pointee(..) => None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,20 @@ fn enforce_trait_manually_implementable(
|
||||
let did = Some(trait_def_id);
|
||||
let li = tcx.lang_items();
|
||||
|
||||
// Disallow *all* explicit impls of `DiscriminantKind`, `Sized` and `Unsize` for now.
|
||||
// Disallow *all* explicit impls of `Pointee`, `DiscriminantKind`, `Sized` and `Unsize` for now.
|
||||
if did == li.pointee_trait() {
|
||||
let span = impl_header_span(tcx, impl_def_id);
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
E0322,
|
||||
"explicit impls for the `Pointee` trait are not permitted"
|
||||
)
|
||||
.span_label(span, "impl of 'Pointee' not allowed")
|
||||
.emit();
|
||||
return;
|
||||
}
|
||||
|
||||
if did == li.discriminant_kind_trait() {
|
||||
let span = impl_header_span(tcx, impl_def_id);
|
||||
struct_span_err!(
|
||||
|
@ -673,19 +673,28 @@ mod impls {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> Hash for *const T {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
if mem::size_of::<Self>() == mem::size_of::<usize>() {
|
||||
// Thin pointer
|
||||
state.write_usize(*self as *const () as usize);
|
||||
} else {
|
||||
// Fat pointer
|
||||
// SAFETY: we are accessing the memory occupied by `self`
|
||||
// which is guaranteed to be valid.
|
||||
// This assumes a fat pointer can be represented by a `(usize, usize)`,
|
||||
// which is safe to do in `std` because it is shipped and kept in sync
|
||||
// with the implementation of fat pointers in `rustc`.
|
||||
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
|
||||
state.write_usize(a);
|
||||
state.write_usize(b);
|
||||
#[cfg(not(bootstrap))]
|
||||
{
|
||||
let (address, metadata) = self.to_raw_parts();
|
||||
state.write_usize(address as usize);
|
||||
metadata.hash(state);
|
||||
}
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
if mem::size_of::<Self>() == mem::size_of::<usize>() {
|
||||
// Thin pointer
|
||||
state.write_usize(*self as *const () as usize);
|
||||
} else {
|
||||
// Fat pointer
|
||||
// SAFETY: we are accessing the memory occupied by `self`
|
||||
// which is guaranteed to be valid.
|
||||
// This assumes a fat pointer can be represented by a `(usize, usize)`,
|
||||
// which is safe to do in `std` because it is shipped and kept in sync
|
||||
// with the implementation of fat pointers in `rustc`.
|
||||
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
|
||||
state.write_usize(a);
|
||||
state.write_usize(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -693,19 +702,28 @@ mod impls {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> Hash for *mut T {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
if mem::size_of::<Self>() == mem::size_of::<usize>() {
|
||||
// Thin pointer
|
||||
state.write_usize(*self as *const () as usize);
|
||||
} else {
|
||||
// Fat pointer
|
||||
// SAFETY: we are accessing the memory occupied by `self`
|
||||
// which is guaranteed to be valid.
|
||||
// This assumes a fat pointer can be represented by a `(usize, usize)`,
|
||||
// which is safe to do in `std` because it is shipped and kept in sync
|
||||
// with the implementation of fat pointers in `rustc`.
|
||||
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
|
||||
state.write_usize(a);
|
||||
state.write_usize(b);
|
||||
#[cfg(not(bootstrap))]
|
||||
{
|
||||
let (address, metadata) = self.to_raw_parts();
|
||||
state.write_usize(address as usize);
|
||||
metadata.hash(state);
|
||||
}
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
if mem::size_of::<Self>() == mem::size_of::<usize>() {
|
||||
// Thin pointer
|
||||
state.write_usize(*self as *const () as usize);
|
||||
} else {
|
||||
// Fat pointer
|
||||
// SAFETY: we are accessing the memory occupied by `self`
|
||||
// which is guaranteed to be valid.
|
||||
// This assumes a fat pointer can be represented by a `(usize, usize)`,
|
||||
// which is safe to do in `std` because it is shipped and kept in sync
|
||||
// with the implementation of fat pointers in `rustc`.
|
||||
let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) };
|
||||
state.write_usize(a);
|
||||
state.write_usize(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,7 @@
|
||||
#![feature(extended_key_value_attributes)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(fundamental)]
|
||||
#![cfg_attr(not(bootstrap), feature(intra_doc_pointers))]
|
||||
#![feature(intrinsics)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(link_llvm_intrinsics)]
|
||||
@ -123,6 +124,7 @@
|
||||
#![feature(auto_traits)]
|
||||
#![feature(or_patterns)]
|
||||
#![feature(prelude_import)]
|
||||
#![cfg_attr(not(bootstrap), feature(ptr_metadata))]
|
||||
#![feature(repr_simd, platform_intrinsics)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(simd_ffi)]
|
||||
@ -133,6 +135,7 @@
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(str_split_as_str)]
|
||||
#![feature(str_split_inclusive_as_str)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(transparent_unions)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
@ -48,6 +48,17 @@ impl<T: ?Sized> *const T {
|
||||
self as _
|
||||
}
|
||||
|
||||
/// Decompose a (possibly wide) pointer into is address and metadata components.
|
||||
///
|
||||
/// The pointer can be later reconstructed with [`from_raw_parts`].
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn to_raw_parts(self) -> (*const (), <T as super::Pointee>::Metadata) {
|
||||
(self.cast(), metadata(self))
|
||||
}
|
||||
|
||||
/// Returns `None` if the pointer is null, or else returns a shared reference to
|
||||
/// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`]
|
||||
/// must be used instead.
|
||||
@ -905,9 +916,14 @@ impl<T> *const [T] {
|
||||
#[unstable(feature = "slice_ptr_len", issue = "71146")]
|
||||
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
|
||||
pub const fn len(self) -> usize {
|
||||
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
|
||||
// Only `std` can make this guarantee.
|
||||
unsafe { Repr { rust: self }.raw }.len
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
|
||||
// Only `std` can make this guarantee.
|
||||
unsafe { Repr { rust: self }.raw }.len
|
||||
}
|
||||
#[cfg(not(bootstrap))]
|
||||
metadata(self)
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the slice's buffer.
|
||||
|
265
library/core/src/ptr/metadata.rs
Normal file
265
library/core/src/ptr/metadata.rs
Normal file
@ -0,0 +1,265 @@
|
||||
#![unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
|
||||
use crate::fmt;
|
||||
use crate::hash::{Hash, Hasher};
|
||||
|
||||
/// Provides the pointer metadata type of any pointed-to type.
|
||||
///
|
||||
/// # Pointer metadata
|
||||
///
|
||||
/// Raw pointer types and reference types in Rust can be thought of as made of two parts:
|
||||
/// a data pointer that contains the memory address of the value, and some metadata.
|
||||
///
|
||||
/// For statically-sized types (that implement the `Sized` traits)
|
||||
/// as well as for `extern` types,
|
||||
/// pointers are said to be “thin”: metadata is zero-sized and its type is `()`.
|
||||
///
|
||||
/// Pointers to [dynamically-sized types][dst] are said to be “wide” or “fat”,
|
||||
/// they have non-zero-sized metadata:
|
||||
///
|
||||
/// * For structs whose last field is a DST, metadata is the metadata for the last field
|
||||
/// * For the `str` type, metadata is the length in bytes as `usize`
|
||||
/// * For slice types like `[T]`, metadata is the length in items as `usize`
|
||||
/// * For trait objects like `dyn SomeTrait`, metadata is [`DynMetadata<Self>`][DynMetadata]
|
||||
/// (e.g. `DynMetadata<dyn SomeTrait>`)
|
||||
///
|
||||
/// In the future, the Rust language may gain new kinds of types
|
||||
/// that have different pointer metadata.
|
||||
///
|
||||
/// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts
|
||||
///
|
||||
///
|
||||
/// # The `Pointee` trait
|
||||
///
|
||||
/// The point of this trait is its `Metadata` associated type,
|
||||
/// which is `()` or `usize` or `DynMetadata<_>` as described above.
|
||||
/// It is automatically implemented for every type.
|
||||
/// It can be assumed to be implemented in a generic context, even without a corresponding bound.
|
||||
///
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// Raw pointers can be decomposed into the data address and metadata components
|
||||
/// with their [`to_raw_parts`] method.
|
||||
///
|
||||
/// Alternatively, metadata alone can be extracted with the [`metadata`] function.
|
||||
/// A reference can be passed to [`metadata`] and implicitly coerced.
|
||||
///
|
||||
/// A (possibly-wide) pointer can be put back together from its address and metadata
|
||||
/// with [`from_raw_parts`] or [`from_raw_parts_mut`].
|
||||
///
|
||||
/// [`to_raw_parts`]: *const::to_raw_parts
|
||||
#[lang = "pointee_trait"]
|
||||
pub trait Pointee {
|
||||
/// The type for metadata in pointers and references to `Self`.
|
||||
#[lang = "metadata_type"]
|
||||
// NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata`
|
||||
// in `library/core/src/ptr/metadata.rs`
|
||||
// in sync with those here:
|
||||
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
|
||||
}
|
||||
|
||||
/// Pointers to types implementing this trait alias are “thin”.
|
||||
///
|
||||
/// This includes statically-`Sized` types and `extern` types.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(ptr_metadata)]
|
||||
///
|
||||
/// fn this_never_panics<T: std::ptr::Thin>() {
|
||||
/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::<usize>())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
// NOTE: don’t stabilize this before trait aliases are stable in the language?
|
||||
pub trait Thin = Pointee<Metadata = ()>;
|
||||
|
||||
/// Extract the metadata component of a pointer.
|
||||
///
|
||||
/// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function
|
||||
/// as they implicitly coerce to `*const T`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ptr_metadata)]
|
||||
///
|
||||
/// assert_eq!(std::ptr::metadata("foo"), 3_usize);
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
|
||||
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
|
||||
// and PtrComponents<T> have the same memory layouts. Only std can make this
|
||||
// guarantee.
|
||||
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
|
||||
}
|
||||
|
||||
/// Forms a (possibly-wide) raw pointer from a data address and metadata.
|
||||
///
|
||||
/// This function is safe but the returned pointer is not necessarily safe to dereference.
|
||||
/// For slices, see the documentation of [`slice::from_raw_parts`] for safety requirements.
|
||||
/// For trait objects, the metadata must come from a pointer to the same underlying ereased type.
|
||||
///
|
||||
/// [`slice::from_raw_parts`]: crate::slice::from_raw_parts
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn from_raw_parts<T: ?Sized>(
|
||||
data_address: *const (),
|
||||
metadata: <T as Pointee>::Metadata,
|
||||
) -> *const T {
|
||||
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
|
||||
// and PtrComponents<T> have the same memory layouts. Only std can make this
|
||||
// guarantee.
|
||||
unsafe { PtrRepr { components: PtrComponents { data_address, metadata } }.const_ptr }
|
||||
}
|
||||
|
||||
/// Performs the same functionality as [`from_raw_parts`], except that a
|
||||
/// raw `*mut` pointer is returned, as opposed to a raw `*const` pointer.
|
||||
///
|
||||
/// See the documentation of [`from_raw_parts`] for more details.
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn from_raw_parts_mut<T: ?Sized>(
|
||||
data_address: *mut (),
|
||||
metadata: <T as Pointee>::Metadata,
|
||||
) -> *mut T {
|
||||
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
|
||||
// and PtrComponents<T> have the same memory layouts. Only std can make this
|
||||
// guarantee.
|
||||
unsafe { PtrRepr { components: PtrComponents { data_address, metadata } }.mut_ptr }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) union PtrRepr<T: ?Sized> {
|
||||
pub(crate) const_ptr: *const T,
|
||||
pub(crate) mut_ptr: *mut T,
|
||||
pub(crate) components: PtrComponents<T>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct PtrComponents<T: ?Sized> {
|
||||
pub(crate) data_address: *const (),
|
||||
pub(crate) metadata: <T as Pointee>::Metadata,
|
||||
}
|
||||
|
||||
// Manual impl needed to avoid `T: Copy` bound.
|
||||
impl<T: ?Sized> Copy for PtrComponents<T> {}
|
||||
|
||||
// Manual impl needed to avoid `T: Clone` bound.
|
||||
impl<T: ?Sized> Clone for PtrComponents<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
/// The metadata for a `Dyn = dyn SomeTrait` trait object type.
|
||||
///
|
||||
/// It is a pointer to a vtable (virtual call table)
|
||||
/// that represents all the necessary information
|
||||
/// to manipulate the concrete type stored inside a trait object.
|
||||
/// The vtable notably it contains:
|
||||
///
|
||||
/// * type size
|
||||
/// * type alignment
|
||||
/// * a pointer to the type’s `drop_in_place` impl (may be a no-op for plain-old-data)
|
||||
/// * pointers to all the methods for the type’s implementation of the trait
|
||||
///
|
||||
/// Note that the first three are special because they’re necessary to allocate, drop,
|
||||
/// and deallocate any trait object.
|
||||
///
|
||||
/// It is possible to name this struct with a type parameter that is not a `dyn` trait object
|
||||
/// (for example `DynMetadata<u64>`) but not to obtain a meaningful value of that struct.
|
||||
#[lang = "dyn_metadata"]
|
||||
pub struct DynMetadata<Dyn: ?Sized> {
|
||||
vtable_ptr: &'static VTable,
|
||||
phantom: crate::marker::PhantomData<Dyn>,
|
||||
}
|
||||
|
||||
/// The common prefix of all vtables. It is followed by function pointers for trait methods.
|
||||
///
|
||||
/// Private implementation detail of `DynMetadata::size_of` etc.
|
||||
#[repr(C)]
|
||||
struct VTable {
|
||||
drop_in_place: fn(*mut ()),
|
||||
size_of: usize,
|
||||
align_of: usize,
|
||||
}
|
||||
|
||||
impl<Dyn: ?Sized> DynMetadata<Dyn> {
|
||||
/// Returns the size of the type associated with this vtable.
|
||||
#[inline]
|
||||
pub fn size_of(self) -> usize {
|
||||
self.vtable_ptr.size_of
|
||||
}
|
||||
|
||||
/// Returns the alignment of the type associated with this vtable.
|
||||
#[inline]
|
||||
pub fn align_of(self) -> usize {
|
||||
self.vtable_ptr.align_of
|
||||
}
|
||||
|
||||
/// Returns the size and alignment together as a `Layout`
|
||||
#[inline]
|
||||
pub fn layout(self) -> crate::alloc::Layout {
|
||||
// SAFETY: the compiler emitted this vtable for a concrete Rust type which
|
||||
// is known to have a valid layout. Same rationale as in `Layout::for_value`.
|
||||
unsafe { crate::alloc::Layout::from_size_align_unchecked(self.size_of(), self.align_of()) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<Dyn: ?Sized> Send for DynMetadata<Dyn> {}
|
||||
unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {}
|
||||
|
||||
impl<Dyn: ?Sized> fmt::Debug for DynMetadata<Dyn> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("DynMetadata").field(&(self.vtable_ptr as *const VTable)).finish()
|
||||
}
|
||||
}
|
||||
|
||||
// Manual impls needed to avoid `Dyn: $Trait` bounds.
|
||||
|
||||
impl<Dyn: ?Sized> Unpin for DynMetadata<Dyn> {}
|
||||
|
||||
impl<Dyn: ?Sized> Copy for DynMetadata<Dyn> {}
|
||||
|
||||
impl<Dyn: ?Sized> Clone for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {}
|
||||
|
||||
impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
crate::ptr::eq::<VTable>(self.vtable_ptr, other.vtable_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> crate::cmp::Ordering {
|
||||
(self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<crate::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
crate::ptr::hash::<VTable, _>(self.vtable_ptr, hasher)
|
||||
}
|
||||
}
|
@ -82,6 +82,14 @@ pub use crate::intrinsics::copy;
|
||||
#[doc(inline)]
|
||||
pub use crate::intrinsics::write_bytes;
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
mod metadata;
|
||||
#[cfg(not(bootstrap))]
|
||||
pub(crate) use metadata::PtrRepr;
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin};
|
||||
|
||||
mod non_null;
|
||||
#[stable(feature = "nonnull", since = "1.25.0")]
|
||||
pub use non_null::NonNull;
|
||||
@ -220,6 +228,7 @@ pub const fn null_mut<T>() -> *mut T {
|
||||
0 as *mut T
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[repr(C)]
|
||||
pub(crate) union Repr<T> {
|
||||
pub(crate) rust: *const [T],
|
||||
@ -227,12 +236,14 @@ pub(crate) union Repr<T> {
|
||||
pub(crate) raw: FatPtr<T>,
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct FatPtr<T> {
|
||||
data: *const T,
|
||||
pub(crate) len: usize,
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
// Manual impl needed to avoid `T: Clone` bound.
|
||||
impl<T> Clone for FatPtr<T> {
|
||||
fn clone(&self) -> Self {
|
||||
@ -240,6 +251,7 @@ impl<T> Clone for FatPtr<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
// Manual impl needed to avoid `T: Copy` bound.
|
||||
impl<T> Copy for FatPtr<T> {}
|
||||
|
||||
@ -267,10 +279,15 @@ impl<T> Copy for FatPtr<T> {}
|
||||
#[stable(feature = "slice_from_raw_parts", since = "1.42.0")]
|
||||
#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")]
|
||||
pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
|
||||
// SAFETY: Accessing the value from the `Repr` union is safe since *const [T]
|
||||
// and FatPtr have the same memory layouts. Only std can make this
|
||||
// guarantee.
|
||||
unsafe { Repr { raw: FatPtr { data, len } }.rust }
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
// SAFETY: Accessing the value from the `Repr` union is safe since *const [T]
|
||||
// and FatPtr have the same memory layouts. Only std can make this
|
||||
// guarantee.
|
||||
unsafe { Repr { raw: FatPtr { data, len } }.rust }
|
||||
}
|
||||
#[cfg(not(bootstrap))]
|
||||
from_raw_parts(data.cast(), len)
|
||||
}
|
||||
|
||||
/// Performs the same functionality as [`slice_from_raw_parts`], except that a
|
||||
@ -302,9 +319,14 @@ pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
|
||||
#[stable(feature = "slice_from_raw_parts", since = "1.42.0")]
|
||||
#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")]
|
||||
pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
|
||||
// SAFETY: Accessing the value from the `Repr` union is safe since *mut [T]
|
||||
// and FatPtr have the same memory layouts
|
||||
unsafe { Repr { raw: FatPtr { data, len } }.rust_mut }
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
// SAFETY: Accessing the value from the `Repr` union is safe since *mut [T]
|
||||
// and FatPtr have the same memory layouts
|
||||
unsafe { Repr { raw: FatPtr { data, len } }.rust_mut }
|
||||
}
|
||||
#[cfg(not(bootstrap))]
|
||||
from_raw_parts_mut(data.cast(), len)
|
||||
}
|
||||
|
||||
/// Swaps the values at two mutable locations of the same type, without
|
||||
|
@ -47,6 +47,17 @@ impl<T: ?Sized> *mut T {
|
||||
self as _
|
||||
}
|
||||
|
||||
/// Decompose a (possibly wide) pointer into is address and metadata components.
|
||||
///
|
||||
/// The pointer can be later reconstructed with [`from_raw_parts_mut`].
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn to_raw_parts(self) -> (*mut (), <T as super::Pointee>::Metadata) {
|
||||
(self.cast(), super::metadata(self))
|
||||
}
|
||||
|
||||
/// Returns `None` if the pointer is null, or else returns a shared reference to
|
||||
/// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`]
|
||||
/// must be used instead.
|
||||
@ -1162,9 +1173,14 @@ impl<T> *mut [T] {
|
||||
#[unstable(feature = "slice_ptr_len", issue = "71146")]
|
||||
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
|
||||
pub const fn len(self) -> usize {
|
||||
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
|
||||
// Only `std` can make this guarantee.
|
||||
unsafe { Repr { rust_mut: self }.raw }.len
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
// SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
|
||||
// Only `std` can make this guarantee.
|
||||
unsafe { Repr { rust_mut: self }.raw }.len
|
||||
}
|
||||
#[cfg(not(bootstrap))]
|
||||
metadata(self)
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the slice's buffer.
|
||||
|
@ -175,6 +175,37 @@ impl<T: ?Sized> NonNull<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the same functionality as [`std::ptr::from_raw_parts`], except that a
|
||||
/// `NonNull` pointer is returned, as opposed to a raw `*const` pointer.
|
||||
///
|
||||
/// See the documentation of [`std::ptr::from_raw_parts`] for more details.
|
||||
///
|
||||
/// [`std::ptr::from_raw_parts`]: crate::ptr::from_raw_parts
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn from_raw_parts(
|
||||
data_address: NonNull<()>,
|
||||
metadata: <T as super::Pointee>::Metadata,
|
||||
) -> NonNull<T> {
|
||||
// SAFETY: The result of `ptr::from::raw_parts_mut` is non-null because `data_address` is.
|
||||
unsafe {
|
||||
NonNull::new_unchecked(super::from_raw_parts_mut(data_address.as_ptr(), metadata))
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompose a (possibly wide) pointer into is address and metadata components.
|
||||
///
|
||||
/// The pointer can be later reconstructed with [`NonNull::from_raw_parts`].
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
|
||||
#[inline]
|
||||
pub const fn to_raw_parts(self) -> (NonNull<()>, <T as super::Pointee>::Metadata) {
|
||||
(self.cast(), super::metadata(self.as_ptr()))
|
||||
}
|
||||
|
||||
/// Acquires the underlying `*mut` pointer.
|
||||
#[stable(feature = "nonnull", since = "1.25.0")]
|
||||
#[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")]
|
||||
|
@ -94,9 +94,23 @@ impl<T> [T] {
|
||||
// SAFETY: const sound because we transmute out the length field as a usize (which it must be)
|
||||
#[rustc_allow_const_fn_unstable(const_fn_union)]
|
||||
pub const fn len(&self) -> usize {
|
||||
// SAFETY: this is safe because `&[T]` and `FatPtr<T>` have the same layout.
|
||||
// Only `std` can make this guarantee.
|
||||
unsafe { crate::ptr::Repr { rust: self }.raw.len }
|
||||
#[cfg(bootstrap)]
|
||||
{
|
||||
// SAFETY: this is safe because `&[T]` and `FatPtr<T>` have the same layout.
|
||||
// Only `std` can make this guarantee.
|
||||
unsafe { crate::ptr::Repr { rust: self }.raw.len }
|
||||
}
|
||||
#[cfg(not(bootstrap))]
|
||||
{
|
||||
// FIXME: Replace with `crate::ptr::metadata(self)` when that is const-stable.
|
||||
// As of this writing this causes a "Const-stable functions can only call other
|
||||
// const-stable functions" error.
|
||||
|
||||
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
|
||||
// and PtrComponents<T> have the same memory layouts. Only std can make this
|
||||
// guarantee.
|
||||
unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the slice has a length of 0.
|
||||
|
@ -27,6 +27,7 @@
|
||||
#![feature(duration_saturating_ops)]
|
||||
#![feature(duration_zero)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(fixed_size_array)]
|
||||
#![feature(flt2dec)]
|
||||
#![feature(fmt_internals)]
|
||||
@ -68,8 +69,10 @@
|
||||
#![feature(result_into_ok_or_err)]
|
||||
#![feature(option_unwrap_none)]
|
||||
#![feature(peekable_peek_mut)]
|
||||
#![cfg_attr(not(bootstrap), feature(ptr_metadata))]
|
||||
#![feature(once_cell)]
|
||||
#![feature(unsafe_block_in_unsafe_fn)]
|
||||
#![feature(unsized_tuple_coercion)]
|
||||
#![feature(int_bits_const)]
|
||||
#![feature(nonzero_leading_trailing_zeros)]
|
||||
#![feature(const_option)]
|
||||
@ -77,6 +80,7 @@
|
||||
#![feature(slice_group_by)]
|
||||
#![feature(trusted_random_access)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(not(bootstrap), feature(unsize))]
|
||||
|
||||
extern crate test;
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
use core::cell::RefCell;
|
||||
#[cfg(not(bootstrap))]
|
||||
use core::ptr;
|
||||
use core::ptr::*;
|
||||
#[cfg(not(bootstrap))]
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
#[test]
|
||||
fn test_const_from_raw_parts() {
|
||||
@ -413,3 +417,253 @@ fn offset_from() {
|
||||
assert_eq!(ptr2.offset(-2), ptr1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(bootstrap))]
|
||||
fn ptr_metadata() {
|
||||
struct Unit;
|
||||
struct Pair<A, B: ?Sized>(A, B);
|
||||
extern "C" {
|
||||
type Extern;
|
||||
}
|
||||
let () = metadata(&());
|
||||
let () = metadata(&Unit);
|
||||
let () = metadata(&4_u32);
|
||||
let () = metadata(&String::new());
|
||||
let () = metadata(&Some(4_u32));
|
||||
let () = metadata(&ptr_metadata);
|
||||
let () = metadata(&|| {});
|
||||
let () = metadata(&[4, 7]);
|
||||
let () = metadata(&(4, String::new()));
|
||||
let () = metadata(&Pair(4, String::new()));
|
||||
let () = metadata(0 as *const Extern);
|
||||
let () = metadata(0 as *const <&u32 as std::ops::Deref>::Target);
|
||||
|
||||
assert_eq!(metadata("foo"), 3_usize);
|
||||
assert_eq!(metadata(&[4, 7][..]), 2_usize);
|
||||
|
||||
let dst_tuple: &(bool, [u8]) = &(true, [0x66, 0x6F, 0x6F]);
|
||||
let dst_struct: &Pair<bool, [u8]> = &Pair(true, [0x66, 0x6F, 0x6F]);
|
||||
assert_eq!(metadata(dst_tuple), 3_usize);
|
||||
assert_eq!(metadata(dst_struct), 3_usize);
|
||||
unsafe {
|
||||
let dst_tuple: &(bool, str) = std::mem::transmute(dst_tuple);
|
||||
let dst_struct: &Pair<bool, str> = std::mem::transmute(dst_struct);
|
||||
assert_eq!(&dst_tuple.1, "foo");
|
||||
assert_eq!(&dst_struct.1, "foo");
|
||||
assert_eq!(metadata(dst_tuple), 3_usize);
|
||||
assert_eq!(metadata(dst_struct), 3_usize);
|
||||
}
|
||||
|
||||
let vtable_1: DynMetadata<dyn Debug> = metadata(&4_u16 as &dyn Debug);
|
||||
let vtable_2: DynMetadata<dyn Display> = metadata(&4_u16 as &dyn Display);
|
||||
let vtable_3: DynMetadata<dyn Display> = metadata(&4_u32 as &dyn Display);
|
||||
let vtable_4: DynMetadata<dyn Display> = metadata(&(true, 7_u32) as &(bool, dyn Display));
|
||||
let vtable_5: DynMetadata<dyn Display> =
|
||||
metadata(&Pair(true, 7_u32) as &Pair<bool, dyn Display>);
|
||||
unsafe {
|
||||
let address_1: usize = std::mem::transmute(vtable_1);
|
||||
let address_2: usize = std::mem::transmute(vtable_2);
|
||||
let address_3: usize = std::mem::transmute(vtable_3);
|
||||
let address_4: usize = std::mem::transmute(vtable_4);
|
||||
let address_5: usize = std::mem::transmute(vtable_5);
|
||||
// Different trait => different vtable pointer
|
||||
assert_ne!(address_1, address_2);
|
||||
// Different erased type => different vtable pointer
|
||||
assert_ne!(address_2, address_3);
|
||||
// Same erased type and same trait => same vtable pointer
|
||||
assert_eq!(address_3, address_4);
|
||||
assert_eq!(address_3, address_5);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(bootstrap))]
|
||||
fn ptr_metadata_bounds() {
|
||||
fn metadata_eq_method_address<T: ?Sized>() -> usize {
|
||||
// The `Metadata` associated type has an `Ord` bound, so this is valid:
|
||||
<<T as Pointee>::Metadata as PartialEq>::eq as usize
|
||||
}
|
||||
// "Synthetic" trait impls generated by the compiler like those of `Pointee`
|
||||
// are not checked for bounds of associated type.
|
||||
// So with a buggy libcore we could have both:
|
||||
// * `<dyn Display as Pointee>::Metadata == DynMetadata`
|
||||
// * `DynMetadata: !PartialEq`
|
||||
// … and cause an ICE here:
|
||||
metadata_eq_method_address::<dyn Display>();
|
||||
|
||||
// For this reason, let’s check here that bounds are satisfied:
|
||||
|
||||
let _ = static_assert_expected_bounds_for_metadata::<()>;
|
||||
let _ = static_assert_expected_bounds_for_metadata::<usize>;
|
||||
let _ = static_assert_expected_bounds_for_metadata::<DynMetadata<dyn Display>>;
|
||||
fn _static_assert_associated_type<T: ?Sized>() {
|
||||
let _ = static_assert_expected_bounds_for_metadata::<<T as Pointee>::Metadata>;
|
||||
}
|
||||
|
||||
fn static_assert_expected_bounds_for_metadata<Meta>()
|
||||
where
|
||||
// Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs`
|
||||
Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(bootstrap))]
|
||||
fn dyn_metadata() {
|
||||
#[derive(Debug)]
|
||||
#[repr(align(32))]
|
||||
struct Something([u8; 47]);
|
||||
|
||||
let value = Something([0; 47]);
|
||||
let trait_object: &dyn Debug = &value;
|
||||
let meta = metadata(trait_object);
|
||||
|
||||
assert_eq!(meta.size_of(), 64);
|
||||
assert_eq!(meta.size_of(), std::mem::size_of::<Something>());
|
||||
assert_eq!(meta.align_of(), 32);
|
||||
assert_eq!(meta.align_of(), std::mem::align_of::<Something>());
|
||||
assert_eq!(meta.layout(), std::alloc::Layout::new::<Something>());
|
||||
|
||||
assert!(format!("{:?}", meta).starts_with("DynMetadata(0x"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(bootstrap))]
|
||||
fn from_raw_parts() {
|
||||
let mut value = 5_u32;
|
||||
let address = &mut value as *mut _ as *mut ();
|
||||
let trait_object: &dyn Display = &mut value;
|
||||
let vtable = metadata(trait_object);
|
||||
let trait_object = NonNull::from(trait_object);
|
||||
|
||||
assert_eq!(ptr::from_raw_parts(address, vtable), trait_object.as_ptr());
|
||||
assert_eq!(ptr::from_raw_parts_mut(address, vtable), trait_object.as_ptr());
|
||||
assert_eq!(NonNull::from_raw_parts(NonNull::new(address).unwrap(), vtable), trait_object);
|
||||
|
||||
let mut array = [5_u32, 5, 5, 5, 5];
|
||||
let address = &mut array as *mut _ as *mut ();
|
||||
let array_ptr = NonNull::from(&mut array);
|
||||
let slice_ptr = NonNull::from(&mut array[..]);
|
||||
|
||||
assert_eq!(ptr::from_raw_parts(address, ()), array_ptr.as_ptr());
|
||||
assert_eq!(ptr::from_raw_parts_mut(address, ()), array_ptr.as_ptr());
|
||||
assert_eq!(NonNull::from_raw_parts(NonNull::new(address).unwrap(), ()), array_ptr);
|
||||
|
||||
assert_eq!(ptr::from_raw_parts(address, 5), slice_ptr.as_ptr());
|
||||
assert_eq!(ptr::from_raw_parts_mut(address, 5), slice_ptr.as_ptr());
|
||||
assert_eq!(NonNull::from_raw_parts(NonNull::new(address).unwrap(), 5), slice_ptr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(bootstrap))]
|
||||
fn thin_box() {
|
||||
let foo = ThinBox::<dyn Display>::new(4);
|
||||
assert_eq!(foo.to_string(), "4");
|
||||
drop(foo);
|
||||
let bar = ThinBox::<dyn Display>::new(7);
|
||||
assert_eq!(bar.to_string(), "7");
|
||||
|
||||
// A slightly more interesting library that could be built on top of metadata APIs.
|
||||
//
|
||||
// * It could be generalized to any `T: ?Sized` (not just trait object)
|
||||
// if `{size,align}_of_for_meta<T: ?Sized>(T::Metadata)` are added.
|
||||
// * Constructing a `ThinBox` without consuming and deallocating a `Box`
|
||||
// requires either the unstable `Unsize` marker trait,
|
||||
// or the unstable `unsized_locals` language feature,
|
||||
// or taking `&dyn T` and restricting to `T: Copy`.
|
||||
|
||||
use std::alloc::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct ThinBox<T>
|
||||
where
|
||||
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
|
||||
{
|
||||
ptr: NonNull<DynMetadata<T>>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> ThinBox<T>
|
||||
where
|
||||
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
|
||||
{
|
||||
pub fn new<Value: std::marker::Unsize<T>>(value: Value) -> Self {
|
||||
let unsized_: &T = &value;
|
||||
let meta = metadata(unsized_);
|
||||
let meta_layout = Layout::for_value(&meta);
|
||||
let value_layout = Layout::for_value(&value);
|
||||
let (layout, offset) = meta_layout.extend(value_layout).unwrap();
|
||||
// `DynMetadata` is pointer-sized:
|
||||
assert!(layout.size() > 0);
|
||||
// If `ThinBox<T>` is generalized to any `T: ?Sized`,
|
||||
// handle ZSTs with a dangling pointer without going through `alloc()`,
|
||||
// like `Box<T>` does.
|
||||
unsafe {
|
||||
let ptr = NonNull::new(alloc(layout))
|
||||
.unwrap_or_else(|| handle_alloc_error(layout))
|
||||
.cast::<DynMetadata<T>>();
|
||||
ptr.as_ptr().write(meta);
|
||||
ptr.cast::<u8>().as_ptr().add(offset).cast::<Value>().write(value);
|
||||
Self { ptr, phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
fn meta(&self) -> DynMetadata<T> {
|
||||
unsafe { *self.ptr.as_ref() }
|
||||
}
|
||||
|
||||
fn layout(&self) -> (Layout, usize) {
|
||||
let meta = self.meta();
|
||||
Layout::for_value(&meta).extend(meta.layout()).unwrap()
|
||||
}
|
||||
|
||||
fn value_ptr(&self) -> *const T {
|
||||
let (_, offset) = self.layout();
|
||||
let data_ptr = unsafe { self.ptr.cast::<u8>().as_ptr().add(offset) };
|
||||
ptr::from_raw_parts(data_ptr.cast(), self.meta())
|
||||
}
|
||||
|
||||
fn value_mut_ptr(&mut self) -> *mut T {
|
||||
let (_, offset) = self.layout();
|
||||
// FIXME: can this line be shared with the same in `value_ptr()`
|
||||
// without upsetting Stacked Borrows?
|
||||
let data_ptr = unsafe { self.ptr.cast::<u8>().as_ptr().add(offset) };
|
||||
from_raw_parts_mut(data_ptr.cast(), self.meta())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for ThinBox<T>
|
||||
where
|
||||
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.value_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for ThinBox<T>
|
||||
where
|
||||
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.value_mut_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Drop for ThinBox<T>
|
||||
where
|
||||
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
let (layout, _) = self.layout();
|
||||
unsafe {
|
||||
drop_in_place::<T>(&mut **self);
|
||||
dealloc(self.ptr.cast().as_ptr(), layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +275,7 @@
|
||||
#![feature(int_error_matching)]
|
||||
#![feature(integer_atomics)]
|
||||
#![feature(into_future)]
|
||||
#![cfg_attr(not(bootstrap), feature(intra_doc_pointers))]
|
||||
#![feature(lang_items)]
|
||||
#![feature(link_args)]
|
||||
#![feature(linkage)]
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![deny(broken_intra_doc_links)]
|
||||
#![feature(intra_doc_pointers)]
|
||||
|
||||
pub use std::*;
|
||||
|
Loading…
x
Reference in New Issue
Block a user