basic dyn* support for Miri
This commit is contained in:
parent
dc89a803d6
commit
b2f58146b9
16 changed files with 325 additions and 84 deletions
|
@ -538,10 +538,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively
|
||||
// unwrap those newtypes until we are there.
|
||||
let mut receiver = args[0].clone();
|
||||
let receiver_place = loop {
|
||||
let receiver = loop {
|
||||
match receiver.layout.ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?,
|
||||
ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values
|
||||
ty::Dynamic(..) | ty::Ref(..) | ty::RawPtr(..) => break receiver,
|
||||
_ => {
|
||||
// Not there yet, search for the only non-ZST field.
|
||||
let mut non_zst_field = None;
|
||||
|
@ -567,39 +566,83 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
};
|
||||
// Obtain the underlying trait we are working on.
|
||||
let receiver_tail = self
|
||||
.tcx
|
||||
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
|
||||
let ty::Dynamic(data, ..) = receiver_tail.kind() else {
|
||||
span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail)
|
||||
};
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub_format!(
|
||||
"`dyn` call on a pointer whose vtable does not match its type"
|
||||
);
|
||||
}
|
||||
// break self.deref_operand(&receiver)?.into();
|
||||
|
||||
// Obtain the underlying trait we are working on, and the adjusted receiver argument.
|
||||
let recv_ty = receiver.layout.ty;
|
||||
let (vptr, dyn_ty, adjusted_receiver) = match recv_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..)
|
||||
if matches!(
|
||||
recv_ty.builtin_deref(true).unwrap().ty.kind(),
|
||||
ty::Dynamic(_, _, ty::DynStar)
|
||||
) =>
|
||||
{
|
||||
let receiver = self.deref_operand(&receiver)?;
|
||||
let ty::Dynamic(data, ..) = receiver.layout.ty.kind() else { bug!() };
|
||||
let (recv, vptr) = self.unpack_dyn_star(&receiver.into())?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub_format!(
|
||||
"`dyn*` call on a pointer whose vtable does not match its type"
|
||||
);
|
||||
}
|
||||
let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one
|
||||
|
||||
(vptr, dyn_ty, recv.ptr)
|
||||
}
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// Not clear how to handle this, so far we assume the receiver is always a pointer.
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"by-value calls on a `dyn*`... are those a thing?"
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
let receiver_place = match recv_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => self.deref_operand(&receiver)?,
|
||||
ty::Dynamic(_, _, ty::Dyn) => receiver.assert_mem_place(), // unsized (`dyn`) cannot be immediate
|
||||
_ => bug!(),
|
||||
};
|
||||
// Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`.
|
||||
// (For that reason we also cannot use `unpack_dyn_trait`.)
|
||||
let receiver_tail = self.tcx.struct_tail_erasing_lifetimes(
|
||||
receiver_place.layout.ty,
|
||||
self.param_env,
|
||||
);
|
||||
let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else {
|
||||
span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail)
|
||||
};
|
||||
assert!(receiver_place.layout.is_unsized());
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub_format!(
|
||||
"`dyn` call on a pointer whose vtable does not match its type"
|
||||
);
|
||||
}
|
||||
|
||||
// It might be surprising that we use a pointer as the receiver even if this
|
||||
// is a by-val case; this works because by-val passing of an unsized `dyn
|
||||
// Trait` to a function is actually desugared to a pointer.
|
||||
(vptr, dyn_ty, receiver_place.ptr)
|
||||
}
|
||||
};
|
||||
|
||||
// Now determine the actual method to call. We can do that in two different ways and
|
||||
// compare them to ensure everything fits.
|
||||
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else {
|
||||
throw_ub_format!("`dyn` call trying to call something that is not a method")
|
||||
};
|
||||
trace!("Virtual call dispatches to {fn_inst:#?}");
|
||||
if cfg!(debug_assertions) {
|
||||
let tcx = *self.tcx;
|
||||
|
||||
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
|
||||
let virtual_trait_ref =
|
||||
ty::TraitRef::from_method(tcx, trait_def_id, instance.substs);
|
||||
assert_eq!(
|
||||
receiver_tail,
|
||||
virtual_trait_ref.self_ty(),
|
||||
"mismatch in underlying dyn trait computation within Miri and MIR building",
|
||||
);
|
||||
let existential_trait_ref =
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
|
||||
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
|
||||
|
@ -614,17 +657,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
assert_eq!(fn_inst, concrete_method);
|
||||
}
|
||||
|
||||
// `*mut receiver_place.layout.ty` is almost the layout that we
|
||||
// want for args[0]: We have to project to field 0 because we want
|
||||
// a thin pointer.
|
||||
assert!(receiver_place.layout.is_unsized());
|
||||
let receiver_ptr_ty = self.tcx.mk_mut_ptr(receiver_place.layout.ty);
|
||||
let this_receiver_ptr = self.layout_of(receiver_ptr_ty)?.field(self, 0);
|
||||
// Adjust receiver argument.
|
||||
args[0] = OpTy::from(ImmTy::from_immediate(
|
||||
Scalar::from_maybe_pointer(receiver_place.ptr, self).into(),
|
||||
this_receiver_ptr,
|
||||
));
|
||||
// Adjust receiver argument. Layout can be any (thin) ptr.
|
||||
args[0] = ImmTy::from_immediate(
|
||||
Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
|
||||
self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?,
|
||||
)
|
||||
.into();
|
||||
trace!("Patched receiver operand to {:#?}", args[0]);
|
||||
// recurse with concrete function
|
||||
self.eval_fn_call(
|
||||
|
@ -653,15 +691,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// implementation fail -- a problem shared by rustc.
|
||||
let place = self.force_allocation(place)?;
|
||||
|
||||
let (instance, place) = match place.layout.ty.kind() {
|
||||
ty::Dynamic(..) => {
|
||||
let place = match place.layout.ty.kind() {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
// Dropping a trait object. Need to find actual drop fn.
|
||||
let place = self.unpack_dyn_trait(&place)?;
|
||||
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
(instance, place)
|
||||
self.unpack_dyn_trait(&place)?.0
|
||||
}
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// Dropping a `dyn*`. Need to find actual drop fn.
|
||||
self.unpack_dyn_star(&place.into())?.0.assert_mem_place()
|
||||
}
|
||||
_ => {
|
||||
debug_assert_eq!(
|
||||
instance,
|
||||
ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty)
|
||||
);
|
||||
place
|
||||
}
|
||||
_ => (instance, place),
|
||||
};
|
||||
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
|
||||
|
||||
let arg = ImmTy::from_immediate(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue