diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index b9d379c6117c9..d594731b4dfce 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -25,39 +25,73 @@ pub(crate) fn unsized_info<'tcx>( .bcx .ins() .iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64), - (&ty::Dynamic(..), &ty::Dynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - old_info.expect("unsized_info: missing old info for trait upcast") + (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { + let old_info = + old_info.expect("unsized_info: missing old info for trait upcasting coercion"); + if data_a.principal_def_id() == data_b.principal_def_id() { + return old_info; + } + // trait upcasting coercion + + // if both of the two `principal`s are `None`, this function would have returned early above. + // and if one of the two `principal`s is `None`, typechecking would have rejected this case. + let principal_a = data_a + .principal() + .expect("unsized_info: missing principal trait for trait upcasting coercion"); + let principal_b = data_b + .principal() + .expect("unsized_info: missing principal trait for trait upcasting coercion"); + + let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( + principal_a.with_self_ty(fx.tcx, source), + principal_b.with_self_ty(fx.tcx, source), + )); + + if let Some(entry_idx) = vptr_entry_idx { + let entry_idx = u32::try_from(entry_idx).unwrap(); + let entry_offset = entry_idx * fx.pointer_type.bytes(); + let vptr_ptr = Pointer::new(old_info).offset_i64(fx, entry_offset.into()).load( + fx, + fx.pointer_type, + crate::vtable::vtable_memflags(), + ); + vptr_ptr + } else { + old_info + } } (_, &ty::Dynamic(ref data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()), _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), } } -/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. -fn unsize_thin_ptr<'tcx>( +/// Coerce `src` to `dst_ty`. +fn unsize_ptr<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, src: Value, src_layout: TyAndLayout<'tcx>, dst_layout: TyAndLayout<'tcx>, + old_info: Option, ) -> (Value, Value) { match (&src_layout.ty.kind(), &dst_layout.ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { assert!(!fx.layout_of(a).is_unsized()); - (src, unsized_info(fx, a, b, None)) + (src, unsized_info(fx, a, b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty()); assert!(!fx.layout_of(a).is_unsized()); - (src, unsized_info(fx, a, b, None)) + (src, unsized_info(fx, a, b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); + if src_layout == dst_layout { + return (src, old_info.unwrap()); + } + let mut result = None; for i in 0..src_layout.fields.count() { let src_f = src_layout.field(fx, i); @@ -71,11 +105,11 @@ fn unsize_thin_ptr<'tcx>( let dst_f = dst_layout.field(fx, i); assert_ne!(src_f.ty, dst_f.ty); assert_eq!(result, None); - result = Some(unsize_thin_ptr(fx, src, src_f, dst_f)); + result = Some(unsize_ptr(fx, src, src_f, dst_f, old_info)); } result.unwrap() } - _ => bug!("unsize_thin_ptr: called on bad types"), + _ => bug!("unsize_ptr: called on bad types"), } } @@ -91,12 +125,11 @@ pub(crate) fn coerce_unsized_into<'tcx>( let mut coerce_ptr = || { let (base, info) = if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e., &'a fmt::Debug+Send => &'a fmt::Debug - src.load_scalar_pair(fx) + let (old_base, old_info) = src.load_scalar_pair(fx); + unsize_ptr(fx, old_base, src.layout(), dst.layout(), Some(old_info)) } else { let base = src.load_scalar(fx); - unsize_thin_ptr(fx, base, src.layout(), dst.layout()) + unsize_ptr(fx, base, src.layout(), dst.layout(), None) }; dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); }; diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 4a5f9f133a2bb..1b31587430887 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -5,7 +5,7 @@ use crate::constant::data_id_for_alloc_id; use crate::prelude::*; -fn vtable_memflags() -> MemFlags { +pub(crate) fn vtable_memflags() -> MemFlags { let mut flags = MemFlags::trusted(); // A vtable access is always aligned and will never trap. flags.set_readonly(); // A vtable is always read-only. flags diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index be2bf8b199724..87b342e844391 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -23,7 +23,6 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::lang_items; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; -use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::cgu_reuse_tracker::CguReuse; @@ -32,6 +31,7 @@ use rustc_session::Session; use rustc_span::symbol::sym; use rustc_target::abi::{Align, LayoutOf, VariantIdx}; +use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; @@ -128,55 +128,92 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// /// The `old_info` argument is a bit odd. It is intended for use in an upcast, /// where the new vtable for an object will be derived from the old one. -pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>( - cx: &Cx, +pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, source: Ty<'tcx>, target: Ty<'tcx>, - old_info: Option, -) -> Cx::Value { + old_info: Option, +) -> Bx::Value { + let cx = bx.cx(); let (source, target) = - cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env()); + cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env()); match (source.kind(), target.kind()) { (&ty::Array(_, len), &ty::Slice(_)) => { cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all())) } - (&ty::Dynamic(..), &ty::Dynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - old_info.expect("unsized_info: missing old info for trait upcast") + (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { + let old_info = + old_info.expect("unsized_info: missing old info for trait upcasting coercion"); + if data_a.principal_def_id() == data_b.principal_def_id() { + return old_info; + } + + // trait upcasting coercion + + // if both of the two `principal`s are `None`, this function would have returned early above. + // and if one of the two `principal`s is `None`, typechecking would have rejected this case. + let principal_a = data_a + .principal() + .expect("unsized_info: missing principal trait for trait upcasting coercion"); + let principal_b = data_b + .principal() + .expect("unsized_info: missing principal trait for trait upcasting coercion"); + + let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot(( + principal_a.with_self_ty(cx.tcx(), source), + principal_b.with_self_ty(cx.tcx(), source), + )); + + if let Some(entry_idx) = vptr_entry_idx { + let ptr_ty = cx.type_i8p(); + let ptr_align = cx.tcx().data_layout.pointer_align.abi; + let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty)); + let gep = + bx.inbounds_gep(llvtable, &[bx.const_usize(u64::try_from(entry_idx).unwrap())]); + let new_vptr = bx.load(ptr_ty, gep, ptr_align); + bx.nonnull_metadata(new_vptr); + // Vtable loads are invariant. + bx.set_invariant_load(new_vptr); + new_vptr + } else { + old_info + } } (_, &ty::Dynamic(ref data, ..)) => { - let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)).field(cx, FAT_PTR_EXTRA); - cx.const_ptrcast( - meth::get_vtable(cx, source, data.principal()), - cx.backend_type(vtable_ptr), - ) + let vtable_ptr_ty = cx.scalar_pair_element_backend_type( + cx.layout_of(cx.tcx().mk_mut_ptr(target)), + 1, + true, + ); + cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty) } _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), } } -/// Coerces `src` to `dst_ty`. `src_ty` must be a thin pointer. -pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer. +pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, src: Bx::Value, src_ty: Ty<'tcx>, dst_ty: Ty<'tcx>, + old_info: Option, ) -> (Bx::Value, Bx::Value) { - debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); + debug!("unsize_ptr: {:?} => {:?}", src_ty, 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!(bx.cx().type_is_sized(a)); + 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.cx(), a, b, None)) + (bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { 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()); + } let mut result = None; for i in 0..src_layout.fields.count() { let src_f = src_layout.field(bx.cx(), i); @@ -190,18 +227,15 @@ pub fn unsize_thin_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); - result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); + result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info)); } let (lldata, llextra) = result.unwrap(); + 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. - // FIXME(eddyb) move these out of this `match` arm, so they're always - // applied, uniformly, no matter the source/destination types. - ( - bx.bitcast(lldata, bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true)), - bx.bitcast(llextra, bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true)), - ) + (bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty)) } - _ => bug!("unsize_thin_ptr: called on bad types"), + _ => bug!("unsize_ptr: called on bad types"), } } @@ -217,17 +251,8 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( 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 { - OperandValue::Pair(base, info) => { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e., &'a fmt::Debug+Send => &'a fmt::Debug - // So we need to pointercast the base to ensure - // the types match up. - // FIXME(eddyb) use `scalar_pair_element_backend_type` here, - // like `unsize_thin_ptr` does. - let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR); - (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info) - } - OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src_ty, dst_ty), + 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); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 530de3de9e870..7e1dfeb2457eb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -220,34 +220,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::CastKind::Pointer(PointerCast::Unsize) => { assert!(bx.cx().is_backend_scalar_pair(cast)); - match operand.val { + let (lldata, llextra) = match operand.val { OperandValue::Pair(lldata, llextra) => { // unsize from a fat pointer -- this is a - // "trait-object-to-supertrait" coercion, for - // example, `&'a fmt::Debug + Send => &'a fmt::Debug`. - - // HACK(eddyb) have to bitcast pointers - // until LLVM removes pointee types. - let lldata = bx.pointercast( - lldata, - bx.cx().scalar_pair_element_backend_type(cast, 0, true), - ); - OperandValue::Pair(lldata, llextra) + // "trait-object-to-supertrait" coercion. + (lldata, Some(llextra)) } OperandValue::Immediate(lldata) => { // "standard" unsize - let (lldata, llextra) = base::unsize_thin_ptr( - &mut bx, - lldata, - operand.layout.ty, - cast.ty, - ); - OperandValue::Pair(lldata, llextra) + (lldata, None) } OperandValue::Ref(..) => { 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) } mir::CastKind::Pointer(PointerCast::MutToConstPointer) | mir::CastKind::Misc diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 2de836c058cf1..3c16852df059a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -977,6 +977,11 @@ rustc_queries! { desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) } } + query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option { + desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable", + tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) } + } + query codegen_fulfill_obligation( key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) ) -> Result, ErrorReported> { diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index 514c1aa9646a0..cee4c2e30aa09 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -269,12 +269,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self); self.write_immediate(val, dest) } - (&ty::Dynamic(..), &ty::Dynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. + (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { let val = self.read_immediate(src)?; - self.write_immediate(*val, dest) + if data_a.principal_def_id() == data_b.principal_def_id() { + return self.write_immediate(*val, dest); + } + // trait upcasting coercion + let principal_a = data_a.principal().expect( + "unsize_into_ptr: missing principal trait for trait upcasting coercion", + ); + let principal_b = data_b.principal().expect( + "unsize_into_ptr: missing principal trait for trait upcasting coercion", + ); + + let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( + principal_a.with_self_ty(*self.tcx, src_pointee_ty), + principal_b.with_self_ty(*self.tcx, src_pointee_ty), + )); + + 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 = 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) + } else { + self.write_immediate(*val, dest) + } } (_, &ty::Dynamic(ref data, _)) => { // Initial cast from sized to dyn trait diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index aba7db7816843..d5bc2b1e2eacb 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -63,15 +63,19 @@ impl<'tcx, Tag: Provenance> Immediate { Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) } - pub fn new_dyn_trait(val: Scalar, vtable: Pointer, cx: &impl HasDataLayout) -> Self { - Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_pointer(vtable, cx)) + pub fn new_dyn_trait( + val: Scalar, + vtable: Pointer>, + cx: &impl HasDataLayout, + ) -> Self { + Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx)) } #[inline] pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit { match self { Immediate::Scalar(val) => val, - Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"), + Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), } } @@ -79,6 +83,16 @@ impl<'tcx, Tag: Provenance> Immediate { pub fn to_scalar(self) -> InterpResult<'tcx, Scalar> { self.to_scalar_or_uninit().check_init() } + + #[inline] + pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar, Scalar)> { + match self { + Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)), + Immediate::Scalar(..) => { + bug!("Got a scalar where a scalar pair was expected") + } + } + } } // ScalarPair needs a type to interpret, so we often have an immediate and a type together diff --git a/compiler/rustc_mir/src/interpret/traits.rs b/compiler/rustc_mir/src/interpret/traits.rs index 7a93fcee78e33..a6ba00ec6952e 100644 --- a/compiler/rustc_mir/src/interpret/traits.rs +++ b/compiler/rustc_mir/src/interpret/traits.rs @@ -21,7 +21,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, ty: Ty<'tcx>, poly_trait_ref: Option>, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, Pointer>> { trace!("get_vtable(trait_ref={:?})", poly_trait_ref); let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); @@ -34,7 +34,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?; - Ok(vtable_ptr) + Ok(vtable_ptr.into()) } /// Resolves the function at the specified slot in the provided @@ -121,4 +121,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Ok((Size::from_bytes(size), align)) } + + pub fn read_new_vtable_after_trait_upcasting_from_vtable( + &self, + vtable: Pointer>, + idx: u64, + ) -> InterpResult<'tcx, Pointer>> { + let pointer_size = self.pointer_size(); + + let vtable_slot = vtable.offset(pointer_size * idx, self)?; + let new_vtable = self + .memory + .get(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)? + .expect("cannot be a ZST"); + + let new_vtable = self.scalar_to_ptr(new_vtable.read_ptr_sized(Size::ZERO)?.check_init()?); + + Ok(new_vtable) + } } diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index 0ad360c7d89c3..ace7cffd16daf 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -282,6 +282,16 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx> { } } +impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + self.0.def_id().krate == LOCAL_CRATE + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.def_span(self.0.def_id()) + } +} + impl<'tcx> Key for GenericArg<'tcx> { #[inline(always)] fn query_crate_is_local(&self) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 693384602a75f..585fcf795b7f5 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -756,16 +756,17 @@ fn vtable_trait_first_method_offset<'tcx>( } /// Find slot offset for trait vptr within vtable entries of another trait -/// FIXME: This function is not yet used. Remove `#[allow(dead_code)]` when it's used in upcoming pr. -#[allow(dead_code)] -fn vtable_trait_vptr_slot_offset<'tcx>( +pub fn vtable_trait_upcasting_coercion_new_vptr_slot( tcx: TyCtxt<'tcx>, key: ( - ty::PolyTraitRef<'tcx>, // trait_to_be_found - ty::PolyTraitRef<'tcx>, // trait_owning_vtable + ty::PolyTraitRef<'tcx>, // trait owning vtable + ty::PolyTraitRef<'tcx>, // super trait ref ), ) -> Option { - let (trait_to_be_found, trait_owning_vtable) = key; + let (trait_owning_vtable, super_trait_ref) = key; + let super_trait_did = super_trait_ref.def_id(); + // FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur + // multiple times. let vtable_segment_callback = { let mut vptr_offset = 0; @@ -776,7 +777,7 @@ fn vtable_trait_vptr_slot_offset<'tcx>( } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { vptr_offset += util::count_own_vtable_entries(tcx, trait_ref); - if trait_ref == trait_to_be_found { + if trait_ref.def_id() == super_trait_did { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -810,6 +811,7 @@ pub fn provide(providers: &mut ty::query::Providers) { specializes: specialize::specializes, codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_entries, + vtable_trait_upcasting_coercion_new_vptr_slot, subst_and_check_impossible_predicates, mir_abstract_const: |tcx, def_id| { let def_id = def_id.expect_local(); diff --git a/src/doc/unstable-book/src/language-features/trait-upcasting.md b/src/doc/unstable-book/src/language-features/trait-upcasting.md new file mode 100644 index 0000000000000..3697ae38f9d81 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/trait-upcasting.md @@ -0,0 +1,27 @@ +# `trait_upcasting` + +The tracking issue for this feature is: [#65991] + +[#65991]: https://github.com/rust-lang/rust/issues/65991 + +------------------------ + +The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a +trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo` +so long as `Bar: Foo`. + +```rust,edition2018 +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo {} + +trait Bar: Foo {} + +impl Foo for i32 {} + +impl Bar for T {} + +let bar: &dyn Bar = &123; +let foo: &dyn Foo = bar; +``` diff --git a/src/test/ui/traits/trait-upcasting/basic.rs b/src/test/ui/traits/trait-upcasting/basic.rs new file mode 100644 index 0000000000000..484a222bc012d --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/basic.rs @@ -0,0 +1,87 @@ +// run-pass + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } +} + +trait Bar: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } +} + +trait Baz: Bar { + fn c(&self) -> i32 { + 30 + } +} + +impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } +} + +impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } +} + +impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } +} + +fn main() { + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let bar: &dyn Bar = baz; + let _: &dyn std::fmt::Debug = bar; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let foo: &dyn Foo = baz; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let foo: &dyn Foo = bar; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} diff --git a/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs new file mode 100644 index 0000000000000..511e41562b220 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs @@ -0,0 +1,13 @@ +trait A: B + A {} +//~^ ERROR cycle detected when computing the super predicates of `A` [E0391] + +trait B {} + +impl A for () {} + +impl B for () {} + +fn main() { + let a: Box = Box::new(()); + let _b: Box = a; +} diff --git a/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr new file mode 100644 index 0000000000000..ac005725ab485 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr @@ -0,0 +1,21 @@ +error[E0391]: cycle detected when computing the super predicates of `A` + --> $DIR/cyclic-trait-resolution.rs:1:1 + | +LL | trait A: B + A {} + | ^^^^^^^^^^^^^^ + | +note: ...which requires computing the super traits of `A`... + --> $DIR/cyclic-trait-resolution.rs:1:14 + | +LL | trait A: B + A {} + | ^ + = note: ...which again requires computing the super predicates of `A`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/cyclic-trait-resolution.rs:1:1 + | +LL | trait A: B + A {} + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/traits/trait-upcasting/diamond.rs b/src/test/ui/traits/trait-upcasting/diamond.rs new file mode 100644 index 0000000000000..e4e23c1a26e78 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/diamond.rs @@ -0,0 +1,115 @@ +// run-pass + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } +} + +trait Bar1: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } +} + +trait Bar2: Foo { + fn c(&self) -> i32 { + 30 + } + + fn v(&self) -> i32 { + 31 + } +} + +trait Baz: Bar1 + Bar2 { + fn d(&self) -> i32 { + 40 + } +} + +impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } +} + +impl Bar1 for i32 { + fn b(&self) -> i32 { + 200 + } +} + +impl Bar2 for i32 { + fn c(&self) -> i32 { + 300 + } +} + +impl Baz for i32 { + fn d(&self) -> i32 { + 400 + } +} + +fn main() { + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.d(), 400); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + assert_eq!(baz.v(), 31); + + let bar1: &dyn Bar1 = baz; + let _: &dyn std::fmt::Debug = bar1; + assert_eq!(*bar1, 1); + assert_eq!(bar1.a(), 100); + assert_eq!(bar1.b(), 200); + assert_eq!(bar1.z(), 11); + assert_eq!(bar1.y(), 12); + assert_eq!(bar1.w(), 21); + + let bar2: &dyn Bar2 = baz; + let _: &dyn std::fmt::Debug = bar2; + assert_eq!(*bar2, 1); + assert_eq!(bar2.a(), 100); + assert_eq!(bar2.c(), 300); + assert_eq!(bar2.z(), 11); + assert_eq!(bar2.y(), 12); + assert_eq!(bar2.v(), 31); + + let foo: &dyn Foo = baz; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + + let foo: &dyn Foo = bar1; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + + let foo: &dyn Foo = bar2; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); +} diff --git a/src/test/ui/traits/trait-upcasting/invalid-upcast.rs b/src/test/ui/traits/trait-upcasting/invalid-upcast.rs new file mode 100644 index 0000000000000..24022450406a7 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/invalid-upcast.rs @@ -0,0 +1,87 @@ +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } +} + +trait Bar { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } +} + +trait Baz { + fn c(&self) -> i32 { + 30 + } +} + +impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } +} + +impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } +} + +impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } +} + +fn main() { + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + //~^ ERROR mismatched types [E0308] + let _: &dyn Send = baz; + //~^ ERROR mismatched types [E0308] + let _: &dyn Sync = baz; + //~^ ERROR mismatched types [E0308] + + let bar: &dyn Bar = baz; + //~^ ERROR mismatched types [E0308] + let _: &dyn std::fmt::Debug = bar; + //~^ ERROR mismatched types [E0308] + let _: &dyn Send = bar; + //~^ ERROR mismatched types [E0308] + let _: &dyn Sync = bar; + //~^ ERROR mismatched types [E0308] + + let foo: &dyn Foo = baz; + //~^ ERROR mismatched types [E0308] + let _: &dyn std::fmt::Debug = foo; + //~^ ERROR mismatched types [E0308] + let _: &dyn Send = foo; + //~^ ERROR mismatched types [E0308] + let _: &dyn Sync = foo; + //~^ ERROR mismatched types [E0308] + + let foo: &dyn Foo = bar; + //~^ ERROR mismatched types [E0308] + let _: &dyn std::fmt::Debug = foo; + //~^ ERROR mismatched types [E0308] + let _: &dyn Send = foo; + //~^ ERROR mismatched types [E0308] + let _: &dyn Sync = foo; + //~^ ERROR mismatched types [E0308] +} diff --git a/src/test/ui/traits/trait-upcasting/invalid-upcast.stderr b/src/test/ui/traits/trait-upcasting/invalid-upcast.stderr new file mode 100644 index 0000000000000..b4530ed0c3a94 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/invalid-upcast.stderr @@ -0,0 +1,168 @@ +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:54:35 + | +LL | let _: &dyn std::fmt::Debug = baz; + | -------------------- ^^^ expected trait `Debug`, found trait `Baz` + | | + | expected due to this + | + = note: expected reference `&dyn Debug` + found reference `&dyn Baz` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:56:24 + | +LL | let _: &dyn Send = baz; + | --------- ^^^ expected trait `Send`, found trait `Baz` + | | + | expected due to this + | + = note: expected reference `&dyn Send` + found reference `&dyn Baz` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:58:24 + | +LL | let _: &dyn Sync = baz; + | --------- ^^^ expected trait `Sync`, found trait `Baz` + | | + | expected due to this + | + = note: expected reference `&dyn Sync` + found reference `&dyn Baz` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:61:25 + | +LL | let bar: &dyn Bar = baz; + | -------- ^^^ expected trait `Bar`, found trait `Baz` + | | + | expected due to this + | + = note: expected reference `&dyn Bar` + found reference `&dyn Baz` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:63:35 + | +LL | let _: &dyn std::fmt::Debug = bar; + | -------------------- ^^^ expected trait `Debug`, found trait `Bar` + | | + | expected due to this + | + = note: expected reference `&dyn Debug` + found reference `&dyn Bar` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:65:24 + | +LL | let _: &dyn Send = bar; + | --------- ^^^ expected trait `Send`, found trait `Bar` + | | + | expected due to this + | + = note: expected reference `&dyn Send` + found reference `&dyn Bar` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:67:24 + | +LL | let _: &dyn Sync = bar; + | --------- ^^^ expected trait `Sync`, found trait `Bar` + | | + | expected due to this + | + = note: expected reference `&dyn Sync` + found reference `&dyn Bar` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:70:25 + | +LL | let foo: &dyn Foo = baz; + | -------- ^^^ expected trait `Foo`, found trait `Baz` + | | + | expected due to this + | + = note: expected reference `&dyn Foo` + found reference `&dyn Baz` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:72:35 + | +LL | let _: &dyn std::fmt::Debug = foo; + | -------------------- ^^^ expected trait `Debug`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Debug` + found reference `&dyn Foo` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:74:24 + | +LL | let _: &dyn Send = foo; + | --------- ^^^ expected trait `Send`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Send` + found reference `&dyn Foo` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:76:24 + | +LL | let _: &dyn Sync = foo; + | --------- ^^^ expected trait `Sync`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Sync` + found reference `&dyn Foo` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:79:25 + | +LL | let foo: &dyn Foo = bar; + | -------- ^^^ expected trait `Foo`, found trait `Bar` + | | + | expected due to this + | + = note: expected reference `&dyn Foo` + found reference `&dyn Bar` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:81:35 + | +LL | let _: &dyn std::fmt::Debug = foo; + | -------------------- ^^^ expected trait `Debug`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Debug` + found reference `&dyn Foo` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:83:24 + | +LL | let _: &dyn Send = foo; + | --------- ^^^ expected trait `Send`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Send` + found reference `&dyn Foo` + +error[E0308]: mismatched types + --> $DIR/invalid-upcast.rs:85:24 + | +LL | let _: &dyn Sync = foo; + | --------- ^^^ expected trait `Sync`, found trait `Foo` + | | + | expected due to this + | + = note: expected reference `&dyn Sync` + found reference `&dyn Foo` + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/traits/trait-upcasting/lifetime.rs b/src/test/ui/traits/trait-upcasting/lifetime.rs new file mode 100644 index 0000000000000..052f090102e96 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/lifetime.rs @@ -0,0 +1,96 @@ +// run-pass +// ignore-compare-mode-nll + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } +} + +trait Bar: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } +} + +trait Baz: Bar { + fn c(&self) -> i32 { + 30 + } +} + +impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } +} + +impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } +} + +impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } +} + +// Note: upcast lifetime means a shorter lifetime. +fn upcast_baz<'a: 'b, 'b, T>(v: Box, _l: &'b T) -> Box { + v +} +fn upcast_bar<'a: 'b, 'b, T>(v: Box, _l: &'b T) -> Box { + v +} +fn upcast_foo<'a: 'b, 'b, T>(v: Box, _l: &'b T) -> Box { + v +} + +fn main() { + let v = Box::new(1); + let l = &(); // dummy lifetime (shorter than `baz`) + + let baz: Box = v.clone(); + let u = upcast_baz(baz, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); + assert_eq!(u.b(), 200); + assert_eq!(u.c(), 300); + + let baz: Box = v.clone(); + let bar: Box = baz; + let u = upcast_bar(bar, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); + assert_eq!(u.b(), 200); + + let baz: Box = v.clone(); + let foo: Box = baz; + let u = upcast_foo(foo, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); + + let baz: Box = v.clone(); + let bar: Box = baz; + let foo: Box = bar; + let u = upcast_foo(foo, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); +} diff --git a/src/test/ui/traits/trait-upcasting/replace-vptr.rs b/src/test/ui/traits/trait-upcasting/replace-vptr.rs new file mode 100644 index 0000000000000..1164e43611a1d --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/replace-vptr.rs @@ -0,0 +1,49 @@ +// run-pass + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait A { + fn foo_a(&self); +} + +trait B { + fn foo_b(&self); +} + +trait C: A + B { + fn foo_c(&self); +} + +struct S(i32); + +impl A for S { + fn foo_a(&self) { + unreachable!(); + } +} + +impl B for S { + fn foo_b(&self) { + assert_eq!(42, self.0); + } +} + +impl C for S { + fn foo_c(&self) { + unreachable!(); + } +} + +fn invoke_inner(b: &dyn B) { + b.foo_b(); +} + +fn invoke_outer(c: &dyn C) { + invoke_inner(c); +} + +fn main() { + let s = S(42); + invoke_outer(&s); +} diff --git a/src/test/ui/traits/trait-upcasting/struct.rs b/src/test/ui/traits/trait-upcasting/struct.rs new file mode 100644 index 0000000000000..0f3cb285bf4c3 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/struct.rs @@ -0,0 +1,174 @@ +// run-pass + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +use std::rc::Rc; +use std::sync::Arc; + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } +} + +trait Bar: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } +} + +trait Baz: Bar { + fn c(&self) -> i32 { + 30 + } +} + +impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } +} + +impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } +} + +impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } +} + +fn test_box() { + let v = Box::new(1); + + let baz: Box = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Box = v.clone(); + let bar: Box = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Box = v.clone(); + let foo: Box = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Box = v.clone(); + let bar: Box = baz; + let foo: Box = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn test_rc() { + let v = Rc::new(1); + + let baz: Rc = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Rc = v.clone(); + let bar: Rc = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Rc = v.clone(); + let foo: Rc = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Rc = v.clone(); + let bar: Rc = baz; + let foo: Rc = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn test_arc() { + let v = Arc::new(1); + + let baz: Arc = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Arc = v.clone(); + let bar: Arc = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Arc = v.clone(); + let foo: Arc = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Arc = v.clone(); + let bar: Arc = baz; + let foo: Arc = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn main() { + test_box(); + test_rc(); + test_arc(); +} diff --git a/src/test/ui/traits/trait-upcasting/subtrait-method.rs b/src/test/ui/traits/trait-upcasting/subtrait-method.rs new file mode 100644 index 0000000000000..3508e15284bf2 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/subtrait-method.rs @@ -0,0 +1,70 @@ +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } +} + +trait Bar: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } +} + +trait Baz: Bar { + fn c(&self) -> i32 { + 30 + } +} + +impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } +} + +impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } +} + +impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } +} + +fn main() { + let baz: &dyn Baz = &1; + + let bar: &dyn Bar = baz; + bar.c(); + //~^ ERROR no method named `c` found for reference `&dyn Bar` in the current scope [E0599] + + let foo: &dyn Foo = baz; + foo.b(); + //~^ ERROR no method named `b` found for reference `&dyn Foo` in the current scope [E0599] + foo.c(); + //~^ ERROR no method named `c` found for reference `&dyn Foo` in the current scope [E0599] + + let foo: &dyn Foo = bar; + foo.b(); + //~^ ERROR no method named `b` found for reference `&dyn Foo` in the current scope [E0599] + foo.c(); + //~^ ERROR no method named `c` found for reference `&dyn Foo` in the current scope [E0599] +} diff --git a/src/test/ui/traits/trait-upcasting/subtrait-method.stderr b/src/test/ui/traits/trait-upcasting/subtrait-method.stderr new file mode 100644 index 0000000000000..8c69011800b47 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/subtrait-method.stderr @@ -0,0 +1,68 @@ +error[E0599]: no method named `c` found for reference `&dyn Bar` in the current scope + --> $DIR/subtrait-method.rs:56:9 + | +LL | bar.c(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Baz` defines an item `c`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:28:1 + | +LL | trait Baz: Bar { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:60:9 + | +LL | foo.b(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `b`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:18:1 + | +LL | trait Bar: Foo { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:62:9 + | +LL | foo.c(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Baz` defines an item `c`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:28:1 + | +LL | trait Baz: Bar { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:66:9 + | +LL | foo.b(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `b`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:18:1 + | +LL | trait Bar: Foo { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:68:9 + | +LL | foo.c(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Baz` defines an item `c`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:28:1 + | +LL | trait Baz: Bar { + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0599`.