about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-25 07:21:17 +0000
committerbors <bors@rust-lang.org>2024-06-25 07:21:17 +0000
commitbda221a0eb5019a939e34e0f4583f1c4b6e5b862 (patch)
treec13a3c138206e7a48137622356f26346d52fabbd
parent164e1297e1bce47a241e4d93a9f044452b288715 (diff)
parent9f0c7f00d1d705435fef309576e0522cb0c25497 (diff)
downloadrust-bda221a0eb5019a939e34e0f4583f1c4b6e5b862.tar.gz
rust-bda221a0eb5019a939e34e0f4583f1c4b6e5b862.zip
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
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs4
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs25
-rw-r--r--tests/ui/layout/base-layout-is-sized-ice-123078.stderr2
-rw-r--r--tests/ui/transmute/transmute-different-sizes.rs20
-rw-r--r--tests/ui/transmute/transmute-different-sizes.stderr20
-rw-r--r--tests/ui/transmute/transmute-zst-generics.rs10
6 files changed, 69 insertions, 12 deletions
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index fb8863c143f..5eafc60a04e 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // 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 {
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index f608b02f42c..eb25aecd9ce 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -309,7 +309,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.
@@ -339,7 +340,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
@@ -389,19 +395,20 @@ impl<'tcx> SizeSkeleton<'tcx> {
             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 @@ 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);
                                 }
                             }
@@ -492,7 +501,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
             }
diff --git a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr
index 7e0a41a4367..455bd2cbf8b 100644
--- a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr
+++ b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr
@@ -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
 
diff --git a/tests/ui/transmute/transmute-different-sizes.rs b/tests/ui/transmute/transmute-different-sizes.rs
index 4fe79b9fa4e..ac98eb231dd 100644
--- a/tests/ui/transmute/transmute-different-sizes.rs
+++ b/tests/ui/transmute/transmute-different-sizes.rs
@@ -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() {}
diff --git a/tests/ui/transmute/transmute-different-sizes.stderr b/tests/ui/transmute/transmute-different-sizes.stderr
index 07a38df6973..ea3a017c16e 100644
--- a/tests/ui/transmute/transmute-different-sizes.stderr
+++ b/tests/ui/transmute/transmute-different-sizes.stderr
@@ -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`.
diff --git a/tests/ui/transmute/transmute-zst-generics.rs b/tests/ui/transmute/transmute-zst-generics.rs
index 9aeb21923ea..b80ec794f83 100644
--- a/tests/ui/transmute/transmute-zst-generics.rs
+++ b/tests/ui/transmute/transmute-zst-generics.rs
@@ -24,11 +24,21 @@ unsafe fn cast_zst3<T>(from: ()) -> [[T; 0]; 8] {
     ::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);
     };
 }