about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSimon Sapin <simon.sapin@exyr.org>2020-10-19 15:38:11 +0200
committerSimon Sapin <simon.sapin@exyr.org>2021-02-15 14:27:12 +0100
commit696b239f72350ce2a647ede1a330039d0e0ecfa9 (patch)
tree8092d539513e303265fcd90065a8291244c93863
parent9503ea19edbf01b9435e80e17d60ce1b88390116 (diff)
downloadrust-696b239f72350ce2a647ede1a330039d0e0ecfa9.tar.gz
rust-696b239f72350ce2a647ede1a330039d0e0ecfa9.zip
Add `ptr::Pointee` trait (for all types) and `ptr::metadata` function
RFC: https://github.com/rust-lang/rfcs/pull/2580
-rw-r--r--compiler/rustc_hir/src/lang_items.rs4
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs15
-rw-r--r--compiler/rustc_middle/src/traits/select.rs3
-rw-r--r--compiler/rustc_middle/src/traits/structural_impls.rs3
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs45
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs68
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs18
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs3
-rw-r--r--compiler/rustc_typeck/src/coherence/mod.rs15
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/ptr/metadata.rs77
-rw-r--r--library/core/src/ptr/mod.rs6
-rw-r--r--library/core/tests/lib.rs3
-rw-r--r--library/core/tests/ptr.rs87
17 files changed, 349 insertions, 10 deletions
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 26ce30cb511..27a3210c5ce 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 163b400973b..2084baa9c20 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 e056240f941..ab085175762 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 5a17d38c734..4f978e63630 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 04cc4db0bcf..3992e570cdc 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 ef062da3f6e..d35d179a8dd 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 6908480f431..2819b60c144 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 f09ce8d64ed..752f6a8debc 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 ed3e117fcfa..272930f6bb9 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 87c8099dc3a..49591df9775 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 cf2c6efb471..be43c3a920e 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 4294450333c..f6b77fb442f 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 fd4a76c1eb5..b835f78ca83 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 00000000000..416b1b860ce
--- /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<T: std::ptr::Thin>() {
+///     assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::<usize>())
+/// }
+/// ```
+#[unstable(feature = "ptr_metadata", issue = /* FIXME */ "none")]
+// NOTE: don’t stabilize this before trait aliases are stable in the language?
+pub trait Thin = Pointee<Metadata = ()>;
+
+/// Extract the metadata component of a pointer.
+#[inline]
+pub fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
+    // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
+    // and PtrComponents<T> have the same memory layouts. Only std can make this
+    // guarantee.
+    unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
+}
+
+#[repr(C)]
+union PtrRepr<T: ?Sized> {
+    const_ptr: *const T,
+    components: PtrComponents<T>,
+}
+
+#[repr(C)]
+struct PtrComponents<T: ?Sized> {
+    data_address: usize,
+    metadata: <T as Pointee>::Metadata,
+}
+
+// Manual impl needed to avoid `T: Copy` bound.
+impl<T: ?Sized> Copy for PtrComponents<T> {}
+
+// Manual impl needed to avoid `T: Clone` bound.
+impl<T: ?Sized> Clone for PtrComponents<T> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+/// The metadata for a `dyn 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 f71233e7c32..e0c1cd7aa39 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 40dc6473b7d..f5035c9f16f 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 57c2fb06c16..03d2be725ef 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: ?Sized>(A, B);
+    extern "C" {
+        type Extern;
+    }
+    let () = metadata(&());
+    let () = metadata(&Unit);
+    let () = metadata(&4_u32);
+    let () = metadata(&String::new());
+    let () = metadata(&Some(4_u32));
+    let () = metadata(&ptr_metadata);
+    let () = metadata(&|| {});
+    let () = metadata(&[4, 7]);
+    let () = metadata(&(4, String::new()));
+    let () = metadata(&Pair(4, String::new()));
+    let () = metadata(0 as *const Extern);
+    let () = metadata(0 as *const <&u32 as std::ops::Deref>::Target);
+
+    assert_eq!(metadata("foo"), 3_usize);
+    assert_eq!(metadata(&[4, 7][..]), 2_usize);
+
+    let dst_tuple: &(bool, [u8]) = &(true, [0x66, 0x6F, 0x6F]);
+    let dst_struct: &Pair<bool, [u8]> = &Pair(true, [0x66, 0x6F, 0x6F]);
+    assert_eq!(metadata(dst_tuple), 3_usize);
+    assert_eq!(metadata(dst_struct), 3_usize);
+    unsafe {
+        let dst_tuple: &(bool, str) = std::mem::transmute(dst_tuple);
+        let dst_struct: &Pair<bool, str> = std::mem::transmute(dst_struct);
+        assert_eq!(&dst_tuple.1, "foo");
+        assert_eq!(&dst_struct.1, "foo");
+        assert_eq!(metadata(dst_tuple), 3_usize);
+        assert_eq!(metadata(dst_struct), 3_usize);
+    }
+
+    let vtable_1: DynMetadata = 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<bool, dyn Display>);
+    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<T: ?Sized>() -> usize {
+        // The `Metadata` associated type has an `Ord` bound, so this is valid:
+        <<T as Pointee>::Metadata as PartialEq>::eq as usize
+    }
+    // "Synthetic" trait impls generated by the compiler like those of `Pointee`
+    // are not checked for bounds of associated type.
+    // So with a buggy libcore we could have both:
+    // * `<dyn Display as Pointee>::Metadata == DynMetadata`
+    // * `DynMetadata: !PartialEq`
+    // … and cause an ICE here:
+    metadata_eq_method_address::<dyn Display>();
+
+    // For this reason, let’s check here that bounds are satisfied:
+
+    static_assert_expected_bounds_for_metadata::<()>();
+    static_assert_expected_bounds_for_metadata::<usize>();
+    static_assert_expected_bounds_for_metadata::<DynMetadata>();
+    fn static_assert_associated_type<T: ?Sized>() {
+        static_assert_expected_bounds_for_metadata::<<T as Pointee>::Metadata>()
+    }
+
+    fn static_assert_expected_bounds_for_metadata<Meta>()
+    where
+        // Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs`
+        Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin,
+    {
+    }
+}