simplify handling of valtrees for unsized types

This commit is contained in:
Ralf Jung 2023-08-05 16:05:30 +02:00
parent b6e3bc23ef
commit 997ec63fb1
3 changed files with 43 additions and 82 deletions

View File

@ -7,9 +7,10 @@ use crate::interpret::{
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
MemoryKind, Place, Projectable, Scalar, MemoryKind, Place, Projectable, Scalar,
}; };
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::source_map::DUMMY_SP; use rustc_span::source_map::DUMMY_SP;
use rustc_target::abi::{Align, VariantIdx}; use rustc_target::abi::VariantIdx;
#[instrument(skip(ecx), level = "debug")] #[instrument(skip(ecx), level = "debug")]
fn branches<'tcx>( fn branches<'tcx>(
@ -154,52 +155,37 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
} }
} }
#[instrument(skip(ecx), level = "debug")] /// Valtrees don't store the `MemPlaceMeta` that all dynamically sized values have in the interpreter.
fn create_mplace_from_layout<'tcx>( /// This function reconstructs it.
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, fn reconstruct_place_meta<'tcx>(
ty: Ty<'tcx>, layout: TyAndLayout<'tcx>,
) -> MPlaceTy<'tcx> {
let tcx = ecx.tcx;
let param_env = ecx.param_env;
let layout = tcx.layout_of(param_env.and(ty)).unwrap();
debug!(?layout);
ecx.allocate(layout, MemoryKind::Stack).unwrap()
}
// Walks custom DSTs and gets the type of the unsized field and the number of elements
// in the unsized field.
fn get_info_on_unsized_field<'tcx>(
ty: Ty<'tcx>,
valtree: ty::ValTree<'tcx>, valtree: ty::ValTree<'tcx>,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
) -> (Ty<'tcx>, usize) { ) -> MemPlaceMeta {
if layout.is_sized() {
return MemPlaceMeta::None;
}
let mut last_valtree = valtree; let mut last_valtree = valtree;
// Traverse the type, and update `last_valtree` as we go.
let tail = tcx.struct_tail_with_normalize( let tail = tcx.struct_tail_with_normalize(
ty, layout.ty,
|ty| ty, |ty| ty,
|| { || {
let branches = last_valtree.unwrap_branch(); let branches = last_valtree.unwrap_branch();
last_valtree = branches[branches.len() - 1]; last_valtree = *branches.last().unwrap();
debug!(?branches, ?last_valtree); debug!(?branches, ?last_valtree);
}, },
); );
let unsized_inner_ty = match tail.kind() { // Sanity-check that we got a tail we support.
ty::Slice(t) => *t, match tail.kind() {
ty::Str => tail, ty::Slice(..) | ty::Str => {}
_ => bug!("expected Slice or Str"), _ => bug!("unsized tail of a valtree must be Slice or Str"),
}; };
// Have to adjust type for ty::Str // Get the number of elements in the unsized field.
let unsized_inner_ty = match unsized_inner_ty.kind() {
ty::Str => tcx.types.u8,
_ => unsized_inner_ty,
};
// Get the number of elements in the unsized field
let num_elems = last_valtree.unwrap_branch().len(); let num_elems = last_valtree.unwrap_branch().len();
MemPlaceMeta::Meta(Scalar::from_target_usize(num_elems as u64, &tcx))
(unsized_inner_ty, num_elems)
} }
#[instrument(skip(ecx), level = "debug", ret)] #[instrument(skip(ecx), level = "debug", ret)]
@ -208,41 +194,9 @@ fn create_pointee_place<'tcx>(
ty: Ty<'tcx>, ty: Ty<'tcx>,
valtree: ty::ValTree<'tcx>, valtree: ty::ValTree<'tcx>,
) -> MPlaceTy<'tcx> { ) -> MPlaceTy<'tcx> {
let tcx = ecx.tcx.tcx; let layout = ecx.layout_of(ty).unwrap();
let meta = reconstruct_place_meta(layout, valtree, ecx.tcx.tcx);
if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) { ecx.allocate_dyn(layout, MemoryKind::Stack, meta).unwrap()
// We need to create `Allocation`s for custom DSTs
let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx);
let unsized_inner_ty = match unsized_inner_ty.kind() {
ty::Str => tcx.types.u8,
_ => unsized_inner_ty,
};
let unsized_inner_ty_size =
tcx.layout_of(ty::ParamEnv::empty().and(unsized_inner_ty)).unwrap().layout.size();
debug!(?unsized_inner_ty, ?unsized_inner_ty_size, ?num_elems);
// for custom DSTs only the last field/element is unsized, but we need to also allocate
// space for the other fields/elements
let layout = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap();
let size_of_sized_part = layout.layout.size();
// Get the size of the memory behind the DST
let dst_size = unsized_inner_ty_size.checked_mul(num_elems as u64, &tcx).unwrap();
let size = size_of_sized_part.checked_add(dst_size, &tcx).unwrap();
let align = Align::from_bytes(size.bytes().next_power_of_two()).unwrap();
let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap();
debug!(?ptr);
MPlaceTy::from_aligned_ptr_with_meta(
ptr.into(),
layout,
MemPlaceMeta::Meta(Scalar::from_target_usize(num_elems as u64, &tcx)),
)
} else {
create_mplace_from_layout(ecx, ty)
}
} }
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
@ -282,10 +236,13 @@ pub fn valtree_to_const_value<'tcx>(
ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
let place = match ty.kind() { let place = match ty.kind() {
ty::Ref(_, inner_ty, _) => { ty::Ref(_, inner_ty, _) => {
// Need to create a place for the pointee to fill for Refs // Need to create a place for the pointee (the reference itself will be an immediate)
create_pointee_place(&mut ecx, *inner_ty, valtree) create_pointee_place(&mut ecx, *inner_ty, valtree)
} }
_ => create_mplace_from_layout(&mut ecx, ty), _ => {
// Need to create a place for this valtree.
create_pointee_place(&mut ecx, ty, valtree)
}
}; };
debug!(?place); debug!(?place);

View File

@ -934,14 +934,26 @@ where
Ok(MPlaceTy { mplace, layout: place.layout, align: place.align }) Ok(MPlaceTy { mplace, layout: place.layout, align: place.align })
} }
pub fn allocate_dyn(
&mut self,
layout: TyAndLayout<'tcx>,
kind: MemoryKind<M::MemoryKind>,
meta: MemPlaceMeta<M::Provenance>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else {
span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
};
let ptr = self.allocate_ptr(size, align, kind)?;
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.into(), layout, meta))
}
pub fn allocate( pub fn allocate(
&mut self, &mut self,
layout: TyAndLayout<'tcx>, layout: TyAndLayout<'tcx>,
kind: MemoryKind<M::MemoryKind>, kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
assert!(layout.is_sized()); assert!(layout.is_sized());
let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?; self.allocate_dyn(layout, kind, MemPlaceMeta::None)
Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
} }
/// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation. /// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation.

View File

@ -393,15 +393,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
)); ));
assert_eq!(dest_offset, None); assert_eq!(dest_offset, None);
// Allocate enough memory to hold `src`. // Allocate enough memory to hold `src`.
let Some((size, align)) = self.size_and_align_of_mplace(&src)? else { let dest_place = self.allocate_dyn(src.layout, MemoryKind::Stack, src.meta)?;
span_bug!(
self.cur_span(),
"unsized fn arg with `extern` type tail should not be allowed"
)
};
let ptr = self.allocate_ptr(size, align, MemoryKind::Stack)?;
let dest_place =
MPlaceTy::from_aligned_ptr_with_meta(ptr.into(), callee_arg.layout, src.meta);
// Update the local to be that new place. // Update the local to be that new place.
*M::access_local_mut(self, dest_frame, dest_local)? = Operand::Indirect(*dest_place); *M::access_local_mut(self, dest_frame, dest_local)? = Operand::Indirect(*dest_place);
} }