about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-12-02 14:24:17 +0100
committerRalf Jung <post@ralfj.de>2023-12-03 08:26:51 +0100
commitef15a8182ba0b6b7b5d6abb9d00eeb93f7cf0247 (patch)
treef20bc84e992f652ea03aabd550ed580b7702ae40
parent0919ad18381f6f4fcaddc809e786553e028bbde0 (diff)
downloadrust-ef15a8182ba0b6b7b5d6abb9d00eeb93f7cf0247.tar.gz
rust-ef15a8182ba0b6b7b5d6abb9d00eeb93f7cf0247.zip
codegen, miri: fix computing the offset of an unsized field in a packed struct
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs22
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs12
-rw-r--r--src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs35
-rw-r--r--src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs38
-rw-r--r--tests/ui/packed/issue-118537-field-offset-ice.rs39
-rw-r--r--tests/ui/packed/issue-118537-field-offset.rs36
6 files changed, 171 insertions, 11 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 45795a7f735..83425dee1a8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -143,7 +143,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         // Simple cases, which don't need DST adjustment:
         //   * no metadata available - just log the case
         //   * known alignment - sized types, `[T]`, `str` or a foreign type
-        //   * packed struct - there is no alignment padding
+        // Note that looking at `field.align` is incorrect since that is not necessarily equal
+        // to the dynamic alignment of the type.
         match field.ty.kind() {
             _ if self.llextra.is_none() => {
                 debug!(
@@ -154,14 +155,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
             }
             _ if field.is_sized() => return simple(),
             ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
-            ty::Adt(def, _) => {
-                if def.repr().packed() {
-                    // FIXME(eddyb) generalize the adjustment when we
-                    // start supporting packing to larger alignments.
-                    assert_eq!(self.layout.align.abi.bytes(), 1);
-                    return simple();
-                }
-            }
             _ => {}
         }
 
@@ -186,7 +179,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         let unaligned_offset = bx.cx().const_usize(offset.bytes());
 
         // Get the alignment of the field
-        let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
+        let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
+
+        // For packed types, we need to cap alignment.
+        if let ty::Adt(def, _) = self.layout.ty.kind()
+            && let Some(packed) = def.repr().pack
+        {
+            let packed = bx.const_usize(packed.bytes());
+            let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
+            unsized_align = bx.select(cmp, unsized_align, packed)
+        }
 
         // Bump the unaligned offset up to the appropriate alignment
         let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align);
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index c8977aac0fc..4d9e296d544 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -163,7 +163,17 @@ where
             // With custom DSTS, this *will* execute user-defined code, but the same
             // happens at run-time so that's okay.
             match self.size_and_align_of(&base_meta, &field_layout)? {
-                Some((_, align)) => (base_meta, offset.align_to(align)),
+                Some((_, align)) => {
+                    // For packed types, we need to cap alignment.
+                    let align = if let ty::Adt(def, _) = base.layout().ty.kind()
+                        && let Some(packed) = def.repr().pack
+                    {
+                        align.min(packed)
+                    } else {
+                        align
+                    };
+                    (base_meta, offset.align_to(align))
+                }
                 None => {
                     // For unsized types with an extern type tail we perform no adjustments.
                     // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
diff --git a/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs b/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs
new file mode 100644
index 00000000000..e58fe60b5f6
--- /dev/null
+++ b/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs
@@ -0,0 +1,35 @@
+#![feature(layout_for_ptr)]
+use std::mem;
+
+#[repr(packed, C)]
+struct PackedSized {
+    f: u8,
+    d: [u32; 4],
+}
+
+#[repr(packed, C)]
+struct PackedUnsized {
+    f: u8,
+    d: [u32],
+}
+
+impl PackedSized {
+    fn unsize(&self) -> &PackedUnsized {
+        // We can't unsize via a generic type since then we get the error
+        // that packed structs with unsized tail don't work if the tail
+        // might need dropping.
+        let len = 4usize;
+        unsafe { mem::transmute((self, len)) }
+    }
+}
+
+fn main() { unsafe {
+    let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
+    let p = p.unsize() as *const PackedUnsized;
+    // Make sure the size computation does *not* think there is
+    // any padding in front of the `d` field.
+    assert_eq!(mem::size_of_val_raw(p), 1 + 4*4);
+    // And likewise for the offset computation.
+    let d = std::ptr::addr_of!((*p).d);
+    assert_eq!(d.cast::<u32>().read_unaligned(), 1);
+} }
diff --git a/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs b/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs
new file mode 100644
index 00000000000..910b5d94320
--- /dev/null
+++ b/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs
@@ -0,0 +1,38 @@
+#![feature(layout_for_ptr)]
+use std::mem;
+
+#[repr(packed(4))]
+struct Slice([u32]);
+
+#[repr(packed(2), C)]
+struct PackedSized {
+    f: u8,
+    d: [u32; 4],
+}
+
+#[repr(packed(2), C)]
+struct PackedUnsized {
+    f: u8,
+    d: Slice,
+}
+
+impl PackedSized {
+    fn unsize(&self) -> &PackedUnsized {
+        // We can't unsize via a generic type since then we get the error
+        // that packed structs with unsized tail don't work if the tail
+        // might need dropping.
+        let len = 4usize;
+        unsafe { mem::transmute((self, len)) }
+    }
+}
+
+fn main() { unsafe {
+    let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
+    let p = p.unsize() as *const PackedUnsized;
+    // Make sure the size computation correctly adds exact 1 byte of padding
+    // in front of the `d` field.
+    assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4*4);
+    // And likewise for the offset computation.
+    let d = std::ptr::addr_of!((*p).d);
+    assert_eq!(d.cast::<u32>().read_unaligned(), 1);
+} }
diff --git a/tests/ui/packed/issue-118537-field-offset-ice.rs b/tests/ui/packed/issue-118537-field-offset-ice.rs
new file mode 100644
index 00000000000..657aec64003
--- /dev/null
+++ b/tests/ui/packed/issue-118537-field-offset-ice.rs
@@ -0,0 +1,39 @@
+// run-pass
+#![feature(layout_for_ptr)]
+use std::mem;
+
+#[repr(packed(4))]
+struct Slice([u32]);
+
+#[repr(packed(2), C)]
+struct PackedSized {
+    f: u8,
+    d: [u32; 4],
+}
+
+#[repr(packed(2), C)]
+struct PackedUnsized {
+    f: u8,
+    d: Slice,
+}
+
+impl PackedSized {
+    fn unsize(&self) -> &PackedUnsized {
+        // We can't unsize via a generic type since then we get the error
+        // that packed structs with unsized tail don't work if the tail
+        // might need dropping.
+        let len = 4usize;
+        unsafe { mem::transmute((self, len)) }
+    }
+}
+
+fn main() { unsafe {
+    let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
+    let p = p.unsize() as *const PackedUnsized;
+    // Make sure the size computation correctly adds exact 1 byte of padding
+    // in front of the `d` field.
+    assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4*4);
+    // And likewise for the offset computation.
+    let d = std::ptr::addr_of!((*p).d);
+    assert_eq!(d.cast::<u32>().read_unaligned(), 1);
+} }
diff --git a/tests/ui/packed/issue-118537-field-offset.rs b/tests/ui/packed/issue-118537-field-offset.rs
new file mode 100644
index 00000000000..cd17f767947
--- /dev/null
+++ b/tests/ui/packed/issue-118537-field-offset.rs
@@ -0,0 +1,36 @@
+// run-pass
+#![feature(layout_for_ptr)]
+use std::mem;
+
+#[repr(packed, C)]
+struct PackedSized {
+    f: u8,
+    d: [u32; 4],
+}
+
+#[repr(packed, C)]
+struct PackedUnsized {
+    f: u8,
+    d: [u32],
+}
+
+impl PackedSized {
+    fn unsize(&self) -> &PackedUnsized {
+        // We can't unsize via a generic type since then we get the error
+        // that packed structs with unsized tail don't work if the tail
+        // might need dropping.
+        let len = 4usize;
+        unsafe { mem::transmute((self, len)) }
+    }
+}
+
+fn main() { unsafe {
+    let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
+    let p = p.unsize() as *const PackedUnsized;
+    // Make sure the size computation does *not* think there is
+    // any padding in front of the `d` field.
+    assert_eq!(mem::size_of_val_raw(p), 1 + 4*4);
+    // And likewise for the offset computation.
+    let d = std::ptr::addr_of!((*p).d);
+    assert_eq!(d.cast::<u32>().read_unaligned(), 1);
+} }