Allow dyn* upcasting
This commit is contained in:
parent
76386bd65e
commit
feb4244f54
@ -38,7 +38,7 @@
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
||||
use rustc_target::abi::{Align, VariantIdx};
|
||||
use rustc_target::abi::{Align, Size, VariantIdx};
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::convert::TryFrom;
|
||||
@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
(&ty::Array(_, len), &ty::Slice(_)) => {
|
||||
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
|
||||
}
|
||||
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
|
||||
(
|
||||
&ty::Dynamic(ref data_a, _, src_dyn_kind),
|
||||
&ty::Dynamic(ref data_b, _, target_dyn_kind),
|
||||
) => {
|
||||
assert_eq!(src_dyn_kind, target_dyn_kind);
|
||||
|
||||
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() {
|
||||
@ -166,11 +171,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
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 vtable_ptr_ty = cx.scalar_pair_element_backend_type(
|
||||
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
|
||||
1,
|
||||
true,
|
||||
);
|
||||
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
|
||||
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
|
||||
let gep = bx.inbounds_gep(
|
||||
ptr_ty,
|
||||
@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
old_info
|
||||
}
|
||||
}
|
||||
(_, &ty::Dynamic(ref data, ..)) => {
|
||||
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
|
||||
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
|
||||
1,
|
||||
true,
|
||||
);
|
||||
(_, &ty::Dynamic(ref data, _, target_dyn_kind)) => {
|
||||
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
|
||||
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
|
||||
}
|
||||
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the vtable pointer type of a `dyn` or `dyn*` type
|
||||
fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
|
||||
cx: &Cx,
|
||||
target: Ty<'tcx>,
|
||||
kind: ty::DynKind,
|
||||
) -> <Cx as BackendTypes>::Type {
|
||||
cx.scalar_pair_element_backend_type(
|
||||
cx.layout_of(match kind {
|
||||
// vtable is the second field of `*mut dyn Trait`
|
||||
ty::Dyn => cx.tcx().mk_mut_ptr(target),
|
||||
// vtable is the second field of `dyn* Trait`
|
||||
ty::DynStar => target,
|
||||
}),
|
||||
1,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
|
||||
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx: &mut Bx,
|
||||
@ -247,6 +262,26 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerces `src` to `dst_ty` which is guaranteed to be a `dyn*` type.
|
||||
pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx: &mut Bx,
|
||||
src: Bx::Value,
|
||||
src_ty_and_layout: TyAndLayout<'tcx>,
|
||||
dst_ty: Ty<'tcx>,
|
||||
old_info: Option<Bx::Value>,
|
||||
) -> (Bx::Value, Bx::Value) {
|
||||
debug!("unsize_ptr: {:?} => {:?}", src_ty_and_layout.ty, dst_ty);
|
||||
assert!(matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)));
|
||||
// FIXME(dyn-star): this is probably not the best way to check if this is
|
||||
// a pointer, and really we should ensure that the value is a suitable
|
||||
// pointer earlier in the compilation process.
|
||||
let src = match src_ty_and_layout.pointee_info_at(bx.cx(), Size::ZERO) {
|
||||
Some(_) => bx.ptrtoint(src, bx.cx().type_isize()),
|
||||
None => bx.bitcast(src, bx.type_isize()),
|
||||
};
|
||||
(src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info))
|
||||
}
|
||||
|
||||
/// Coerces `src`, which is a reference to a value of type `src_ty`,
|
||||
/// to a value of type `dst_ty`, and stores the result in `dst`.
|
||||
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use crate::base;
|
||||
use crate::common::{self, IntPredicate};
|
||||
use crate::meth::get_vtable;
|
||||
use crate::traits::*;
|
||||
use crate::MemFlags;
|
||||
|
||||
@ -14,7 +13,6 @@
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
#[instrument(level = "trace", skip(self, bx))]
|
||||
@ -274,27 +272,14 @@ pub fn codegen_rvalue_operand(
|
||||
}
|
||||
}
|
||||
mir::CastKind::DynStar => {
|
||||
let data = match operand.val {
|
||||
let (lldata, llextra) = match operand.val {
|
||||
OperandValue::Ref(_, _, _) => todo!(),
|
||||
OperandValue::Immediate(v) => v,
|
||||
OperandValue::Pair(_, _) => todo!(),
|
||||
OperandValue::Immediate(v) => (v, None),
|
||||
OperandValue::Pair(v, l) => (v, Some(l)),
|
||||
};
|
||||
let trait_ref =
|
||||
if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() {
|
||||
data.principal()
|
||||
} else {
|
||||
bug!("Only valid to do a DynStar cast into a DynStar type")
|
||||
};
|
||||
let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref);
|
||||
let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize()));
|
||||
// FIXME(dyn-star): this is probably not the best way to check if this is
|
||||
// a pointer, and really we should ensure that the value is a suitable
|
||||
// pointer earlier in the compilation process.
|
||||
let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) {
|
||||
Some(_) => bx.ptrtoint(data, bx.cx().type_isize()),
|
||||
None => data,
|
||||
};
|
||||
OperandValue::Pair(data, vtable)
|
||||
let (lldata, llextra) =
|
||||
base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
|
||||
OperandValue::Pair(lldata, llextra)
|
||||
}
|
||||
mir::CastKind::Pointer(
|
||||
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
|
||||
|
@ -764,8 +764,16 @@ fn coerce_dyn_star(
|
||||
{
|
||||
if a_data.principal_def_id() == b_data.principal_def_id() {
|
||||
return self.unify_and(a, b, |_| vec![]);
|
||||
} else {
|
||||
bug!("dyn* trait upcasting is not supported");
|
||||
} else if !self.tcx().features().trait_upcasting {
|
||||
let mut err = feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
sym::trait_upcasting,
|
||||
self.cause.span,
|
||||
&format!(
|
||||
"cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
|
||||
),
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
33
src/test/ui/dyn-star/upcast.rs
Normal file
33
src/test/ui/dyn-star/upcast.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(dyn_star, trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Foo: Bar {
|
||||
fn hello(&self);
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
fn world(&self);
|
||||
}
|
||||
|
||||
struct W(usize);
|
||||
|
||||
impl Foo for W {
|
||||
fn hello(&self) {
|
||||
println!("hello!");
|
||||
}
|
||||
}
|
||||
|
||||
impl Bar for W {
|
||||
fn world(&self) {
|
||||
println!("world!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let w: dyn* Foo = W(0);
|
||||
w.hello();
|
||||
let w: dyn* Bar = w;
|
||||
w.world();
|
||||
}
|
Loading…
Reference in New Issue
Block a user