From d366471e58b4387aa1d644c33c469e268abf7160 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Sat, 29 Oct 2022 15:38:00 +0200
Subject: [PATCH] interpret: fix align_of_val on packed types

---
 .../src/interpret/eval_context.rs             |  9 ++++++++-
 library/core/tests/lib.rs                     |  2 ++
 library/core/tests/mem.rs                     | 20 +++++++++++++++++++
 3 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index d2e0a0dd240..51cfcdb8331 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -598,7 +598,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // the last field).  Can't have foreign types here, how would we
                 // adjust alignment and size for them?
                 let field = layout.field(self, layout.fields.count() - 1);
-                let Some((unsized_size, unsized_align)) = self.size_and_align_of(metadata, &field)? else {
+                let Some((unsized_size, mut unsized_align)) = self.size_and_align_of(metadata, &field)? else {
                     // A field with an extern type. We don't know the actual dynamic size
                     // or the alignment.
                     return Ok(None);
@@ -614,6 +614,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Return the sum of sizes and max of aligns.
                 let size = sized_size + unsized_size; // `Size` addition
 
+                // Packed types ignore the alignment of their fields.
+                if let ty::Adt(def, _) = layout.ty.kind() {
+                    if def.repr().packed() {
+                        unsized_align = sized_align;
+                    }
+                }
+
                 // Choose max of two known alignments (combined value must
                 // be aligned according to more restrictive of the two).
                 let align = sized_align.max(unsized_align);
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 51f858adee1..eda176d9fcb 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -5,6 +5,7 @@
 #![feature(bigint_helper_methods)]
 #![feature(cell_update)]
 #![feature(const_assume)]
+#![feature(const_align_of_val_raw)]
 #![feature(const_black_box)]
 #![feature(const_bool_to_option)]
 #![feature(const_caller_location)]
@@ -42,6 +43,7 @@
 #![feature(try_find)]
 #![feature(inline_const)]
 #![feature(is_sorted)]
+#![feature(layout_for_ptr)]
 #![feature(pattern)]
 #![feature(pin_macro)]
 #![feature(sort_internals)]
diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs
index 0362e1c8afb..1cfb4fd9fd1 100644
--- a/library/core/tests/mem.rs
+++ b/library/core/tests/mem.rs
@@ -1,4 +1,5 @@
 use core::mem::*;
+use core::ptr;
 
 #[cfg(panic = "unwind")]
 use std::rc::Rc;
@@ -75,6 +76,25 @@ fn align_of_val_basic() {
     assert_eq!(align_of_val(&1u32), 4);
 }
 
+#[test]
+#[cfg(not(bootstrap))] // stage 0 doesn't have the fix yet, so the test fails
+fn align_of_val_raw_packed() {
+    #[repr(C, packed)]
+    struct B {
+        f: [u32],
+    }
+    let storage = [0u8; 4];
+    let b: *const B = ptr::from_raw_parts(storage.as_ptr().cast(), 1);
+    assert_eq!(unsafe { align_of_val_raw(b) }, 1);
+
+    const ALIGN_OF_VAL_RAW: usize = {
+        let storage = [0u8; 4];
+        let b: *const B = ptr::from_raw_parts(storage.as_ptr().cast(), 1);
+        unsafe { align_of_val_raw(b) }
+    };
+    assert_eq!(ALIGN_OF_VAL_RAW, 1);
+}
+
 #[test]
 fn test_swap() {
     let mut x = 31337;