Allow dyn* upcasting
This commit is contained in:
parent
76386bd65e
commit
feb4244f54
@ -38,7 +38,7 @@ use rustc_session::Session;
|
|||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
||||||
use rustc_target::abi::{Align, VariantIdx};
|
use rustc_target::abi::{Align, Size, VariantIdx};
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
(&ty::Array(_, len), &ty::Slice(_)) => {
|
(&ty::Array(_, len), &ty::Slice(_)) => {
|
||||||
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
|
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 =
|
let old_info =
|
||||||
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
|
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
|
||||||
if data_a.principal_def_id() == data_b.principal_def_id() {
|
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 {
|
if let Some(entry_idx) = vptr_entry_idx {
|
||||||
let ptr_ty = cx.type_i8p();
|
let ptr_ty = cx.type_i8p();
|
||||||
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
|
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
|
||||||
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
|
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
|
||||||
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
|
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
|
||||||
let gep = bx.inbounds_gep(
|
let gep = bx.inbounds_gep(
|
||||||
ptr_ty,
|
ptr_ty,
|
||||||
@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
old_info
|
old_info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, &ty::Dynamic(ref data, ..)) => {
|
(_, &ty::Dynamic(ref data, _, target_dyn_kind)) => {
|
||||||
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
|
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
|
||||||
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
|
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
|
||||||
}
|
}
|
||||||
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
|
_ => 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.
|
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
|
||||||
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
bx: &mut Bx,
|
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`,
|
/// 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`.
|
/// to a value of type `dst_ty`, and stores the result in `dst`.
|
||||||
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
|
@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef};
|
|||||||
|
|
||||||
use crate::base;
|
use crate::base;
|
||||||
use crate::common::{self, IntPredicate};
|
use crate::common::{self, IntPredicate};
|
||||||
use crate::meth::get_vtable;
|
|
||||||
use crate::traits::*;
|
use crate::traits::*;
|
||||||
use crate::MemFlags;
|
use crate::MemFlags;
|
||||||
|
|
||||||
@ -14,7 +13,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
|
|||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
||||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||||
use rustc_target::abi::Size;
|
|
||||||
|
|
||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
#[instrument(level = "trace", skip(self, bx))]
|
#[instrument(level = "trace", skip(self, bx))]
|
||||||
@ -274,27 +272,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mir::CastKind::DynStar => {
|
mir::CastKind::DynStar => {
|
||||||
let data = match operand.val {
|
let (lldata, llextra) = match operand.val {
|
||||||
OperandValue::Ref(_, _, _) => todo!(),
|
OperandValue::Ref(_, _, _) => todo!(),
|
||||||
OperandValue::Immediate(v) => v,
|
OperandValue::Immediate(v) => (v, None),
|
||||||
OperandValue::Pair(_, _) => todo!(),
|
OperandValue::Pair(v, l) => (v, Some(l)),
|
||||||
};
|
};
|
||||||
let trait_ref =
|
let (lldata, llextra) =
|
||||||
if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() {
|
base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
|
||||||
data.principal()
|
OperandValue::Pair(lldata, llextra)
|
||||||
} 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)
|
|
||||||
}
|
}
|
||||||
mir::CastKind::Pointer(
|
mir::CastKind::Pointer(
|
||||||
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
|
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
|
||||||
|
@ -764,8 +764,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
{
|
{
|
||||||
if a_data.principal_def_id() == b_data.principal_def_id() {
|
if a_data.principal_def_id() == b_data.principal_def_id() {
|
||||||
return self.unify_and(a, b, |_| vec![]);
|
return self.unify_and(a, b, |_| vec![]);
|
||||||
} else {
|
} else if !self.tcx().features().trait_upcasting {
|
||||||
bug!("dyn* trait upcasting is not supported");
|
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…
x
Reference in New Issue
Block a user