From 696b239f72350ce2a647ede1a330039d0e0ecfa9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 19 Oct 2020 15:38:11 +0200 Subject: [PATCH 01/12] Add `ptr::Pointee` trait (for all types) and `ptr::metadata` function RFC: https://github.com/rust-lang/rfcs/pull/2580 --- compiler/rustc_hir/src/lang_items.rs | 4 + compiler/rustc_middle/src/traits/mod.rs | 15 +++- compiler/rustc_middle/src/traits/select.rs | 3 + .../src/traits/structural_impls.rs | 3 + compiler/rustc_middle/src/ty/sty.rs | 45 ++++++++++ compiler/rustc_span/src/symbol.rs | 3 + .../src/traits/project.rs | 68 ++++++++++++++- .../src/traits/select/candidate_assembly.rs | 3 + .../src/traits/select/confirmation.rs | 5 +- .../src/traits/select/mod.rs | 18 +++- compiler/rustc_ty_utils/src/instance.rs | 3 +- compiler/rustc_typeck/src/coherence/mod.rs | 15 +++- library/core/src/lib.rs | 1 + library/core/src/ptr/metadata.rs | 77 ++++++++++++++++ library/core/src/ptr/mod.rs | 6 ++ library/core/tests/lib.rs | 3 + library/core/tests/ptr.rs | 87 +++++++++++++++++++ 17 files changed, 349 insertions(+), 10 deletions(-) create mode 100644 library/core/src/ptr/metadata.rs 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..3992e570cdc58 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2133,6 +2133,51 @@ 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(..) => tcx.type_of(tcx.lang_items().dyn_metadata().unwrap()), + + 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/lib.rs b/library/core/src/lib.rs index fd4a76c1eb548..b835f78ca8339 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -133,6 +133,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/metadata.rs b/library/core/src/ptr/metadata.rs new file mode 100644 index 0000000000000..416b1b860ce88 --- /dev/null +++ b/library/core/src/ptr/metadata.rs @@ -0,0 +1,77 @@ +#![unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + +use crate::fmt; +use crate::hash::Hash; +use crate::ptr::NonNull; + +/// FIXME docs +#[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” +/// +/// ```rust +/// #![feature(ptr_metadata)] +/// +/// fn this_never_panics() { +/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::()) +/// } +/// ``` +#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +// NOTE: don’t stabilize this before trait aliases are stable in the language? +pub trait Thin = Pointee; + +/// Extract the metadata component of a pointer. +#[inline] +pub 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 } +} + +#[repr(C)] +union PtrRepr { + const_ptr: *const T, + components: PtrComponents, +} + +#[repr(C)] +struct PtrComponents { + data_address: usize, + 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 SomeTrait` trait object type. +#[lang = "dyn_metadata"] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct DynMetadata { + #[allow(unused)] + vtable_ptr: NonNull<()>, +} + +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.write_str("DynMetadata { … }") + } +} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f71233e7c32b9..e0c1cd7aa394e 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -82,6 +82,12 @@ pub use crate::intrinsics::copy; #[doc(inline)] pub use crate::intrinsics::write_bytes; +#[cfg(not(bootstrap))] +mod metadata; +#[cfg(not(bootstrap))] +#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +pub use metadata::{metadata, DynMetadata, Pointee, Thin}; + mod non_null; #[stable(feature = "nonnull", since = "1.25.0")] pub use non_null::NonNull; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 40dc6473b7d40..f5035c9f16f95 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)] +#![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)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 57c2fb06c16dd..03d2be725ef51 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,5 +1,6 @@ use core::cell::RefCell; use core::ptr::*; +use std::fmt::Display; #[test] fn test_const_from_raw_parts() { @@ -413,3 +414,89 @@ 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_u32 as &dyn Display); + let vtable_2: DynMetadata = metadata(&(true, 7_u32) as &(bool, dyn Display)); + let vtable_3: DynMetadata = metadata(&Pair(true, 7_u32) as &Pair); + let vtable_4: DynMetadata = metadata(&4_u16 as &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); + // Same erased type and same trait: same vtable pointer + assert_eq!(address_1, address_2); + assert_eq!(address_1, address_3); + // Different erased type: different vtable pointer + assert_ne!(address_1, address_4); + } +} + +#[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: + + static_assert_expected_bounds_for_metadata::<()>(); + static_assert_expected_bounds_for_metadata::(); + static_assert_expected_bounds_for_metadata::(); + fn static_assert_associated_type() { + 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, + { + } +} From b1e15fa8a29558b1233278f5d6da9ee37e30dcee Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 29 Dec 2020 18:31:22 +0100 Subject: [PATCH 02/12] Parameterize `DynMetadata` over its `dyn SomeTrait` type --- compiler/rustc_middle/src/ty/sty.rs | 5 ++- library/core/src/ptr/metadata.rs | 55 +++++++++++++++++++++++++---- library/core/tests/ptr.rs | 35 ++++++++++-------- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 3992e570cdc58..b534b5ac4d47d 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2164,7 +2164,10 @@ impl<'tcx> TyS<'tcx> { | ty::Tuple(..) => tcx.types.unit, ty::Str | ty::Slice(_) => tcx.types.usize, - ty::Dynamic(..) => tcx.type_of(tcx.lang_items().dyn_metadata().unwrap()), + ty::Dynamic(..) => { + let dyn_metadata = tcx.lang_items().dyn_metadata().unwrap(); + tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()]) + }, ty::Projection(_) | ty::Param(_) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 416b1b860ce88..948d7f0b03944 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -1,7 +1,7 @@ #![unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] use crate::fmt; -use crate::hash::Hash; +use crate::hash::{Hash, Hasher}; use crate::ptr::NonNull; /// FIXME docs @@ -61,17 +61,60 @@ impl Clone for PtrComponents { /// The metadata for a `dyn SomeTrait` trait object type. #[lang = "dyn_metadata"] -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct DynMetadata { +pub struct DynMetadata { #[allow(unused)] vtable_ptr: NonNull<()>, + phantom: crate::marker::PhantomData, } -unsafe impl Send for DynMetadata {} -unsafe impl Sync for DynMetadata {} +unsafe impl Send for DynMetadata {} +unsafe impl Sync for DynMetadata {} -impl fmt::Debug for DynMetadata { +impl fmt::Debug for DynMetadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("DynMetadata { … }") } } + +// 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 { + self.vtable_ptr == other.vtable_ptr + } +} + +impl Ord for DynMetadata { + #[inline] + fn cmp(&self, other: &Self) -> crate::cmp::Ordering { + self.vtable_ptr.cmp(&other.vtable_ptr) + } +} + +impl PartialOrd for DynMetadata { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.vtable_ptr.cmp(&other.vtable_ptr)) + } +} + +impl Hash for DynMetadata { + #[inline] + fn hash(&self, hasher: &mut H) { + self.vtable_ptr.hash(hasher) + } +} diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 03d2be725ef51..ff3db740dfdb7 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::ptr::*; -use std::fmt::Display; +use std::fmt::{Debug, Display}; #[test] fn test_const_from_raw_parts() { @@ -452,20 +452,25 @@ fn ptr_metadata() { assert_eq!(metadata(dst_struct), 3_usize); } - let vtable_1: DynMetadata = metadata(&4_u32 as &dyn Display); - let vtable_2: DynMetadata = metadata(&(true, 7_u32) as &(bool, dyn Display)); - let vtable_3: DynMetadata = metadata(&Pair(true, 7_u32) as &Pair); - let vtable_4: DynMetadata = metadata(&4_u16 as &dyn Display); + 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); - // Same erased type and same trait: same vtable pointer - assert_eq!(address_1, address_2); - assert_eq!(address_1, address_3); - // Different erased type: different vtable pointer - assert_ne!(address_1, address_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); } } @@ -486,11 +491,11 @@ fn ptr_metadata_bounds() { // For this reason, let’s check here that bounds are satisfied: - static_assert_expected_bounds_for_metadata::<()>(); - static_assert_expected_bounds_for_metadata::(); - static_assert_expected_bounds_for_metadata::(); - fn static_assert_associated_type() { - static_assert_expected_bounds_for_metadata::<::Metadata>() + 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() From 9ab83b93383f7b7f87186a2a8a289540c4e27564 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 13 Jan 2021 11:09:15 +0100 Subject: [PATCH 03/12] Add `size_of`, `align_of`, and `layout` methods to `DynMetadata` --- library/core/src/ptr/metadata.rs | 46 ++++++++++++++++++++++++++------ library/core/tests/ptr.rs | 20 ++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 948d7f0b03944..5b5a403e71983 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,7 +2,6 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::ptr::NonNull; /// FIXME docs #[lang = "pointee_trait"] @@ -62,17 +61,48 @@ impl Clone for PtrComponents { /// The metadata for a `dyn SomeTrait` trait object type. #[lang = "dyn_metadata"] pub struct DynMetadata { - #[allow(unused)] - vtable_ptr: NonNull<()>, + 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.write_str("DynMetadata { … }") + f.debug_tuple("DynMetadata").field(&(self.vtable_ptr as *const VTable)).finish() } } @@ -94,27 +124,27 @@ impl Eq for DynMetadata {} impl PartialEq for DynMetadata { #[inline] fn eq(&self, other: &Self) -> bool { - self.vtable_ptr == other.vtable_ptr + 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.cmp(&other.vtable_ptr) + (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.vtable_ptr.cmp(&other.vtable_ptr)) + Some(self.cmp(other)) } } impl Hash for DynMetadata { #[inline] fn hash(&self, hasher: &mut H) { - self.vtable_ptr.hash(hasher) + crate::ptr::hash::(self.vtable_ptr, hasher) } } diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index ff3db740dfdb7..26fafd0180614 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -505,3 +505,23 @@ fn ptr_metadata_bounds() { { } } + +#[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")); +} From 937d580a2522186a1d5e1b5cd107f9ce8213b7ec Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 16:19:29 +0100 Subject: [PATCH 04/12] Add `ptr::from_raw_parts`, `ptr::from_raw_parts_mut`, and `NonNull::from_raw_parts` The use of module-level functions instead of associated functions on `<*const T>` or `<*mut T>` follows the precedent of `ptr::slice_from_raw_parts` and `ptr::slice_from_raw_parts_mut`. --- library/core/src/lib.rs | 1 + library/core/src/ptr/metadata.rs | 44 +++++++++++++++++++++++++++----- library/core/src/ptr/mod.rs | 2 +- library/core/src/ptr/non_null.rs | 18 +++++++++++++ library/core/tests/ptr.rs | 29 ++++++++++++++++++++- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b835f78ca8339..0e925893bd5aa 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -123,6 +123,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)] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 5b5a403e71983..136e986b376db 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -36,16 +36,48 @@ pub fn metadata(ptr: *const T) -> ::Metadata { unsafe { PtrRepr { const_ptr: ptr }.components.metadata } } +/// Forms a raw pointer from a data address and metadata. +#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[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 = /* FIXME */ "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[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)] -union PtrRepr { - const_ptr: *const T, - components: PtrComponents, +pub(crate) union PtrRepr { + pub(crate) const_ptr: *const T, + pub(crate) mut_ptr: *mut T, + pub(crate) components: PtrComponents, } #[repr(C)] -struct PtrComponents { - data_address: usize, - metadata: ::Metadata, +pub(crate) struct PtrComponents { + pub(crate) data_address: *const (), + pub(crate) metadata: ::Metadata, } // Manual impl needed to avoid `T: Copy` bound. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index e0c1cd7aa394e..967495f7f2ddc 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -86,7 +86,7 @@ pub use crate::intrinsics::write_bytes; mod metadata; #[cfg(not(bootstrap))] #[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] -pub use metadata::{metadata, DynMetadata, Pointee, Thin}; +pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; mod non_null; #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index e45fefc7ed765..ef7a7b974a614 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -175,6 +175,24 @@ 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. + #[cfg(not(bootstrap))] + #[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[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)) + } + } + /// 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/tests/ptr.rs b/library/core/tests/ptr.rs index 26fafd0180614..d594af991bf64 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::ptr::*; +use core::ptr::{self, *}; use std::fmt::{Debug, Display}; #[test] @@ -525,3 +525,30 @@ fn dyn_metadata() { 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); +} From c0e3a1b0968da04723ff326dc7def1d706c62377 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 16:56:53 +0100 Subject: [PATCH 05/12] Add `to_raw_parts` methods to `*const`, `*mut`, and `NonNull` These are not named `into_` because they do not consume their receiver since raw pointers are `Copy`. --- library/core/src/ptr/const_ptr.rs | 11 +++++++++++ library/core/src/ptr/metadata.rs | 3 ++- library/core/src/ptr/mut_ptr.rs | 11 +++++++++++ library/core/src/ptr/non_null.rs | 11 +++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 28de28c70e4b4..bd89ec27ae223 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 = /* FIXME */ "none")] + #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[inline] + pub const fn to_raw_parts(self) -> (*const (), ::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. diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 136e986b376db..0661ccc020014 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -28,8 +28,9 @@ pub trait Pointee { pub trait Thin = Pointee; /// Extract the metadata component of a pointer. +#[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] #[inline] -pub fn metadata(ptr: *const T) -> ::Metadata { +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. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 99744fc711216..731b97a06cbc6 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 = /* FIXME */ "none")] + #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[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. diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index ef7a7b974a614..00cb1e1b27145 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -193,6 +193,17 @@ impl NonNull { } } + /// 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 = /* FIXME */ "none")] + #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[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")] From 787f4de6ab3150ea56e8a7cc872b60d55b0db27f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 17:18:13 +0100 Subject: [PATCH 06/12] Use new pointer metadata API inside libcore instead of manual transmutes --- library/core/src/hash/mod.rs | 70 +++++++++++++++++++------------ library/core/src/ptr/const_ptr.rs | 11 +++-- library/core/src/ptr/mod.rs | 30 +++++++++---- library/core/src/ptr/mut_ptr.rs | 11 +++-- library/core/src/slice/mod.rs | 20 +++++++-- 5 files changed, 100 insertions(+), 42 deletions(-) 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/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index bd89ec27ae223..a635e59e89bea 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -916,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/mod.rs b/library/core/src/ptr/mod.rs index 967495f7f2ddc..05fd090125ce2 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -85,6 +85,8 @@ 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 = /* FIXME */ "none")] pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; @@ -226,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], @@ -233,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 { @@ -246,6 +251,7 @@ impl Clone for FatPtr { } } +#[cfg(bootstrap)] // Manual impl needed to avoid `T: Copy` bound. impl Copy for FatPtr {} @@ -273,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 @@ -308,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 731b97a06cbc6..f7da49290ae48 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1173,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/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. From 3ea7f1504c43dbc5e139c393b41427323e1c5f45 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 19:45:32 +0100 Subject: [PATCH 07/12] More doc-comments for pointer metadata APIs --- library/core/src/ptr/metadata.rs | 90 ++++++++++++++++++++++++++++++-- library/core/src/ptr/non_null.rs | 2 + 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 0661ccc020014..f89e891cb8657 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -3,7 +3,52 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; -/// FIXME docs +/// 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`. @@ -14,7 +59,11 @@ pub trait Pointee { type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } -/// Pointers to types implementing this trait alias are “thin” +/// Pointers to types implementing this trait alias are “thin”. +/// +/// This includes statically-`Sized` types and `extern` types. +/// +/// # Example /// /// ```rust /// #![feature(ptr_metadata)] @@ -28,6 +77,17 @@ pub trait Pointee { 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 = /* FIXME */ "none")] #[inline] pub const fn metadata(ptr: *const T) -> ::Metadata { @@ -37,7 +97,13 @@ pub const fn metadata(ptr: *const T) -> ::Metadata { unsafe { PtrRepr { const_ptr: ptr }.components.metadata } } -/// Forms a raw pointer from a data address and 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 = /* FIXME */ "none")] #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] #[inline] @@ -91,7 +157,23 @@ impl Clone for PtrComponents { } } -/// The metadata for a `dyn SomeTrait` trait object type. +/// 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, diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 00cb1e1b27145..3de5b097f5e34 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -179,6 +179,8 @@ impl NonNull { /// `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 = /* FIXME */ "none")] #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] From 642486c2b2e79fe06700e5f107fa1d68fd125c2c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 20:45:34 +0100 Subject: [PATCH 08/12] Fix libcore unit tests in stage 0 --- library/core/tests/lib.rs | 2 +- library/core/tests/ptr.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index f5035c9f16f95..b82cfe862904e 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -68,7 +68,7 @@ #![feature(option_result_unwrap_unchecked)] #![feature(option_unwrap_none)] #![feature(peekable_peek_mut)] -#![feature(ptr_metadata)] +#![cfg_attr(not(bootstrap), feature(ptr_metadata))] #![feature(once_cell)] #![feature(unsafe_block_in_unsafe_fn)] #![feature(unsized_tuple_coercion)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index d594af991bf64..1cdcb8c97670d 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,5 +1,8 @@ use core::cell::RefCell; -use core::ptr::{self, *}; +#[cfg(not(bootstrap))] +use core::ptr; +use core::ptr::*; +#[cfg(not(bootstrap))] use std::fmt::{Debug, Display}; #[test] From 21ceebf296ce0a818d2c56b331772f4a7f5b0a41 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 20:56:29 +0100 Subject: [PATCH 09/12] Fix intra-doc link to raw pointer method CC https://github.com/rust-lang/rust/pull/80181 --- library/core/src/lib.rs | 1 + library/core/src/ptr/metadata.rs | 2 +- library/std/src/lib.rs | 1 + src/test/rustdoc/intra-doc/libstd-re-export.rs | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0e925893bd5aa..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)] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index f89e891cb8657..f4fb37bbdb7ce 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -48,7 +48,7 @@ use crate::hash::{Hash, Hasher}; /// 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 +/// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. 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::*; From 5ade3fe32c8a742504aaddcbe0d6e498f8eae11d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 18 Jan 2021 22:24:33 +0100 Subject: [PATCH 10/12] Add a ThinBox library as a libcore test for pointer metadata APIs --- library/core/tests/lib.rs | 1 + library/core/tests/ptr.rs | 112 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index b82cfe862904e..2d4f13167fd60 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -79,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 1cdcb8c97670d..224a58e3ccdb7 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -555,3 +555,115 @@ fn from_raw_parts() { 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); + } + } + } +} From cf000f0408f3d9c4e62d2c71aae979ab0677ca0e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 29 Jan 2021 14:38:48 +0100 Subject: [PATCH 11/12] Pointer metadata: add tracking issue number --- library/core/src/ptr/const_ptr.rs | 4 ++-- library/core/src/ptr/metadata.rs | 14 +++++++------- library/core/src/ptr/mod.rs | 2 +- library/core/src/ptr/mut_ptr.rs | 4 ++-- library/core/src/ptr/non_null.rs | 8 ++++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a635e59e89bea..3f065e08ddfa7 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -52,8 +52,8 @@ impl *const T { /// /// The pointer can be later reconstructed with [`from_raw_parts`]. #[cfg(not(bootstrap))] - #[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] - #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[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(), super::metadata(self)) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index f4fb37bbdb7ce..7c7dce0ce74e2 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -1,4 +1,4 @@ -#![unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#![unstable(feature = "ptr_metadata", issue = "81513")] use crate::fmt; use crate::hash::{Hash, Hasher}; @@ -72,7 +72,7 @@ pub trait Pointee { /// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::()) /// } /// ``` -#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[unstable(feature = "ptr_metadata", issue = "81513")] // NOTE: don’t stabilize this before trait aliases are stable in the language? pub trait Thin = Pointee; @@ -88,7 +88,7 @@ pub trait Thin = Pointee; /// /// assert_eq!(std::ptr::metadata("foo"), 3_usize); /// ``` -#[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[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 @@ -104,8 +104,8 @@ pub const fn metadata(ptr: *const T) -> ::Metadata { /// 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 = /* FIXME */ "none")] -#[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[unstable(feature = "ptr_metadata", issue = "81513")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn from_raw_parts( data_address: *const (), @@ -121,8 +121,8 @@ pub const fn from_raw_parts( /// 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 = /* FIXME */ "none")] -#[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[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 (), diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 05fd090125ce2..9c53430ce3556 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -87,7 +87,7 @@ mod metadata; #[cfg(not(bootstrap))] pub(crate) use metadata::PtrRepr; #[cfg(not(bootstrap))] -#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] +#[unstable(feature = "ptr_metadata", issue = "81513")] pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; mod non_null; diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index f7da49290ae48..6651c3dd4e86b 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -51,8 +51,8 @@ impl *mut T { /// /// The pointer can be later reconstructed with [`from_raw_parts_mut`]. #[cfg(not(bootstrap))] - #[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] - #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[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)) diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 3de5b097f5e34..709c247f29614 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -182,8 +182,8 @@ impl NonNull { /// /// [`std::ptr::from_raw_parts`]: crate::ptr::from_raw_parts #[cfg(not(bootstrap))] - #[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] - #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[unstable(feature = "ptr_metadata", issue = "81513")] + #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn from_raw_parts( data_address: NonNull<()>, @@ -199,8 +199,8 @@ impl NonNull { /// /// The pointer can be later reconstructed with [`NonNull::from_raw_parts`]. #[cfg(not(bootstrap))] - #[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] - #[rustc_const_unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")] + #[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())) From cac71bf8098f41205567d0442f99ae972effbee1 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 15 Feb 2021 14:25:22 +0100 Subject: [PATCH 12/12] Use local path for already-imported function This module has `use super::*;` at the top. Co-authored-by: Oli Scherer --- library/core/src/ptr/const_ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 3f065e08ddfa7..ddff0ff67dea2 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -56,7 +56,7 @@ impl *const T { #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn to_raw_parts(self) -> (*const (), ::Metadata) { - (self.cast(), super::metadata(self)) + (self.cast(), metadata(self)) } /// Returns `None` if the pointer is null, or else returns a shared reference to