mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #87515 - crlf0710:trait_upcasting_part2, r=bjorn3
Trait upcasting coercion (part2) This is the second part of trait upcasting coercion implementation. Currently this is blocked on #86264 . The third part might be implemented using unsafety checking r? `@bjorn3`
This commit is contained in:
commit
c6bc102fea
@ -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, 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()));
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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>,
|
||||
) -> Cx::Value {
|
||||
old_info: Option<Bx::Value>,
|
||||
) -> 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, 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);
|
||||
|
@ -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
|
||||
|
@ -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<usize> {
|
||||
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<ImplSource<'tcx, ()>, ErrorReported> {
|
||||
|
@ -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
|
||||
|
@ -63,15 +63,19 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
|
||||
Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into())
|
||||
}
|
||||
|
||||
pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_pointer(vtable, cx))
|
||||
pub fn new_dyn_trait(
|
||||
val: Scalar<Tag>,
|
||||
vtable: Pointer<Option<Tag>>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> Self {
|
||||
Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> {
|
||||
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<Tag> {
|
||||
pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
|
||||
self.to_scalar_or_uninit().check_init()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
|
||||
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
|
||||
|
@ -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<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
|
||||
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<Option<M::PointerTag>>,
|
||||
idx: u64,
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<usize> {
|
||||
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();
|
||||
|
@ -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<T: Foo + ?Sized> Bar for T {}
|
||||
|
||||
let bar: &dyn Bar = &123;
|
||||
let foo: &dyn Foo = bar;
|
||||
```
|
87
src/test/ui/traits/trait-upcasting/basic.rs
Normal file
87
src/test/ui/traits/trait-upcasting/basic.rs
Normal file
@ -0,0 +1,87 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Foo: PartialEq<i32> + 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);
|
||||
}
|
@ -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<dyn A> = Box::new(());
|
||||
let _b: Box<dyn B> = a;
|
||||
}
|
@ -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`.
|
115
src/test/ui/traits/trait-upcasting/diamond.rs
Normal file
115
src/test/ui/traits/trait-upcasting/diamond.rs
Normal file
@ -0,0 +1,115 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Foo: PartialEq<i32> + 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);
|
||||
}
|
87
src/test/ui/traits/trait-upcasting/invalid-upcast.rs
Normal file
87
src/test/ui/traits/trait-upcasting/invalid-upcast.rs
Normal file
@ -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]
|
||||
}
|
168
src/test/ui/traits/trait-upcasting/invalid-upcast.stderr
Normal file
168
src/test/ui/traits/trait-upcasting/invalid-upcast.stderr
Normal file
@ -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`.
|
96
src/test/ui/traits/trait-upcasting/lifetime.rs
Normal file
96
src/test/ui/traits/trait-upcasting/lifetime.rs
Normal file
@ -0,0 +1,96 @@
|
||||
// run-pass
|
||||
// ignore-compare-mode-nll
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Foo: PartialEq<i32> + 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<dyn Baz + 'a>, _l: &'b T) -> Box<dyn Baz + 'b> {
|
||||
v
|
||||
}
|
||||
fn upcast_bar<'a: 'b, 'b, T>(v: Box<dyn Bar + 'a>, _l: &'b T) -> Box<dyn Bar + 'b> {
|
||||
v
|
||||
}
|
||||
fn upcast_foo<'a: 'b, 'b, T>(v: Box<dyn Foo + 'a>, _l: &'b T) -> Box<dyn Foo + 'b> {
|
||||
v
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let v = Box::new(1);
|
||||
let l = &(); // dummy lifetime (shorter than `baz`)
|
||||
|
||||
let baz: Box<dyn Baz> = 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<dyn Baz> = v.clone();
|
||||
let bar: Box<dyn Bar> = baz;
|
||||
let u = upcast_bar(bar, &l);
|
||||
assert_eq!(*u, 1);
|
||||
assert_eq!(u.a(), 100);
|
||||
assert_eq!(u.b(), 200);
|
||||
|
||||
let baz: Box<dyn Baz> = v.clone();
|
||||
let foo: Box<dyn Foo> = baz;
|
||||
let u = upcast_foo(foo, &l);
|
||||
assert_eq!(*u, 1);
|
||||
assert_eq!(u.a(), 100);
|
||||
|
||||
let baz: Box<dyn Baz> = v.clone();
|
||||
let bar: Box<dyn Bar> = baz;
|
||||
let foo: Box<dyn Foo> = bar;
|
||||
let u = upcast_foo(foo, &l);
|
||||
assert_eq!(*u, 1);
|
||||
assert_eq!(u.a(), 100);
|
||||
}
|
49
src/test/ui/traits/trait-upcasting/replace-vptr.rs
Normal file
49
src/test/ui/traits/trait-upcasting/replace-vptr.rs
Normal file
@ -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);
|
||||
}
|
174
src/test/ui/traits/trait-upcasting/struct.rs
Normal file
174
src/test/ui/traits/trait-upcasting/struct.rs
Normal file
@ -0,0 +1,174 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
trait Foo: PartialEq<i32> + 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<dyn Baz> = 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<dyn Baz> = v.clone();
|
||||
let bar: Box<dyn Bar> = 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<dyn Baz> = v.clone();
|
||||
let foo: Box<dyn Foo> = baz;
|
||||
assert_eq!(*foo, 1);
|
||||
assert_eq!(foo.a(), 100);
|
||||
assert_eq!(foo.z(), 11);
|
||||
assert_eq!(foo.y(), 12);
|
||||
|
||||
let baz: Box<dyn Baz> = v.clone();
|
||||
let bar: Box<dyn Bar> = baz;
|
||||
let foo: Box<dyn Foo> = 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<dyn Baz> = 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<dyn Baz> = v.clone();
|
||||
let bar: Rc<dyn Bar> = 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<dyn Baz> = v.clone();
|
||||
let foo: Rc<dyn Foo> = baz;
|
||||
assert_eq!(*foo, 1);
|
||||
assert_eq!(foo.a(), 100);
|
||||
assert_eq!(foo.z(), 11);
|
||||
assert_eq!(foo.y(), 12);
|
||||
|
||||
let baz: Rc<dyn Baz> = v.clone();
|
||||
let bar: Rc<dyn Bar> = baz;
|
||||
let foo: Rc<dyn Foo> = 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<dyn Baz> = 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<dyn Baz> = v.clone();
|
||||
let bar: Arc<dyn Bar> = 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<dyn Baz> = v.clone();
|
||||
let foo: Arc<dyn Foo> = baz;
|
||||
assert_eq!(*foo, 1);
|
||||
assert_eq!(foo.a(), 100);
|
||||
assert_eq!(foo.z(), 11);
|
||||
assert_eq!(foo.y(), 12);
|
||||
|
||||
let baz: Arc<dyn Baz> = v.clone();
|
||||
let bar: Arc<dyn Bar> = baz;
|
||||
let foo: Arc<dyn Foo> = 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();
|
||||
}
|
70
src/test/ui/traits/trait-upcasting/subtrait-method.rs
Normal file
70
src/test/ui/traits/trait-upcasting/subtrait-method.rs
Normal file
@ -0,0 +1,70 @@
|
||||
#![feature(trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Foo: PartialEq<i32> + 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]
|
||||
}
|
68
src/test/ui/traits/trait-upcasting/subtrait-method.stderr
Normal file
68
src/test/ui/traits/trait-upcasting/subtrait-method.stderr
Normal file
@ -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`.
|
Loading…
Reference in New Issue
Block a user