about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_abi/src/layout.rs8
-rw-r--r--tests/ui/layout/issue-112048-unsizing-field-order.rs25
2 files changed, 31 insertions, 2 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index f11c1c77f9c..6f22345617d 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -828,6 +828,7 @@ fn univariant(
     if optimize && fields.len() > 1 {
         let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
         let optimizing = &mut inverse_memory_index.raw[..end];
+        let fields_excluding_tail = &fields.raw[..end];
 
         // If `-Z randomize-layout` was enabled for the type definition we can shuffle
         // the field ordering to try and catch some code making assumptions about layouts
@@ -844,8 +845,11 @@ fn univariant(
             }
             // Otherwise we just leave things alone and actually optimize the type's fields
         } else {
-            let max_field_align = fields.iter().map(|f| f.align().abi.bytes()).max().unwrap_or(1);
-            let largest_niche_size = fields
+            // To allow unsizing `&Foo<Type>` -> `&Foo<dyn Trait>`, the layout of the struct must
+            // not depend on the layout of the tail.
+            let max_field_align =
+                fields_excluding_tail.iter().map(|f| f.align().abi.bytes()).max().unwrap_or(1);
+            let largest_niche_size = fields_excluding_tail
                 .iter()
                 .filter_map(|f| f.largest_niche())
                 .map(|n| n.available(dl))
diff --git a/tests/ui/layout/issue-112048-unsizing-field-order.rs b/tests/ui/layout/issue-112048-unsizing-field-order.rs
new file mode 100644
index 00000000000..ebc4b9e98b7
--- /dev/null
+++ b/tests/ui/layout/issue-112048-unsizing-field-order.rs
@@ -0,0 +1,25 @@
+// run-pass
+
+// Check that unsizing doesn't reorder fields.
+
+#![allow(dead_code)]
+
+use std::fmt::Debug;
+
+#[derive(Debug)]
+struct GcNode<T: ?Sized> {
+    gets_swapped_with_next: usize,
+    next: Option<&'static GcNode<dyn Debug>>,
+    tail: T,
+}
+
+fn main() {
+    let node: Box<GcNode<dyn Debug>> = Box::new(GcNode {
+        gets_swapped_with_next: 42,
+        next: None,
+        tail: Box::new(1),
+    });
+
+    assert_eq!(node.gets_swapped_with_next, 42);
+    assert!(node.next.is_none());
+}