1
Fork 0

transmute size check: properly account for alignment

This commit is contained in:
Ralf Jung 2024-05-29 22:07:56 +02:00
parent a83f933a9d
commit 9f0c7f00d1
6 changed files with 69 additions and 12 deletions

View file

@ -308,7 +308,8 @@ impl<'tcx> LayoutCalculator for LayoutCx<'tcx, TyCtxt<'tcx>> {
#[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.
@ -338,7 +339,12 @@ impl<'tcx> SizeSkeleton<'tcx> {
// 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
@ -390,19 +396,20 @@ impl<'tcx> SizeSkeleton<'tcx> {
{
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)))
}
@ -428,8 +435,10 @@ impl<'tcx> SizeSkeleton<'tcx> {
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);
}
}
@ -493,7 +502,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
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
}