diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b8df163214435..ea3d53639972b 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -182,6 +182,8 @@ language_item_table! { PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; + SizedMetadata, sym::sized_metadata, sized_metadata, Target::Struct, GenericRequirement::None; + MetadataCoerceImpl, sym::metadata_coerce_impl,metadata_coerce_impl, Target::Impl, GenericRequirement::None; Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 63bf929fb8639..d20546a865f45 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -147,7 +147,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, impl_polarity => { table } impl_defaultness => { table } impl_constness => { table } - coerce_unsized_info => { table } + coerce_unsized_kind => { table } mir_const_qualif => { table } rendered_const => { table } asyncness => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index e967750aebb52..7d85d39453542 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1463,9 +1463,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if Some(trait_ref.def_id) == self.tcx.lang_items().coerce_unsized_trait() { - let coerce_unsized_info = - self.tcx.at(item.span).coerce_unsized_info(def_id); - record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); + let coerce_unsized_kind = + self.tcx.at(item.span).coerce_unsized_kind(def_id); + record!(self.tables.coerce_unsized_kind[def_id] <- coerce_unsized_kind); } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 43ccfc64e0563..17cdfa5f3b5b2 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -338,7 +338,7 @@ define_tables! { impl_constness: Table, impl_defaultness: Table, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? - coerce_unsized_info: Table, + coerce_unsized_kind: Table, mir_const_qualif: Table, rendered_const: Table, asyncness: Table, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f38ade1076eac..d4234c0959dce 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -792,8 +792,8 @@ rustc_queries! { } /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo { - desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + query coerce_unsized_kind(key: DefId) -> ty::adjustment::CoerceUnsizedKind { + desc { |tcx| "computing CoerceUnsized kind for `{}`", tcx.def_path_str(key) } separate_provide_extern } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 2676b7ab521d8..7994c7c28f1c8 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -181,16 +181,11 @@ pub enum AutoBorrow<'tcx> { /// Demanding this struct also has the side-effect of reporting errors /// for inappropriate impls. #[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)] -pub struct CoerceUnsizedInfo { - /// If this is a "custom coerce" impl, then what kind of custom - /// coercion is it? This applies to impls of `CoerceUnsized` for - /// structs, primarily, where we store a bit of info about which - /// fields need to be coerced. - pub custom_kind: Option, -} - -#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)] -pub enum CustomCoerceUnsized { - /// Records the index of the field being coerced. +pub enum CoerceUnsizedKind { + /// Coercion of a raw pointer or ref. + Ptr, + /// Coercion of a struct. Records the index of the field being coerced. Struct(usize), + /// Coercion of the pointee metadata directly. + JustMetadata, } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 5a13216846d54..42b4ae6e0f1ea 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2292,14 +2292,18 @@ impl<'tcx> Ty<'tcx> { | ty::Closure(..) | ty::Never | ty::Error(_) - // Extern types have metadata = (). - | 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, false), + | ty::Tuple(..) => { + let sized_metadata = tcx.lang_items().sized_metadata().unwrap(); + (tcx.type_of(sized_metadata).subst(tcx, &[tail.into()]), false) + }, + + // Extern types have metadata = (). + ty::Foreign(..) => (tcx.types.unit, false), ty::Str | ty::Slice(_) => (tcx.types.usize, false), ty::Dynamic(..) => { @@ -2309,7 +2313,10 @@ impl<'tcx> Ty<'tcx> { // type parameters only have unit metadata if they're sized, so return true // to make sure we double check this during confirmation - ty::Param(_) | ty::Projection(_) | ty::Opaque(..) => (tcx.types.unit, true), + ty::Param(_) | ty::Projection(_) | ty::Opaque(..) => { + let sized_metadata = tcx.lang_items().sized_metadata().unwrap(); + (tcx.type_of(sized_metadata).subst(tcx, &[tail.into()]), true) + }, ty::Infer(ty::TyVar(_)) | ty::Bound(..) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 2a659a97db5f9..d1a7183ece04a 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -190,7 +190,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Local, Location}; -use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; +use rustc_middle::ty::adjustment::{CoerceUnsizedKind, PointerCast}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable, VtblEntry}; @@ -1046,8 +1046,11 @@ fn find_vtable_types_for_unsizing<'tcx>( (&ty::Adt(source_adt_def, source_substs), &ty::Adt(target_adt_def, target_substs)) => { assert_eq!(source_adt_def, target_adt_def); - let CustomCoerceUnsized::Struct(coerce_index) = - crate::custom_coerce_unsize_info(tcx, source_ty, target_ty); + let CoerceUnsizedKind::Struct(coerce_index) = + crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) + else { + bug!("ty::Adt should have a struct `CoerceUnsized` kind"); + }; let source_fields = &source_adt_def.non_enum_variant().fields; let target_fields = &target_adt_def.non_enum_variant().fields; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index c9accbcd86ed4..87f31da90de0c 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -13,7 +13,7 @@ extern crate rustc_middle; use rustc_hir::lang_items::LangItem; use rustc_middle::traits; -use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::adjustment::CoerceUnsizedKind; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -26,7 +26,7 @@ fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxt<'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, -) -> CustomCoerceUnsized { +) -> CoerceUnsizedKind { let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None); let trait_ref = ty::Binder::dummy(ty::TraitRef { @@ -38,7 +38,7 @@ fn custom_coerce_unsize_info<'tcx>( Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { impl_def_id, .. - })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), + })) => tcx.coerce_unsized_kind(impl_def_id), impl_source => { bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index dc4d10f699c75..9700046e5798d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -887,6 +887,7 @@ symbols! { memtag, message, meta, + metadata_coerce_impl, metadata_type, min_align_of, min_align_of_val, @@ -1311,6 +1312,7 @@ symbols! { size_of, size_of_val, sized, + sized_metadata, skip, slice, slice_len_fn, diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index 3135e9996ab8b..99313f16868c1 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -10,7 +10,7 @@ use rustc_hir::ItemKind; use rustc_infer::infer; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionckMode, TyCtxtInferExt}; -use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::adjustment::CoerceUnsizedKind; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; @@ -148,7 +148,7 @@ fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: Loc // errors; other parts of the code may demand it for the info of // course. let span = tcx.def_span(impl_did); - tcx.at(span).coerce_unsized_info(impl_did); + tcx.at(span).coerce_unsized_kind(impl_did); } fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { @@ -320,14 +320,15 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: }) } -pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { - debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); +pub fn coerce_unsized_kind<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedKind { + debug!("compute_coerce_unsized_kind(impl_did={:?})", impl_did); // this provider should only get invoked for local def-ids let impl_did = impl_did.expect_local(); let span = tcx.def_span(impl_did); let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)); + let metadata_coerce_impl = tcx.lang_items().metadata_coerce_impl(); let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| { tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); @@ -342,7 +343,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - let err_info = CoerceUnsizedInfo { custom_kind: None }; + let err_info = CoerceUnsizedKind::Ptr; debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); @@ -362,7 +363,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn ) .emit(); } - (mt_a.ty, mt_b.ty, unsize_trait, None) + (mt_a.ty, mt_b.ty, unsize_trait, CoerceUnsizedKind::Ptr) }; let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { @@ -523,8 +524,60 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn } let (i, a, b) = diff_fields[0]; - let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); - (a, b, coerce_unsized_trait, Some(kind)) + let kind = CoerceUnsizedKind::Struct(i); + (a, b, coerce_unsized_trait, kind) + } + + // #[lang = "metadata_coerce_impl"] + // impl, U: ?Sized> CoerceUnsized<::Metadata> + // for ::Metadata + (source, target) if Some(impl_did.to_def_id()) == metadata_coerce_impl => { + let kind = CoerceUnsizedKind::JustMetadata; + + let pointee_trait = tcx.require_lang_item(LangItem::PointeeTrait, Some(span)); + let pointee_metadata = tcx.require_lang_item(LangItem::Metadata, Some(span)); + let sized_metadata = tcx.lang_items().sized_metadata(); + let dyn_metadata = tcx.lang_items().dyn_metadata(); + + // FIXME(CAD97): should this form check be elsewhere? on the lang item wf check? + let (ty::Adt(def_a, substs_a), ty::Projection(proj_b)) = (source, target) + else { + tcx.sess.struct_span_err(span, r#"#[lang = "metadata_coerce_impl] malformed"#).emit(); + return err_info; + }; + + let a = if Some(def_a.did()) == sized_metadata || Some(def_a.did()) == dyn_metadata { + // FIXME(CAD97): actual error reporting + if !substs_a.len() == 1 { + bug!("ill-formed metadata type"); + } + Some(substs_a.type_at(0)) + } else { + tcx.sess.struct_span_err( + span, + r#"#`[lang = "metadata_coerce_impl]` must be used on \ + a Self type of `SizedMetadata` or `DynMetadata`"# + ).emit(); + None + }; + + let b = if proj_b.item_def_id == pointee_metadata && proj_b.trait_def_id(tcx) == pointee_trait { + Some(proj_b.self_ty()) + } else { + tcx.sess.struct_span_err( + span, + r#"#`[lang = "metadata_coerce_impl]` must \ + coerce to `::Metadata`"# + ).emit(); + None + }; + + let (Some(a), Some(b)) = (a, b) + else { + return err_info; + }; + + (a, b, unsize_trait, kind) } _ => { @@ -569,6 +622,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn RegionckMode::default(), ); - CoerceUnsizedInfo { custom_kind: kind } + kind }) } diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 3f1b4828d1af3..4fbf41e1ed803 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -143,7 +143,7 @@ fn enforce_empty_impls_for_marker_traits( } pub fn provide(providers: &mut Providers) { - use self::builtin::coerce_unsized_info; + use self::builtin::coerce_unsized_kind; use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls}; use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; use self::orphan::orphan_check_crate; @@ -154,7 +154,7 @@ pub fn provide(providers: &mut Providers) { crate_incoherent_impls, inherent_impls, crate_inherent_impls_overlap_check, - coerce_unsized_info, + coerce_unsized_kind, orphan_check_crate, ..*providers }; diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 8b376e26dee97..f57742e932b09 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -150,12 +150,17 @@ fn enforce_impl_params_are_constrained( ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "type", - ¶m_ty.to_string(), - ); + // UNLESS it's #[lang = "metadata_coerce_impl"], used to impl + // CoerceUnsized<::Metadata> for SizedMetadata + let metadata_coerce_impl = tcx.lang_items().metadata_coerce_impl(); + if Some(impl_def_id.to_def_id()) != metadata_coerce_impl { + report_unused_parameter( + tcx, + tcx.def_span(param.def_id), + "type", + ¶m_ty.to_string(), + ); + } } } ty::GenericParamDefKind::Lifetime => { diff --git a/library/core/src/ops/unsize.rs b/library/core/src/ops/unsize.rs index a920b9165c18e..9037df70d8dfe 100644 --- a/library/core/src/ops/unsize.rs +++ b/library/core/src/ops/unsize.rs @@ -1,4 +1,6 @@ use crate::marker::Unsize; +#[cfg(not(bootstrap))] +use crate::ptr::{Pointee, SizedMetadata}; /// Trait that indicates that this is a pointer or a wrapper for one, /// where unsizing can be performed on the pointee. @@ -68,6 +70,21 @@ impl, U: ?Sized> CoerceUnsized<*const U> for *mut T {} #[unstable(feature = "coerce_unsized", issue = "27732")] impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} +// SizedMetadata -> ::Metadata +#[unstable(feature = "coerce_unsized", issue = "27732")] +#[cfg(not(bootstrap))] +#[lang = "metadata_coerce_impl"] +impl, U: ?Sized> CoerceUnsized<::Metadata> + for SizedMetadata +{ +} + +// // DynMetadata -> ::Metadata +// #[unstable(feature = "coerce_unsized", issue = "27732")] +// #[cfg(not(bootstrap))] +// #[lang = "metadata_coerce_impl"] +// impl, U: ?Sized> CoerceUnsized<::Metadata> for DynMetadata {} + /// `DispatchFromDyn` is used in the implementation of object safety checks (specifically allowing /// arbitrary self types), to guarantee that a method's receiver type can be dispatched on. /// diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 287ae69acd198..15b01104a0115 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -73,9 +73,28 @@ pub trait Pointee { /// } /// ``` #[unstable(feature = "ptr_metadata", issue = "81513")] -// NOTE: don’t stabilize this before trait aliases are stable in the language? +#[cfg(bootstrap)] pub trait Thin = Pointee; +/// Pointers to types implementing this trait alias are “thin”. +/// +/// This includes statically-`Sized` types and `extern` types. +/// +/// # Example +/// +/// ```rust +/// #![feature(ptr_metadata)] +/// +/// fn this_never_panics() { +/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::()) +/// } +/// ``` +#[unstable(feature = "ptr_metadata", issue = "81513")] +// NOTE: don’t stabilize this before trait aliases are stable in the language? +// NOTE: excludes extern types, as they don't use SizedMetadata... change this? +#[cfg(not(bootstrap))] +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 @@ -157,6 +176,67 @@ impl Clone for PtrComponents { } } +/// The metadata for a `Sized` type. +/// +/// This is just another flavor of `PhantomData` which exists in order to +/// facilitate the unsizing of pointee metadata. +#[cfg_attr(not(bootstrap), lang = "sized_metadata")] +pub struct SizedMetadata { + phantom: crate::marker::PhantomData, +} + +unsafe impl Send for SizedMetadata {} +unsafe impl Sync for SizedMetadata {} + +impl fmt::Debug for SizedMetadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SizedMetadata").finish() + } +} + +// Manual impls needed to avoid `T: $Trait` bounds. + +impl Unpin for SizedMetadata {} + +impl Copy for SizedMetadata {} + +impl Clone for SizedMetadata { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Eq for SizedMetadata {} + +impl PartialEq for SizedMetadata { + #[inline] + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl Ord for SizedMetadata { + #[inline] + fn cmp(&self, _: &Self) -> crate::cmp::Ordering { + crate::cmp::Ordering::Equal + } +} + +impl PartialOrd for SizedMetadata { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Hash for SizedMetadata { + #[inline] + fn hash(&self, _: &mut H) { + // no-op + } +} + /// The metadata for a `Dyn = dyn SomeTrait` trait object type. /// /// It is a pointer to a vtable (virtual call table) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index ba8b0670147ae..36e67ffb8d318 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -391,7 +391,9 @@ pub use crate::intrinsics::write_bytes; mod metadata; pub(crate) use metadata::PtrRepr; #[unstable(feature = "ptr_metadata", issue = "81513")] -pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; +pub use metadata::{ + from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, SizedMetadata, Thin, +}; mod non_null; #[stable(feature = "nonnull", since = "1.25.0")]