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
|
// Special-case transmuting from `typeof(function)` and
|
||||||
// `Option<typeof(function)>` to present a clearer error.
|
// `Option<typeof(function)>` to present a clearer error.
|
||||||
let from = unpack_option_like(tcx, from);
|
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)
|
&& size_to == Pointer(dl.instruction_address_space).size(&tcx)
|
||||||
{
|
{
|
||||||
struct_span_code_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type")
|
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.
|
// Try to display a sensible error with as much information as possible.
|
||||||
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
|
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
|
||||||
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
|
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) {
|
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
|
||||||
format!("{v} bits")
|
format!("{v} bits")
|
||||||
} else {
|
} else {
|
||||||
|
@ -309,7 +309,8 @@ fn current_data_layout(&self) -> Self::TargetDataLayoutRef {
|
|||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum SizeSkeleton<'tcx> {
|
pub enum SizeSkeleton<'tcx> {
|
||||||
/// Any statically computable Layout.
|
/// 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.
|
/// 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.
|
/// 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.
|
// First try computing a static layout.
|
||||||
let err = match tcx.layout_of(param_env.and(ty)) {
|
let err = match tcx.layout_of(param_env.and(ty)) {
|
||||||
Ok(layout) => {
|
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,
|
Err(err @ LayoutError::Unknown(_)) => err,
|
||||||
// We can't extract SizeSkeleton info from other layout errors
|
// 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 => {
|
ty::Array(inner, len) if tcx.features().transmute_generic_consts => {
|
||||||
let len_eval = len.try_eval_target_usize(tcx, param_env);
|
let len_eval = len.try_eval_target_usize(tcx, param_env);
|
||||||
if len_eval == Some(0) {
|
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)? {
|
match SizeSkeleton::compute(inner, tcx, param_env)? {
|
||||||
// This may succeed because the multiplication of two types may overflow
|
// This may succeed because the multiplication of two types may overflow
|
||||||
// but a single size of a nested array will not.
|
// but a single size of a nested array will not.
|
||||||
SizeSkeleton::Known(s) => {
|
SizeSkeleton::Known(s, a) => {
|
||||||
if let Some(c) = len_eval {
|
if let Some(c) = len_eval {
|
||||||
let size = s
|
let size = s
|
||||||
.bytes()
|
.bytes()
|
||||||
.checked_mul(c)
|
.checked_mul(c)
|
||||||
.ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?;
|
.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)))
|
Err(tcx.arena.alloc(LayoutError::Unknown(ty)))
|
||||||
}
|
}
|
||||||
@ -427,8 +434,10 @@ pub fn compute(
|
|||||||
for field in fields {
|
for field in fields {
|
||||||
let field = field?;
|
let field = field?;
|
||||||
match field {
|
match field {
|
||||||
SizeSkeleton::Known(size) => {
|
SizeSkeleton::Known(size, align) => {
|
||||||
if size.bytes() > 0 {
|
let is_1zst = size.bytes() == 0
|
||||||
|
&& align.is_some_and(|align| align.bytes() == 1);
|
||||||
|
if !is_1zst {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -492,7 +501,7 @@ pub fn compute(
|
|||||||
|
|
||||||
pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
|
pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
|
||||||
match (self, other) {
|
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, .. }) => {
|
(SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
|
||||||
a == b
|
a == b
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ LL | const C: S = unsafe { std::mem::transmute(()) };
|
|||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: source type: `()` (0 bits)
|
= 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
|
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
|
//~^ 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() {}
|
fn main() {}
|
||||||
|
@ -25,6 +25,24 @@ LL | transmute(x)
|
|||||||
= note: source type: `u16` (N bits)
|
= note: source type: `u16` (N bits)
|
||||||
= note: target type: `<T as Specializable>::Output` (this type does not have a fixed size)
|
= 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`.
|
For more information about this error, try `rustc --explain E0512`.
|
||||||
|
@ -24,11 +24,21 @@
|
|||||||
::std::mem::transmute::<(), [[T; 0]; 8]>(from)
|
::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() {
|
pub fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: [u32; 0] = cast_zst0(());
|
let _: [u32; 0] = cast_zst0(());
|
||||||
let _ = cast_zst1::<u32>([]);
|
let _ = cast_zst1::<u32>([]);
|
||||||
let _: [(u32, u32); 0] = cast_zst2(());
|
let _: [(u32, u32); 0] = cast_zst2(());
|
||||||
let _: [[u32; 0]; 8] = cast_zst3(());
|
let _: [[u32; 0]; 8] = cast_zst3(());
|
||||||
|
cast_ptr(&mut 42);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user