Skip to content

Commit e78bfc3

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#101084
2 parents 7a6e41b + 9f0c7f0 commit e78bfc3

File tree

6 files changed

+69
-12
lines changed

6 files changed

+69
-12
lines changed

compiler/rustc_hir_typeck/src/intrinsicck.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7373
// Special-case transmuting from `typeof(function)` and
7474
// `Option<typeof(function)>` to present a clearer error.
7575
let from = unpack_option_like(tcx, from);
76-
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to)
76+
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to)
7777
&& size_to == Pointer(dl.instruction_address_space).size(&tcx)
7878
{
7979
struct_span_code_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type")
@@ -88,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8888
// Try to display a sensible error with as much information as possible.
8989
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
9090
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
91-
Ok(SizeSkeleton::Known(size)) => {
91+
Ok(SizeSkeleton::Known(size, _)) => {
9292
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
9393
format!("{v} bits")
9494
} else {

compiler/rustc_middle/src/ty/layout.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ impl<'tcx> LayoutCalculator for LayoutCx<'tcx, TyCtxt<'tcx>> {
309309
#[derive(Copy, Clone, Debug)]
310310
pub enum SizeSkeleton<'tcx> {
311311
/// Any statically computable Layout.
312-
Known(Size),
312+
/// Alignment can be `None` if unknown.
313+
Known(Size, Option<Align>),
313314

314315
/// This is a generic const expression (i.e. N * 2), which may contain some parameters.
315316
/// It must be of type usize, and represents the size of a type in bytes.
@@ -339,7 +340,12 @@ impl<'tcx> SizeSkeleton<'tcx> {
339340
// First try computing a static layout.
340341
let err = match tcx.layout_of(param_env.and(ty)) {
341342
Ok(layout) => {
342-
return Ok(SizeSkeleton::Known(layout.size));
343+
if layout.abi.is_sized() {
344+
return Ok(SizeSkeleton::Known(layout.size, Some(layout.align.abi)));
345+
} else {
346+
// Just to be safe, don't claim a known layout for unsized types.
347+
return Err(tcx.arena.alloc(LayoutError::Unknown(ty)));
348+
}
343349
}
344350
Err(err @ LayoutError::Unknown(_)) => err,
345351
// We can't extract SizeSkeleton info from other layout errors
@@ -389,19 +395,20 @@ impl<'tcx> SizeSkeleton<'tcx> {
389395
ty::Array(inner, len) if tcx.features().transmute_generic_consts => {
390396
let len_eval = len.try_eval_target_usize(tcx, param_env);
391397
if len_eval == Some(0) {
392-
return Ok(SizeSkeleton::Known(Size::from_bytes(0)));
398+
return Ok(SizeSkeleton::Known(Size::from_bytes(0), None));
393399
}
394400

395401
match SizeSkeleton::compute(inner, tcx, param_env)? {
396402
// This may succeed because the multiplication of two types may overflow
397403
// but a single size of a nested array will not.
398-
SizeSkeleton::Known(s) => {
404+
SizeSkeleton::Known(s, a) => {
399405
if let Some(c) = len_eval {
400406
let size = s
401407
.bytes()
402408
.checked_mul(c)
403409
.ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?;
404-
return Ok(SizeSkeleton::Known(Size::from_bytes(size)));
410+
// Alignment is unchanged by arrays.
411+
return Ok(SizeSkeleton::Known(Size::from_bytes(size), a));
405412
}
406413
Err(tcx.arena.alloc(LayoutError::Unknown(ty)))
407414
}
@@ -427,8 +434,10 @@ impl<'tcx> SizeSkeleton<'tcx> {
427434
for field in fields {
428435
let field = field?;
429436
match field {
430-
SizeSkeleton::Known(size) => {
431-
if size.bytes() > 0 {
437+
SizeSkeleton::Known(size, align) => {
438+
let is_1zst = size.bytes() == 0
439+
&& align.is_some_and(|align| align.bytes() == 1);
440+
if !is_1zst {
432441
return Err(err);
433442
}
434443
}
@@ -492,7 +501,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
492501

493502
pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
494503
match (self, other) {
495-
(SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b,
504+
(SizeSkeleton::Known(a, _), SizeSkeleton::Known(b, _)) => a == b,
496505
(SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
497506
a == b
498507
}

tests/ui/layout/base-layout-is-sized-ice-123078.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ LL | const C: S = unsafe { std::mem::transmute(()) };
2323
| ^^^^^^^^^^^^^^^^^^^
2424
|
2525
= note: source type: `()` (0 bits)
26-
= note: target type: `S` (this type does not have a fixed size)
26+
= note: target type: `S` (size can vary because of [u8])
2727

2828
error: aborting due to 2 previous errors
2929

tests/ui/transmute/transmute-different-sizes.rs

+20
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,24 @@ unsafe fn specializable<T>(x: u16) -> <T as Specializable>::Output {
2828
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
2929
}
3030

31+
#[repr(align(32))]
32+
struct OverAlignZST;
33+
pub struct PtrAndOverAlignZST<T: ?Sized> {
34+
_inner: *mut T,
35+
_other: OverAlignZST,
36+
}
37+
pub unsafe fn shouldnt_work<T: ?Sized>(from: *mut T) -> PtrAndOverAlignZST<T> {
38+
transmute(from)
39+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
40+
}
41+
42+
pub struct PtrAndEmptyArray<T: ?Sized> {
43+
_inner: *mut T,
44+
_other: [*mut T; 0],
45+
}
46+
pub unsafe fn shouldnt_work2<T: ?Sized>(from: *mut T) -> PtrAndEmptyArray<T> {
47+
std::mem::transmute(from)
48+
//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
49+
}
50+
3151
fn main() {}

tests/ui/transmute/transmute-different-sizes.stderr

+19-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ LL | transmute(x)
2525
= note: source type: `u16` (N bits)
2626
= note: target type: `<T as Specializable>::Output` (this type does not have a fixed size)
2727

28-
error: aborting due to 3 previous errors
28+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
29+
--> $DIR/transmute-different-sizes.rs:38:5
30+
|
31+
LL | transmute(from)
32+
| ^^^^^^^^^
33+
|
34+
= note: source type: `*mut T` (pointer to `T`)
35+
= note: target type: `PtrAndOverAlignZST<T>` (size can vary because of <T as Pointee>::Metadata)
36+
37+
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
38+
--> $DIR/transmute-different-sizes.rs:47:5
39+
|
40+
LL | std::mem::transmute(from)
41+
| ^^^^^^^^^^^^^^^^^^^
42+
|
43+
= note: source type: `*mut T` (pointer to `T`)
44+
= note: target type: `PtrAndEmptyArray<T>` (size can vary because of <T as Pointee>::Metadata)
45+
46+
error: aborting due to 5 previous errors
2947

3048
For more information about this error, try `rustc --explain E0512`.

tests/ui/transmute/transmute-zst-generics.rs

+10
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,21 @@ unsafe fn cast_zst3<T>(from: ()) -> [[T; 0]; 8] {
2424
::std::mem::transmute::<(), [[T; 0]; 8]>(from)
2525
}
2626

27+
// Verify transmute with an extra ZST field
28+
pub struct PtrAndZst<T: ?Sized> {
29+
_inner: *mut T,
30+
_other: (),
31+
}
32+
pub unsafe fn cast_ptr<T: ?Sized>(from: *mut T) -> PtrAndZst<T> {
33+
std::mem::transmute(from)
34+
}
35+
2736
pub fn main() {
2837
unsafe {
2938
let _: [u32; 0] = cast_zst0(());
3039
let _ = cast_zst1::<u32>([]);
3140
let _: [(u32, u32); 0] = cast_zst2(());
3241
let _: [[u32; 0]; 8] = cast_zst3(());
42+
cast_ptr(&mut 42);
3343
};
3444
}

0 commit comments

Comments
 (0)