diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 7e2e85ead5469..43f98c630cefe 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -37,6 +37,7 @@ use rustc_span::symbol::sym; use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_target::abi::{Align, VariantIdx}; +use std::assert_matches::assert_matches; use std::collections::BTreeSet; use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; @@ -197,21 +198,21 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( src_ty: Ty<'tcx>, dst_ty: Ty<'tcx>, old_info: Option, -) -> (Bx::Value, Bx::Value) { +) -> OperandValue { debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty); + let src_layout = bx.cx().layout_of(src_ty); + let dst_layout = bx.cx().layout_of(dst_ty); match (src_ty.kind(), dst_ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); - (bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info)) + OperandValue::Pair(bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info)) } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if bx.cx().is_backend_scalar_pair(dst_layout) => { assert_eq!(def_a, def_b); - let src_layout = bx.cx().layout_of(src_ty); - let dst_layout = bx.cx().layout_of(dst_ty); if src_ty == dst_ty { - return (src, old_info.unwrap()); + return OperandValue::Pair(src, old_info.unwrap()); } let mut result = None; for i in 0..src_layout.fields.count() { @@ -226,16 +227,29 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let dst_f = dst_layout.field(bx.cx(), i); assert_ne!(src_f.ty, dst_f.ty); - assert_eq!(result, None); + assert_matches!(result, None); result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info)); } - let (lldata, llextra) = result.unwrap(); + let OperandValue::Pair(lldata, llextra) = result.unwrap() + else { bug!() }; let lldata_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true); let llextra_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true); // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - (bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty)) + OperandValue::Pair(bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty)) + } + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { + assert_eq!(def_a, def_b); + let typed_metadata = bx.tcx().require_lang_item(LangItem::TypedMetadata, None); + assert_eq!(def_a.did(), typed_metadata); + if src_ty == dst_ty { + return OperandValue::Immediate(src); + } + let a = substs_a.type_at(0); + let b = substs_b.type_at(0); + assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); + OperandValue::Immediate(unsized_info(bx, a, b, old_info)) } - _ => bug!("unsize_ptr: called on bad types"), + _ => bug!("unsize_ptr: called on bad types {:?} -> {:?}", src_ty, dst_ty), } } @@ -250,12 +264,12 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let dst_ty = dst.layout.ty; match (src_ty.kind(), dst_ty.kind()) { (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { - let (base, info) = match bx.load_operand(src).val { + let val = match bx.load_operand(src).val { OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)), OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None), OperandValue::Ref(..) => bug!(), }; - OperandValue::Pair(base, info).store(bx, dst); + val.store(bx, dst); } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 4be3ae11e4e5b..edfc27ae1d075 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,4 +1,5 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(try_blocks)] #![feature(let_else)] diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index a5806d64d437c..72a9276fe4dd3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -225,7 +225,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operand.val } mir::CastKind::Pointer(PointerCast::Unsize) => { - assert!(bx.cx().is_backend_scalar_pair(cast)); let (lldata, llextra) = match operand.val { OperandValue::Pair(lldata, llextra) => { // unsize from a fat pointer -- this is a @@ -240,9 +239,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand); } }; - let (lldata, llextra) = - base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra); - OperandValue::Pair(lldata, llextra) + base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra) } mir::CastKind::Pointer(PointerCast::MutToConstPointer) | mir::CastKind::Misc diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index d09ab5fa3e85a..e28df0f4c2fdd 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; +use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar, ScalarMaybeUninit}; use rustc_middle::mir::CastKind; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; @@ -305,22 +305,37 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { source_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx> { + // We *could* forward `data` without even checking that it is initialized, but for now, + // let's only allow casting properly initialized pointers. + let (data, old_meta) = match *self.read_immediate(src)? { + // If the input ptr is thin, use `Uninit` for the old metadata. + // `unsize_just_metadata` knows how to handle that. + Immediate::Scalar(data) => (data.check_init()?, ScalarMaybeUninit::Uninit), + Immediate::ScalarPair(data, meta) => (data.check_init()?, meta), + }; + + let new_meta = self.unsize_just_metadata(old_meta, source_ty, cast_ty)?; + self.write_immediate(Immediate::ScalarPair(data.into(), new_meta.into()), dest) + } + + fn unsize_just_metadata( + &mut self, + src_meta: ScalarMaybeUninit, + // The pointee types + source_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Scalar> { // A -> A conversion let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); - match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { + Ok(match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { - let ptr = self.read_immediate(src)?.to_scalar()?; - // u64 cast is from usize to u64, which is always good - let val = - Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self); - self.write_immediate(val, dest) + Scalar::from_machine_usize(length.eval_usize(*self.tcx, self.param_env), self) } (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - let val = self.read_immediate(src)?; if data_a.principal_def_id() == data_b.principal_def_id() { - return self.write_immediate(*val, dest); + return src_meta.check_init(); } // trait upcasting coercion let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( @@ -330,27 +345,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(entry_idx) = vptr_entry_idx { let entry_idx = u64::try_from(entry_idx).unwrap(); - let (old_data, old_vptr) = val.to_scalar_pair()?; + let old_vptr = src_meta.check_init()?; let old_vptr = self.scalar_to_ptr(old_vptr)?; let new_vptr = self .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?; - self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) + Scalar::from_maybe_pointer(new_vptr, self) } else { - self.write_immediate(*val, dest) + src_meta.check_init()? } } (_, &ty::Dynamic(ref data, _)) => { // Initial cast from sized to dyn trait let vtable = self.get_vtable(src_pointee_ty, data.principal())?; - let ptr = self.read_immediate(src)?.to_scalar()?; - let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); - self.write_immediate(val, dest) + Scalar::from_maybe_pointer(vtable, &*self.tcx) } _ => { - span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty) + span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", source_ty, cast_ty) } - } + }) } fn unsize_into( @@ -360,16 +373,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { dest: &PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); - match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { - (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) - | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { + let typed_metadata = self.tcx.lang_items().typed_metadata(); + match (src.layout.ty.kind(), cast_ty.ty.kind()) { + (ty::Ref(_, s, _), ty::Ref(_, c, _) | ty::RawPtr(TypeAndMut { ty: c, .. })) + | (ty::RawPtr(TypeAndMut { ty: s, .. }), ty::RawPtr(TypeAndMut { ty: c, .. })) => { self.unsize_into_ptr(src, dest, *s, *c) } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + (ty::Adt(def_a, _), ty::Adt(def_b, _)) if def_a.is_box() || def_b.is_box() => { assert_eq!(def_a, def_b); - + if !def_a.is_box() || !def_b.is_box() { + span_bug!( + self.cur_span(), + "invalid unsizing between {:?} -> {:?}", + src.layout.ty, + cast_ty.ty + ); + } + self.unsize_into_ptr(src, dest, src.layout.ty.boxed_ty(), cast_ty.ty.boxed_ty()) + } + (ty::Adt(def_a, substs_a), ty::Adt(def_b, substs_b)) + if def_a == def_b && Some(def_a.did()) == typed_metadata => + { + // unsizing of TypedMetadata container + // Example: `TypedMetadata` -> `TypedMetadata` + let a_pointee = substs_a.type_at(0); + let b_pointee = substs_b.type_at(0); + let src_field = self.operand_field(src, 0)?; + let src = self.read_immediate(&src_field)?.to_scalar_or_uninit(); + let dst_field = self.place_field(dest, 0)?; + let new_meta = self.unsize_just_metadata(src, a_pointee, b_pointee)?; + self.write_scalar(new_meta, &dst_field) + } + (ty::Adt(def_a, _), ty::Adt(def_b, _)) if def_a == def_b => { // unsizing of generic struct with pointer fields - // Example: `Arc` -> `Arc` + // Example: `Arc` -> `Arc` // here we need to increase the size of every &T thin ptr field to a fat ptr for i in 0..src.layout.fields.count() { let cast_ty_field = cast_ty.field(self, i); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b0bfac8e1f5ec..f4da36cd9ad70 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -182,6 +182,7 @@ 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; + TypedMetadata, sym::typed_metadata, typed_metadata, Target::Struct, GenericRequirement::Exact(1); 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 0bea2a10da896..0f5a5f673ba9a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -215,7 +215,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, impl_polarity => { table_direct } impl_defaultness => { table_direct } constness => { table_direct } - coerce_unsized_info => { table } + coerce_unsized_kind => { table } mir_const_qualif => { table } rendered_const => { table } asyncness => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 75286b8906871..28c981318461b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1482,9 +1482,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 a58c0e68ee38c..540a55086696a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -368,7 +368,7 @@ define_tables! { is_intrinsic: 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 a1065eef8509d..98510c144fc1d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -835,8 +835,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) } cache_on_disk_if { key.is_local() } separate_provide_extern } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index d9332f6896af6..f40bdc0965ca5 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -177,20 +177,15 @@ pub enum AutoBorrow<'tcx> { /// Information for `CoerceUnsized` impls, storing information we /// have computed about the coercion. /// -/// This struct can be obtained via the `coerce_impl_info` query. +/// This enum can be obtained via the `coerce_unsized_kind` query. /// 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. +#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Debug, HashStable)] +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. + TypedMetadata, } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 54ba9e84fdb7b..24df55b02b1fc 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -59,7 +59,7 @@ trivially_parameterized_over_tcx! { ty::ReprOptions, ty::TraitDef, ty::Visibility, - ty::adjustment::CoerceUnsizedInfo, + ty::adjustment::CoerceUnsizedKind, ty::fast_reject::SimplifiedTypeGen, rustc_ast::Attribute, rustc_ast::MacArgs, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b9936e35b772e..3b8e373b369c1 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -191,7 +191,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}; @@ -1106,21 +1106,39 @@ 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); + match crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) { + CoerceUnsizedKind::Struct(coerce_index) => { + let source_fields = &source_adt_def.non_enum_variant().fields; + let target_fields = &target_adt_def.non_enum_variant().fields; - let source_fields = &source_adt_def.non_enum_variant().fields; - let target_fields = &target_adt_def.non_enum_variant().fields; - - assert!( - coerce_index < source_fields.len() && source_fields.len() == target_fields.len() - ); + assert!( + coerce_index < source_fields.len() + && source_fields.len() == target_fields.len() + ); - find_vtable_types_for_unsizing( - tcx, - source_fields[coerce_index].ty(tcx, source_substs), - target_fields[coerce_index].ty(tcx, target_substs), - ) + find_vtable_types_for_unsizing( + tcx, + source_fields[coerce_index].ty(tcx, source_substs), + target_fields[coerce_index].ty(tcx, target_substs), + ) + } + CoerceUnsizedKind::TypedMetadata => { + let typed_metadata = tcx.lang_items().typed_metadata(); + if !typed_metadata.is_some() || source_adt_def.did() != typed_metadata.unwrap() + { + bug!( + "find_vtable_types_for_unsizing: tried to treat {:?} as `TypedMetadata`", + source_ty + ); + } + ptr_vtable(source_substs.type_at(0), target_substs.type_at(0)) + } + CoerceUnsizedKind::Ptr => bug!( + "find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?} with Adt type but Ptr kind", + source_ty, + target_ty + ), + } } _ => bug!( "find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index ef4560b5ec48e..67252bb8b994e 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -11,7 +11,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}; @@ -24,7 +24,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 { @@ -36,7 +36,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 4e28d2b6001ed..012ff403eb97f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1466,6 +1466,7 @@ symbols! { type_length_limit, type_macros, type_name, + typed_metadata, u128, u16, u32, diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index c647c2a4c1baa..64005c546b244 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::TyCtxtInferExt; -use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::adjustment::CoerceUnsizedKind; use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeFoldable}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; @@ -195,7 +195,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) { @@ -363,8 +363,8 @@ 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(); @@ -376,6 +376,8 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); }); + let typed_metadata = tcx.lang_items().typed_metadata(); + let source = tcx.type_of(impl_did); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); assert_eq!(trait_ref.def_id, coerce_unsized_trait); @@ -385,7 +387,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); @@ -405,7 +407,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)) => { @@ -424,6 +426,17 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) } + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) + if Some(def_a.did()) == typed_metadata && Some(def_b.did()) == typed_metadata => + { + ( + substs_a.type_at(0), + substs_b.type_at(0), + unsize_trait, + CoerceUnsizedKind::TypedMetadata, + ) + } + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) if def_a.is_struct() && def_b.is_struct() => { @@ -566,8 +579,7 @@ 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)) + (a, b, coerce_unsized_trait, CoerceUnsizedKind::Struct(i)) } _ => { @@ -608,6 +620,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn let outlives_env = OutlivesEnvironment::new(param_env); infcx.resolve_regions_and_report_errors(impl_did.to_def_id(), &outlives_env); - CoerceUnsizedInfo { custom_kind: kind } + kind }) } diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 447ec87f30264..ff3b171686756 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_impl; @@ -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_impl, ..*providers }; diff --git a/library/core/src/ops/unsize.rs b/library/core/src/ops/unsize.rs index a920b9165c18e..843d1014b470f 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::TypedMetadata; /// Trait that indicates that this is a pointer or a wrapper for one, /// where unsizing can be performed on the pointee. @@ -68,6 +70,10 @@ impl, U: ?Sized> CoerceUnsized<*const U> for *mut T {} #[unstable(feature = "coerce_unsized", issue = "27732")] impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} +#[cfg(not(bootstrap))] +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl, U: ?Sized> CoerceUnsized> for TypedMetadata {} + /// `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..2c43a122196a5 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -59,6 +59,30 @@ pub trait Pointee { type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } +/// Just the metadata associated with some pointee. +/// +/// This reattaches the pointee type to the metadata to allow pointee unsizing +/// to happen. +/// +/// # Example +/// +/// ```rust +/// #![feature(ptr_metadata)] +/// +/// use std::ptr::TypedMetadata; +/// +/// // The metadata for any sized type is () +/// let sized_metadata: TypedMetadata<[u8; 5]> = TypedMetadata::<[u8; 5]>(()); +/// +/// // But we can coerce the pointee from an array to a slice +/// let unsized_metadata: TypedMetadata<[u8]> = sized_metadata; +/// +/// // Now the metadata is the slice length +/// assert_eq!(unsized_metadata.0, 5); +/// ``` +#[cfg_attr(not(bootstrap), lang = "typed_metadata")] +pub struct TypedMetadata(pub ::Metadata); + /// Pointers to types implementing this trait alias are “thin”. /// /// This includes statically-`Sized` types and `extern` types. @@ -221,6 +245,15 @@ impl fmt::Debug for DynMetadata { } } +impl fmt::Debug for TypedMetadata +where + ::Metadata: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("TypedMetadata").field(&self.0 as &dyn fmt::Debug).finish() + } +} + // Manual impls needed to avoid `Dyn: $Trait` bounds. impl Unpin for DynMetadata {} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8fb0bfbe2e31b..8b7be44e2fe0c 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -392,7 +392,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, Thin, TypedMetadata, +}; mod non_null; #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/src/test/ui/coercion/coerce-just-metadata.rs b/src/test/ui/coercion/coerce-just-metadata.rs new file mode 100644 index 0000000000000..a902bfa2a2d28 --- /dev/null +++ b/src/test/ui/coercion/coerce-just-metadata.rs @@ -0,0 +1,26 @@ +// build-pass +#![feature(ptr_metadata)] + +use std::ptr::TypedMetadata; + +struct Struct; +trait Trait {} + +impl Trait for Struct {} + +fn main() { + // array -> slice + let sized: TypedMetadata<[u8; 5]> = TypedMetadata(()); + let _: TypedMetadata<[u8]> = sized; + + // sized -> dyn + let sized: TypedMetadata = TypedMetadata(()); + let dyn_trait: TypedMetadata = sized; + + // dyn -> dyn + let _: TypedMetadata = dyn_trait; + + // identity + let sized: TypedMetadata = TypedMetadata(()); + let _ = sized as TypedMetadata; +} diff --git a/src/test/ui/coercion/just-metadata-bad-coercions.rs b/src/test/ui/coercion/just-metadata-bad-coercions.rs new file mode 100644 index 0000000000000..dbfd7f6ee42a9 --- /dev/null +++ b/src/test/ui/coercion/just-metadata-bad-coercions.rs @@ -0,0 +1,18 @@ +#![feature(ptr_metadata)] + +use std::ptr::TypedMetadata; + +struct Struct; +trait Trait {} + +fn main() { + let struct_metadata: TypedMetadata = TypedMetadata(()); + + let _: TypedMetadata = struct_metadata; //~ ERROR `Struct: Trait` is not satisfied + let _: TypedMetadata<[Struct]> = struct_metadata; //~ ERROR mismatched types + + let array_metadata: TypedMetadata<[u8; 4]> = TypedMetadata(()); + + let _: TypedMetadata<[u32]> = array_metadata; //~ ERROR mismatched types + let _: TypedMetadata = array_metadata; //~ ERROR mismatched types +} diff --git a/src/test/ui/coercion/just-metadata-bad-coercions.stderr b/src/test/ui/coercion/just-metadata-bad-coercions.stderr new file mode 100644 index 0000000000000..f48c41281589a --- /dev/null +++ b/src/test/ui/coercion/just-metadata-bad-coercions.stderr @@ -0,0 +1,45 @@ +error[E0308]: mismatched types + --> $DIR/just-metadata-bad-coercions.rs:12:38 + | +LL | let _: TypedMetadata<[Struct]> = struct_metadata; + | ----------------------- ^^^^^^^^^^^^^^^ expected slice, found struct `Struct` + | | + | expected due to this + | + = note: expected struct `TypedMetadata<[Struct]>` + found struct `TypedMetadata` + +error[E0277]: the trait bound `Struct: Trait` is not satisfied + --> $DIR/just-metadata-bad-coercions.rs:11:39 + | +LL | let _: TypedMetadata = struct_metadata; + | ^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `Struct` + | + = note: required for the cast from `Struct` to the object type `dyn Trait` + +error[E0308]: mismatched types + --> $DIR/just-metadata-bad-coercions.rs:16:35 + | +LL | let _: TypedMetadata<[u32]> = array_metadata; + | -------------------- ^^^^^^^^^^^^^^ expected slice `[u32]`, found array `[u8; 4]` + | | + | expected due to this + | + = note: expected struct `TypedMetadata<[u32]>` + found struct `TypedMetadata<[u8; 4]>` + +error[E0308]: mismatched types + --> $DIR/just-metadata-bad-coercions.rs:17:36 + | +LL | let _: TypedMetadata = array_metadata; + | --------------------- ^^^^^^^^^^^^^^ expected struct `Struct`, found array `[u8; 4]` + | | + | expected due to this + | + = note: expected struct `TypedMetadata` + found struct `TypedMetadata<[u8; 4]>` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/coercion/silly-box.rs b/src/test/ui/coercion/silly-box.rs new file mode 100644 index 0000000000000..9c459a90cff3d --- /dev/null +++ b/src/test/ui/coercion/silly-box.rs @@ -0,0 +1,27 @@ +// build-pass +#![feature(coerce_unsized, ptr_metadata, unsize)] + +use std::marker::Unsize; +use std::ops::CoerceUnsized; +use std::ptr::{NonNull, TypedMetadata}; + +struct SillyBox { + data: NonNull<()>, + meta: TypedMetadata, +} + +impl, U: ?Sized> CoerceUnsized> for SillyBox {} + +fn do_unsize_slice(it: SillyBox<[u8; 5]>) -> SillyBox<[u8]> { + it +} + +struct S; +trait Trait {} +impl Trait for S {} + +fn do_unsize_trait(it: SillyBox) -> SillyBox { + it +} + +fn main() {}