Skip to content

Commit ddad1e1

Browse files
committedJan 4, 2023
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 #104338 (comment) .. But is also useful with transmutes, for example, given the UI test I added below.
2 parents c361616 + a5d39cf commit ddad1e1

File tree

3 files changed

+82
-30
lines changed

3 files changed

+82
-30
lines changed
 

‎compiler/rustc_middle/src/ty/layout.rs

+43-22
Original file line numberDiff line numberDiff line change
@@ -670,29 +670,50 @@ where
670670
});
671671
}
672672

673-
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
674-
ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize),
675-
ty::Dynamic(_, _, ty::Dyn) => {
676-
TyMaybeWithLayout::Ty(tcx.mk_imm_ref(
677-
tcx.lifetimes.re_static,
678-
tcx.mk_array(tcx.types.usize, 3),
679-
))
680-
/* FIXME: use actual fn pointers
681-
Warning: naively computing the number of entries in the
682-
vtable by counting the methods on the trait + methods on
683-
all parent traits does not work, because some methods can
684-
be not object safe and thus excluded from the vtable.
685-
Increase this counter if you tried to implement this but
686-
failed to do it without duplicating a lot of code from
687-
other places in the compiler: 2
688-
tcx.mk_tup(&[
689-
tcx.mk_array(tcx.types.usize, 3),
690-
tcx.mk_array(Option<fn()>),
691-
])
692-
*/
673+
let mk_dyn_vtable = || {
674+
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
675+
/* FIXME: use actual fn pointers
676+
Warning: naively computing the number of entries in the
677+
vtable by counting the methods on the trait + methods on
678+
all parent traits does not work, because some methods can
679+
be not object safe and thus excluded from the vtable.
680+
Increase this counter if you tried to implement this but
681+
failed to do it without duplicating a lot of code from
682+
other places in the compiler: 2
683+
tcx.mk_tup(&[
684+
tcx.mk_array(tcx.types.usize, 3),
685+
tcx.mk_array(Option<fn()>),
686+
])
687+
*/
688+
};
689+
690+
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
691+
let metadata = tcx.normalize_erasing_regions(
692+
cx.param_env(),
693+
tcx.mk_projection(metadata_def_id, [pointee]),
694+
);
695+
696+
// Map `Metadata = DynMetadata<dyn Trait>` back to a vtable, since it
697+
// offers better information than `std::ptr::metadata::VTable`,
698+
// and we rely on this layout information to trigger a panic in
699+
// `std::mem::uninitialized::<&dyn Trait>()`, for example.
700+
if let ty::Adt(def, substs) = metadata.kind()
701+
&& Some(def.did()) == tcx.lang_items().dyn_metadata()
702+
&& substs.type_at(0).is_trait()
703+
{
704+
mk_dyn_vtable()
705+
} else {
706+
metadata
693707
}
694-
_ => bug!("TyAndLayout::field({:?}): not applicable", this),
695-
}
708+
} else {
709+
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
710+
ty::Slice(_) | ty::Str => tcx.types.usize,
711+
ty::Dynamic(_, _, ty::Dyn) => mk_dyn_vtable(),
712+
_ => bug!("TyAndLayout::field({:?}): not applicable", this),
713+
}
714+
};
715+
716+
TyMaybeWithLayout::Ty(metadata)
696717
}
697718

698719
// Arrays and slices.

‎compiler/rustc_ty_utils/src/layout.rs

+28-8
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,37 @@ fn layout_of_uncached<'tcx>(
155155
}
156156

157157
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
158-
let metadata = match unsized_part.kind() {
159-
ty::Foreign(..) => {
158+
159+
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
160+
let metadata_ty = tcx.normalize_erasing_regions(
161+
param_env,
162+
tcx.mk_projection(metadata_def_id, [pointee]),
163+
);
164+
let metadata_layout = cx.layout_of(metadata_ty)?;
165+
// If the metadata is a 1-zst, then the pointer is thin.
166+
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
160167
return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
161168
}
162-
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
163-
ty::Dynamic(..) => {
164-
let mut vtable = scalar_unit(Pointer);
165-
vtable.valid_range_mut().start = 1;
166-
vtable
169+
170+
let Abi::Scalar(metadata) = metadata_layout.abi else {
171+
return Err(LayoutError::Unknown(unsized_part));
172+
};
173+
metadata
174+
} else {
175+
match unsized_part.kind() {
176+
ty::Foreign(..) => {
177+
return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr)));
178+
}
179+
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
180+
ty::Dynamic(..) => {
181+
let mut vtable = scalar_unit(Pointer);
182+
vtable.valid_range_mut().start = 1;
183+
vtable
184+
}
185+
_ => {
186+
return Err(LayoutError::Unknown(unsized_part));
187+
}
167188
}
168-
_ => return Err(LayoutError::Unknown(unsized_part)),
169189
};
170190

171191
// Effectively a (ptr, meta) tuple.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// check-pass
2+
3+
#![feature(ptr_metadata)]
4+
5+
use std::ptr::Thin;
6+
7+
fn main() {}
8+
9+
fn foo<T: ?Sized + Thin>(t: *const T) -> *const () {
10+
unsafe { std::mem::transmute(t) }
11+
}

0 commit comments

Comments
 (0)
Please sign in to comment.