Skip to content

Commit

Permalink
Auto merge of rust-lang#104376 - compiler-errors:thin-metadata-implie…
Browse files Browse the repository at this point in the history
…s-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 rust-lang#104338 (comment) .. But is also useful with transmutes, for example, given the UI test I added below.
  • Loading branch information
bors committed Jan 4, 2023
2 parents c361616 + a5d39cf commit ddad1e1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 30 deletions.
65 changes: 43 additions & 22 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
36 changes: 28 additions & 8 deletions compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions src/test/ui/layout/thin-meta-implies-thin-ptr.rs
Original file line number Diff line number Diff line change
@@ -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) }
}

0 comments on commit ddad1e1

Please sign in to comment.