Auto merge of #101212 - eholk:dyn-star, r=compiler-errors
Initial implementation of dyn* This PR adds extremely basic and incomplete support for [dyn*](https://smallcultfollowing.com/babysteps//blog/2022/03/29/dyn-can-we-make-dyn-sized/). The goal is to get something in tree behind a flag to make collaboration easier, and also to make sure the implementation so far is not unreasonable. This PR does quite a few things: * Introduce `dyn_star` feature flag * Adds parsing for `dyn* Trait` types * Defines `dyn* Trait` as a sized type * Adds support for explicit casts, like `42usize as dyn* Debug` * Including const evaluation of such casts * Adds codegen for drop glue so things are cleaned up properly when a `dyn* Trait` object goes out of scope * Adds codegen for method calls, at least for methods that take `&self` Quite a bit is still missing, but this gives us a starting point. Note that this is never intended to become stable surface syntax for Rust, but rather `dyn*` is planned to be used as an implementation detail for async functions in dyn traits. Joint work with `@nikomatsakis` and `@compiler-errors.` r? `@bjorn3`
This commit is contained in:
commit
6153d3cbe6
69 changed files with 617 additions and 104 deletions
|
@ -69,7 +69,7 @@ impl<'a, 'tcx> VirtualIndex {
|
|||
fn expect_dyn_trait_in_self<'tcx>(ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'tcx> {
|
||||
for arg in ty.peel_refs().walk() {
|
||||
if let GenericArgKind::Type(ty) = arg.unpack() {
|
||||
if let ty::Dynamic(data, _) = ty.kind() {
|
||||
if let ty::Dynamic(data, _, _) = ty.kind() {
|
||||
return data.principal().expect("expected principal trait object");
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ fn expect_dyn_trait_in_self<'tcx>(ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'
|
|||
/// The `trait_ref` encodes the erased self type. Hence if we are
|
||||
/// making an object `Foo<dyn Trait>` from a value of type `Foo<T>`, then
|
||||
/// `trait_ref` would map `T: Trait`.
|
||||
#[instrument(level = "debug", skip(cx))]
|
||||
pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
|
||||
cx: &Cx,
|
||||
ty: Ty<'tcx>,
|
||||
|
@ -93,8 +94,6 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
|
|||
) -> Cx::Value {
|
||||
let tcx = cx.tcx();
|
||||
|
||||
debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref);
|
||||
|
||||
// Check the cache.
|
||||
if let Some(&val) = cx.vtables().borrow().get(&(ty, trait_ref)) {
|
||||
return val;
|
||||
|
|
|
@ -367,6 +367,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
bx.ret(llval);
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self, helper, bx))]
|
||||
fn codegen_drop_terminator(
|
||||
&mut self,
|
||||
helper: TerminatorCodegenHelper<'tcx>,
|
||||
|
@ -397,13 +398,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let (drop_fn, fn_abi) = match ty.kind() {
|
||||
// FIXME(eddyb) perhaps move some of this logic into
|
||||
// `Instance::resolve_drop_in_place`?
|
||||
ty::Dynamic(..) => {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
// IN THIS ARM, WE HAVE:
|
||||
// ty = *mut (dyn Trait)
|
||||
// which is: exists<T> ( *mut T, Vtable<T: Trait> )
|
||||
// args[0] args[1]
|
||||
//
|
||||
// args = ( Data, Vtable )
|
||||
// |
|
||||
// v
|
||||
// /-------\
|
||||
// | ... |
|
||||
// \-------/
|
||||
//
|
||||
let virtual_drop = Instance {
|
||||
def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0),
|
||||
substs: drop_fn.substs,
|
||||
};
|
||||
debug!("ty = {:?}", ty);
|
||||
debug!("drop_fn = {:?}", drop_fn);
|
||||
debug!("args = {:?}", args);
|
||||
let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty());
|
||||
let vtable = args[1];
|
||||
// Truncate vtable off of args list
|
||||
args = &args[..1];
|
||||
(
|
||||
meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE)
|
||||
|
@ -411,6 +428,51 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
fn_abi,
|
||||
)
|
||||
}
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// IN THIS ARM, WE HAVE:
|
||||
// ty = *mut (dyn* Trait)
|
||||
// which is: *mut exists<T: sizeof(T) == sizeof(usize)> (T, Vtable<T: Trait>)
|
||||
//
|
||||
// args = [ * ]
|
||||
// |
|
||||
// v
|
||||
// ( Data, Vtable )
|
||||
// |
|
||||
// v
|
||||
// /-------\
|
||||
// | ... |
|
||||
// \-------/
|
||||
//
|
||||
//
|
||||
// WE CAN CONVERT THIS INTO THE ABOVE LOGIC BY DOING
|
||||
//
|
||||
// data = &(*args[0]).0 // gives a pointer to Data above (really the same pointer)
|
||||
// vtable = (*args[0]).1 // loads the vtable out
|
||||
// (data, vtable) // an equivalent Rust `*mut dyn Trait`
|
||||
//
|
||||
// SO THEN WE CAN USE THE ABOVE CODE.
|
||||
let virtual_drop = Instance {
|
||||
def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0),
|
||||
substs: drop_fn.substs,
|
||||
};
|
||||
debug!("ty = {:?}", ty);
|
||||
debug!("drop_fn = {:?}", drop_fn);
|
||||
debug!("args = {:?}", args);
|
||||
let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty());
|
||||
let data = args[0];
|
||||
let data_ty = bx.cx().backend_type(place.layout);
|
||||
let vtable_ptr =
|
||||
bx.gep(data_ty, data, &[bx.cx().const_i32(0), bx.cx().const_i32(1)]);
|
||||
let vtable = bx.load(bx.type_i8p(), vtable_ptr, abi::Align::ONE);
|
||||
// Truncate vtable off of args list
|
||||
args = &args[..1];
|
||||
debug!("args' = {:?}", args);
|
||||
(
|
||||
meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE)
|
||||
.get_fn(&mut bx, vtable, ty, &fn_abi),
|
||||
fn_abi,
|
||||
)
|
||||
}
|
||||
_ => (bx.get_fn_addr(drop_fn), bx.fn_abi_of_instance(drop_fn, ty::List::empty())),
|
||||
};
|
||||
helper.do_call(
|
||||
|
@ -845,7 +907,30 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
llargs.push(data_ptr);
|
||||
continue;
|
||||
}
|
||||
_ => span_bug!(span, "can't codegen a virtual call on {:?}", op),
|
||||
Immediate(_) => {
|
||||
let ty::Ref(_, ty, _) = op.layout.ty.kind() else {
|
||||
span_bug!(span, "can't codegen a virtual call on {:#?}", op);
|
||||
};
|
||||
if !ty.is_dyn_star() {
|
||||
span_bug!(span, "can't codegen a virtual call on {:#?}", op);
|
||||
}
|
||||
// FIXME(dyn-star): Make sure this is done on a &dyn* receiver
|
||||
let place = op.deref(bx.cx());
|
||||
let data_ptr = place.project_field(&mut bx, 0);
|
||||
let meta_ptr = place.project_field(&mut bx, 1);
|
||||
let meta = bx.load_operand(meta_ptr);
|
||||
llfn = Some(meth::VirtualIndex::from_index(idx).get_fn(
|
||||
&mut bx,
|
||||
meta.immediate(),
|
||||
op.layout.ty,
|
||||
&fn_abi,
|
||||
));
|
||||
llargs.push(data_ptr.llval);
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
span_bug!(span, "can't codegen a virtual call on {:#?}", op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ use super::{FunctionCx, LocalRef};
|
|||
|
||||
use crate::base;
|
||||
use crate::common::{self, IntPredicate};
|
||||
use crate::meth::get_vtable;
|
||||
use crate::traits::*;
|
||||
use crate::MemFlags;
|
||||
|
||||
|
@ -271,6 +272,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
bug!("unexpected non-pair operand");
|
||||
}
|
||||
}
|
||||
mir::CastKind::DynStar => {
|
||||
let data = match operand.val {
|
||||
OperandValue::Ref(_, _, _) => todo!(),
|
||||
OperandValue::Immediate(v) => v,
|
||||
OperandValue::Pair(_, _) => todo!(),
|
||||
};
|
||||
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);
|
||||
OperandValue::Pair(data, vtable)
|
||||
}
|
||||
mir::CastKind::Pointer(
|
||||
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue