diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 26ce30cb51177..27a3210c5ce9d 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -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; diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 163b400973b7c..2084baa9c200b 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -476,6 +476,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>), @@ -494,7 +497,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, } } @@ -509,7 +513,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[..], } } @@ -554,6 +559,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, @@ -632,6 +640,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, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index e056240f94150..ab085175762ab 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -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 diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index 5a17d38c73460..4f978e6363044 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -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, } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 04cc4db0bcf64..b534b5ac4d47d 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -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 diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ef062da3f6e89..d35d179a8dd13 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -476,6 +476,7 @@ symbols! { dropck_eyepatch, dropck_parametricity, dylib, + dyn_metadata, dyn_trait, edition_macro_pats, eh_catch_typeinfo, @@ -710,6 +711,7 @@ symbols! { memory, message, meta, + metadata_type, min_align_of, min_align_of_val, min_const_fn, @@ -832,6 +834,7 @@ symbols! { plugin, plugin_registrar, plugins, + pointee_trait, pointer, pointer_trait, pointer_trait_fmt, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 6908480f431e6..2819b60c144c6 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -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>, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f09ce8d64ed5e..752f6a8debc9e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -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. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index ed3e117fcfabb..272930f6bb9ca 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -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)) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 87c8099dc3a51..49591df97758e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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 { diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cf2c6efb4711f..be43c3a920ed2 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -274,7 +274,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, }) } diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 4294450333ca3..f6b77fb442f21 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -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!( diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index f53ba98143842..cd47f97496a4a 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -673,19 +673,28 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for *const T { fn hash(&self, state: &mut H) { - if mem::size_of::() == mem::size_of::() { - // 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::() == mem::size_of::() { + // 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 Hash for *mut T { fn hash(&self, state: &mut H) { - if mem::size_of::() == mem::size_of::() { - // 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::() == mem::size_of::() { + // 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); + } } } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index fd4a76c1eb548..7c0e5ab8926ef 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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)] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 28de28c70e4b4..ddff0ff67dea2 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -48,6 +48,17 @@ impl *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 (), ::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 *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` 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` 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. diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs new file mode 100644 index 0000000000000..7c7dce0ce74e2 --- /dev/null +++ b/library/core/src/ptr/metadata.rs @@ -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`][DynMetadata] +/// (e.g. `DynMetadata`) +/// +/// 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() { +/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::()) +/// } +/// ``` +#[unstable(feature = "ptr_metadata", issue = "81513")] +// NOTE: don’t stabilize this before trait aliases are stable in the language? +pub trait Thin = Pointee; + +/// 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(ptr: *const T) -> ::Metadata { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents 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( + data_address: *const (), + metadata: ::Metadata, +) -> *const T { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents 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( + data_address: *mut (), + metadata: ::Metadata, +) -> *mut T { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents 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 { + pub(crate) const_ptr: *const T, + pub(crate) mut_ptr: *mut T, + pub(crate) components: PtrComponents, +} + +#[repr(C)] +pub(crate) struct PtrComponents { + pub(crate) data_address: *const (), + pub(crate) metadata: ::Metadata, +} + +// Manual impl needed to avoid `T: Copy` bound. +impl Copy for PtrComponents {} + +// Manual impl needed to avoid `T: Clone` bound. +impl Clone for PtrComponents { + 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`) but not to obtain a meaningful value of that struct. +#[lang = "dyn_metadata"] +pub struct DynMetadata { + vtable_ptr: &'static VTable, + phantom: crate::marker::PhantomData, +} + +/// 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 DynMetadata { + /// 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 Send for DynMetadata {} +unsafe impl Sync for DynMetadata {} + +impl fmt::Debug for DynMetadata { + 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 Unpin for DynMetadata {} + +impl Copy for DynMetadata {} + +impl Clone for DynMetadata { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Eq for DynMetadata {} + +impl PartialEq for DynMetadata { + #[inline] + fn eq(&self, other: &Self) -> bool { + crate::ptr::eq::(self.vtable_ptr, other.vtable_ptr) + } +} + +impl Ord for DynMetadata { + #[inline] + fn cmp(&self, other: &Self) -> crate::cmp::Ordering { + (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable)) + } +} + +impl PartialOrd for DynMetadata { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Hash for DynMetadata { + #[inline] + fn hash(&self, hasher: &mut H) { + crate::ptr::hash::(self.vtable_ptr, hasher) + } +} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f71233e7c32b9..9c53430ce3556 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -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() -> *mut T { 0 as *mut T } +#[cfg(bootstrap)] #[repr(C)] pub(crate) union Repr { pub(crate) rust: *const [T], @@ -227,12 +236,14 @@ pub(crate) union Repr { pub(crate) raw: FatPtr, } +#[cfg(bootstrap)] #[repr(C)] pub(crate) struct FatPtr { data: *const T, pub(crate) len: usize, } +#[cfg(bootstrap)] // Manual impl needed to avoid `T: Clone` bound. impl Clone for FatPtr { fn clone(&self) -> Self { @@ -240,6 +251,7 @@ impl Clone for FatPtr { } } +#[cfg(bootstrap)] // Manual impl needed to avoid `T: Copy` bound. impl Copy for FatPtr {} @@ -267,10 +279,15 @@ impl Copy for FatPtr {} #[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(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(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(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 diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 99744fc711216..6651c3dd4e86b 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -47,6 +47,17 @@ impl *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 (), ::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 *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` 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` 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. diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index e45fefc7ed765..709c247f29614 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -175,6 +175,37 @@ impl NonNull { } } + /// 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: ::Metadata, + ) -> NonNull { + // 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<()>, ::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")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index a6929e462e7f0..1c1b9e0b27e25 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -94,9 +94,23 @@ impl [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` 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` 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 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. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 40dc6473b7d40..2d4f13167fd60 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)] @@ -67,8 +68,10 @@ #![feature(option_result_unwrap_unchecked)] #![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)] @@ -76,6 +79,7 @@ #![feature(slice_group_by)] #![feature(trusted_random_access)] #![deny(unsafe_op_in_unsafe_fn)] +#![cfg_attr(not(bootstrap), feature(unsize))] extern crate test; diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 57c2fb06c16dd..224a58e3ccdb7 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -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); + 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 = &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 = 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 = metadata(&4_u16 as &dyn Debug); + let vtable_2: DynMetadata = metadata(&4_u16 as &dyn Display); + let vtable_3: DynMetadata = metadata(&4_u32 as &dyn Display); + let vtable_4: DynMetadata = metadata(&(true, 7_u32) as &(bool, dyn Display)); + let vtable_5: DynMetadata = + metadata(&Pair(true, 7_u32) as &Pair); + 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() -> usize { + // The `Metadata` associated type has an `Ord` bound, so this is valid: + <::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: + // * `::Metadata == DynMetadata` + // * `DynMetadata: !PartialEq` + // … and cause an ICE here: + metadata_eq_method_address::(); + + // 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::; + let _ = static_assert_expected_bounds_for_metadata::>; + fn _static_assert_associated_type() { + let _ = static_assert_expected_bounds_for_metadata::<::Metadata>; + } + + fn static_assert_expected_bounds_for_metadata() + 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::()); + assert_eq!(meta.align_of(), 32); + assert_eq!(meta.align_of(), std::mem::align_of::()); + assert_eq!(meta.layout(), std::alloc::Layout::new::()); + + 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::::new(4); + assert_eq!(foo.to_string(), "4"); + drop(foo); + let bar = ThinBox::::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::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 + where + T: ?Sized + Pointee>, + { + ptr: NonNull>, + phantom: PhantomData, + } + + impl ThinBox + where + T: ?Sized + Pointee>, + { + pub fn new>(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` is generalized to any `T: ?Sized`, + // handle ZSTs with a dangling pointer without going through `alloc()`, + // like `Box` does. + unsafe { + let ptr = NonNull::new(alloc(layout)) + .unwrap_or_else(|| handle_alloc_error(layout)) + .cast::>(); + ptr.as_ptr().write(meta); + ptr.cast::().as_ptr().add(offset).cast::().write(value); + Self { ptr, phantom: PhantomData } + } + } + + fn meta(&self) -> DynMetadata { + 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::().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::().as_ptr().add(offset) }; + from_raw_parts_mut(data_ptr.cast(), self.meta()) + } + } + + impl std::ops::Deref for ThinBox + where + T: ?Sized + Pointee>, + { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.value_ptr() } + } + } + + impl std::ops::DerefMut for ThinBox + where + T: ?Sized + Pointee>, + { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.value_mut_ptr() } + } + } + + impl std::ops::Drop for ThinBox + where + T: ?Sized + Pointee>, + { + fn drop(&mut self) { + let (layout, _) = self.layout(); + unsafe { + drop_in_place::(&mut **self); + dealloc(self.ptr.cast().as_ptr(), layout); + } + } + } +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 961cff661e3ba..70ec79a1fe989 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -276,6 +276,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)] diff --git a/src/test/rustdoc/intra-doc/libstd-re-export.rs b/src/test/rustdoc/intra-doc/libstd-re-export.rs index d0af3aec66097..fc0ff90438932 100644 --- a/src/test/rustdoc/intra-doc/libstd-re-export.rs +++ b/src/test/rustdoc/intra-doc/libstd-re-export.rs @@ -1,3 +1,4 @@ #![deny(broken_intra_doc_links)] +#![feature(intra_doc_pointers)] pub use std::*;