Auto merge of #125740 - RalfJung:transmute-size-check, r=oli-obk
transmute size check: properly account for alignment Fixes another place where ZST alignment was ignored when checking whether something is a newtype. I wonder how many more of these there are... Fixes https://github.com/rust-lang/rust/issues/101084
This commit is contained in:
commit
bda221a0eb
@ -73,7 +73,7 @@ pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
|
||||
// Special-case transmuting from `typeof(function)` and
|
||||
// `Option<typeof(function)>` to present a clearer error.
|
||||
let from = unpack_option_like(tcx, from);
|
||||
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to)
|
||||
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to)
|
||||
&& size_to == Pointer(dl.instruction_address_space).size(&tcx)
|
||||
{
|
||||
struct_span_code_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type")
|
||||
@ -88,7 +88,7 @@ pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
|
||||
// Try to display a sensible error with as much information as possible.
|
||||
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
|
||||
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
|
||||
Ok(SizeSkeleton::Known(size)) => {
|
||||
Ok(SizeSkeleton::Known(size, _)) => {
|
||||
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
|
||||
format!("{v} bits")
|
||||
} else {
|
||||
|
@ -309,7 +309,8 @@ fn current_data_layout(&self) -> Self::TargetDataLayoutRef {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum SizeSkeleton<'tcx> {
|
||||
/// Any statically computable Layout.
|
||||
Known(Size),
|
||||
/// Alignment can be `None` if unknown.
|
||||
Known(Size, Option<Align>),
|
||||
|
||||
/// This is a generic const expression (i.e. N * 2), which may contain some parameters.
|
||||
/// It must be of type usize, and represents the size of a type in bytes.
|
||||
@ -339,7 +340,12 @@ pub fn compute(
|
||||
// First try computing a static layout.
|
||||
let err = match tcx.layout_of(param_env.and(ty)) {
|
||||
Ok(layout) => {
|
||||
return Ok(SizeSkeleton::Known(layout.size));
|
||||
if layout.abi.is_sized() {
|
||||
return Ok(SizeSkeleton::Known(layout.size, Some(layout.align.abi)));
|
||||
} else {
|
||||
// Just to be safe, don't claim a known layout for unsized types.
|
||||
return Err(tcx.arena.alloc(LayoutError::Unknown(ty)));
|
||||
}
|
||||
}
|
||||
Err(err @ LayoutError::Unknown(_)) => err,
|
||||
// We can't extract SizeSkeleton info from other layout errors
|
||||
@ -389,19 +395,20 @@ pub fn compute(
|
||||
ty::Array(inner, len) if tcx.features().transmute_generic_consts => {
|
||||
let len_eval = len.try_eval_target_usize(tcx, param_env);
|
||||
if len_eval == Some(0) {
|
||||
return Ok(SizeSkeleton::Known(Size::from_bytes(0)));
|
||||
return Ok(SizeSkeleton::Known(Size::from_bytes(0), None));
|
||||
}
|
||||
|
||||
match SizeSkeleton::compute(inner, tcx, param_env)? {
|
||||
// This may succeed because the multiplication of two types may overflow
|
||||
// but a single size of a nested array will not.
|
||||
SizeSkeleton::Known(s) => {
|
||||
SizeSkeleton::Known(s, a) => {
|
||||
if let Some(c) = len_eval {
|
||||
let size = s
|
||||
.bytes()
|
||||
.checked_mul(c)
|
||||
.ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?;
|
||||
return Ok(SizeSkeleton::Known(Size::from_bytes(size)));
|
||||
// Alignment is unchanged by arrays.
|
||||
return Ok(SizeSkeleton::Known(Size::from_bytes(size), a));
|
||||
}
|
||||
Err(tcx.arena.alloc(LayoutError::Unknown(ty)))
|
||||
}
|
||||
@ -427,8 +434,10 @@ pub fn compute(
|
||||
for field in fields {
|
||||
let field = field?;
|
||||
match field {
|
||||
SizeSkeleton::Known(size) => {
|
||||
if size.bytes() > 0 {
|
||||
SizeSkeleton::Known(size, align) => {
|
||||
let is_1zst = size.bytes() == 0
|
||||
&& align.is_some_and(|align| align.bytes() == 1);
|
||||
if !is_1zst {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
@ -492,7 +501,7 @@ pub fn compute(
|
||||
|
||||
pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
|
||||
match (self, other) {
|
||||
(SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b,
|
||||
(SizeSkeleton::Known(a, _), SizeSkeleton::Known(b, _)) => a == b,
|
||||
(SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
|
||||
a == b
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ LL | const C: S = unsafe { std::mem::transmute(()) };
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: source type: `()` (0 bits)
|
||||
= note: target type: `S` (this type does not have a fixed size)
|
||||
= note: target type: `S` (size can vary because of [u8])
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -28,4 +28,24 @@ unsafe fn specializable<T>(x: u16) -> <T as Specializable>::Output {
|
||||
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
|
||||
}
|
||||
|
||||
#[repr(align(32))]
|
||||
struct OverAlignZST;
|
||||
pub struct PtrAndOverAlignZST<T: ?Sized> {
|
||||
_inner: *mut T,
|
||||
_other: OverAlignZST,
|
||||
}
|
||||
pub unsafe fn shouldnt_work<T: ?Sized>(from: *mut T) -> PtrAndOverAlignZST<T> {
|
||||
transmute(from)
|
||||
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
|
||||
}
|
||||
|
||||
pub struct PtrAndEmptyArray<T: ?Sized> {
|
||||
_inner: *mut T,
|
||||
_other: [*mut T; 0],
|
||||
}
|
||||
pub unsafe fn shouldnt_work2<T: ?Sized>(from: *mut T) -> PtrAndEmptyArray<T> {
|
||||
std::mem::transmute(from)
|
||||
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -25,6 +25,24 @@ LL | transmute(x)
|
||||
= note: source type: `u16` (N bits)
|
||||
= note: target type: `<T as Specializable>::Output` (this type does not have a fixed size)
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
|
||||
--> $DIR/transmute-different-sizes.rs:38:5
|
||||
|
|
||||
LL | transmute(from)
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: source type: `*mut T` (pointer to `T`)
|
||||
= note: target type: `PtrAndOverAlignZST<T>` (size can vary because of <T as Pointee>::Metadata)
|
||||
|
||||
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
|
||||
--> $DIR/transmute-different-sizes.rs:47:5
|
||||
|
|
||||
LL | std::mem::transmute(from)
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: source type: `*mut T` (pointer to `T`)
|
||||
= note: target type: `PtrAndEmptyArray<T>` (size can vary because of <T as Pointee>::Metadata)
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0512`.
|
||||
|
@ -24,11 +24,21 @@
|
||||
::std::mem::transmute::<(), [[T; 0]; 8]>(from)
|
||||
}
|
||||
|
||||
// Verify transmute with an extra ZST field
|
||||
pub struct PtrAndZst<T: ?Sized> {
|
||||
_inner: *mut T,
|
||||
_other: (),
|
||||
}
|
||||
pub unsafe fn cast_ptr<T: ?Sized>(from: *mut T) -> PtrAndZst<T> {
|
||||
std::mem::transmute(from)
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
let _: [u32; 0] = cast_zst0(());
|
||||
let _ = cast_zst1::<u32>([]);
|
||||
let _: [(u32, u32); 0] = cast_zst2(());
|
||||
let _: [[u32; 0]; 8] = cast_zst3(());
|
||||
cast_ptr(&mut 42);
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user