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() { +/// 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 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); + 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, + { + } +}