Auto merge of #104376 - compiler-errors:thin-metadata-implies-thin-ptr, r=wesleywiser

layout_of: `T: Thin` implies `sizeof(&T) == sizeof(usize)`

Use the `<T as Pointee>::Metadata` associated type to calculate the layout of a pointee's metadata, instead of hard-coding rules about certain types.

Maybe this approach is overkill -- we could instead hard-code this approach as a fallback, with the matching on `Slice`/`Dynamic`/etc. happening first

Fixes this issue here https://github.com/rust-lang/rust/pull/104338#issuecomment-1312595844 .. But is also useful with transmutes, for example, given the UI test I added below.
This commit is contained in:
bors 2023-01-04 06:28:05 +00:00
commit ddad1e1f15
3 changed files with 82 additions and 30 deletions

View File

@ -670,29 +670,50 @@ where
});
}
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize),
ty::Dynamic(_, _, ty::Dyn) => {
TyMaybeWithLayout::Ty(tcx.mk_imm_ref(
tcx.lifetimes.re_static,
tcx.mk_array(tcx.types.usize, 3),
))
/* FIXME: use actual fn pointers
Warning: naively computing the number of entries in the
vtable by counting the methods on the trait + methods on
all parent traits does not work, because some methods can
be not object safe and thus excluded from the vtable.
Increase this counter if you tried to implement this but
failed to do it without duplicating a lot of code from
other places in the compiler: 2
tcx.mk_tup(&[
tcx.mk_array(tcx.types.usize, 3),
tcx.mk_array(Option<fn()>),
])
*/
let mk_dyn_vtable = || {
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
/* FIXME: use actual fn pointers
Warning: naively computing the number of entries in the
vtable by counting the methods on the trait + methods on
all parent traits does not work, because some methods can
be not object safe and thus excluded from the vtable.
Increase this counter if you tried to implement this but
failed to do it without duplicating a lot of code from
other places in the compiler: 2
tcx.mk_tup(&[
tcx.mk_array(tcx.types.usize, 3),
tcx.mk_array(Option<fn()>),
])
*/
};
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
let metadata = tcx.normalize_erasing_regions(
cx.param_env(),
tcx.mk_projection(metadata_def_id, [pointee]),
);
// Map `Metadata = DynMetadata<dyn Trait>` back to a vtable, since it
// offers better information than `std::ptr::metadata::VTable`,
// and we rely on this layout information to trigger a panic in
// `std::mem::uninitialized::<&dyn Trait>()`, for example.
if let ty::Adt(def, substs) = metadata.kind()
&& Some(def.did()) == tcx.lang_items().dyn_metadata()
&& substs.type_at(0).is_trait()
{
mk_dyn_vtable()
} else {
metadata
}
_ => bug!("TyAndLayout::field({:?}): not applicable", this),
}
} else {
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => tcx.types.usize,
ty::Dynamic(_, _, ty::Dyn) => mk_dyn_vtable(),
_ => bug!("TyAndLayout::field({:?}): not applicable", this),
}
};
TyMaybeWithLayout::Ty(metadata)
}
// Arrays and slices.

View File

@ -155,17 +155,37 @@ fn layout_of_uncached<'tcx>(
}
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
let metadata = match unsized_part.kind() {
ty::Foreign(..) => {
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
let metadata_ty = tcx.normalize_erasing_regions(
param_env,
tcx.mk_projection(metadata_def_id, [pointee]),
);
let metadata_layout = cx.layout_of(metadata_ty)?;
// If the metadata is a 1-zst, then the pointer is thin.
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
}
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
ty::Dynamic(..) => {
let mut vtable = scalar_unit(Pointer);
vtable.valid_range_mut().start = 1;
vtable
let Abi::Scalar(metadata) = metadata_layout.abi else {
return Err(LayoutError::Unknown(unsized_part));
};
metadata
} else {
match unsized_part.kind() {
ty::Foreign(..) => {
return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
}
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
ty::Dynamic(..) => {
let mut vtable = scalar_unit(Pointer);
vtable.valid_range_mut().start = 1;
vtable
}
_ => {
return Err(LayoutError::Unknown(unsized_part));
}
}
_ => return Err(LayoutError::Unknown(unsized_part)),
};
// Effectively a (ptr, meta) tuple.

View File

@ -0,0 +1,11 @@
// check-pass
#![feature(ptr_metadata)]
use std::ptr::Thin;
fn main() {}
fn foo<T: ?Sized + Thin>(t: *const T) -> *const () {
unsafe { std::mem::transmute(t) }
}