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
22 changed files with 1186 additions and 93 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue