about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2019-01-09 15:16:32 -0500
committerNiko Matsakis <niko@alum.mit.edu>2019-01-25 10:03:47 -0500
commit8e4c57fca2f4bae61f7b567e7c72f96245f385bc (patch)
treeafbf7a76111a3a425c837171709eda9359c65e41 /src
parent01f8e25b15f4ab157c8e7c9c56054df7595ec0e1 (diff)
downloadrust-8e4c57fca2f4bae61f7b567e7c72f96245f385bc.tar.gz
rust-8e4c57fca2f4bae61f7b567e7c72f96245f385bc.zip
distinguish "no data" from "heterogeneous" for ABI purposes
Also, add a testing infrastructure and tests that lets us dump layout.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_driver/driver.rs5
-rw-r--r--src/librustc_passes/layout_test.rs132
-rw-r--r--src/librustc_passes/lib.rs1
-rw-r--r--src/librustc_target/abi/call/aarch64.rs2
-rw-r--r--src/librustc_target/abi/call/arm.rs2
-rw-r--r--src/librustc_target/abi/call/asmjs.rs2
-rw-r--r--src/librustc_target/abi/call/mod.rs79
-rw-r--r--src/librustc_target/abi/call/powerpc64.rs2
-rw-r--r--src/librustc_target/abi/call/sparc64.rs2
-rw-r--r--src/librustc_target/abi/call/x86.rs2
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs36
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr14
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs73
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr32
-rw-r--r--src/test/ui/layout/zero-sized-array-union.rs95
-rw-r--r--src/test/ui/layout/zero-sized-array-union.stderr26
17 files changed, 492 insertions, 20 deletions
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 142d60c8d00..55f9d8a1109 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -22,7 +22,7 @@ use rustc_incremental;
 use rustc_metadata::creader::CrateLoader;
 use rustc_metadata::cstore::{self, CStore};
 use rustc_mir as mir;
-use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion};
+use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test};
 use rustc_plugin as plugin;
 use rustc_plugin::registry::Registry;
 use rustc_privacy;
@@ -1287,6 +1287,9 @@ where
                     mir::transform::check_unsafety::check_unsafety(tcx, def_id)
                 }
             });
+
+            time(sess, "layout testing", || layout_test::test_layout(tcx));
+
             // Avoid overwhelming user with errors if type checking failed.
             // I'm not sure how helpful this is, to be honest, but it avoids
             // a
diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs
new file mode 100644
index 00000000000..d21707c578b
--- /dev/null
+++ b/src/librustc_passes/layout_test.rs
@@ -0,0 +1,132 @@
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::hir::itemlikevisit::ItemLikeVisitor;
+use rustc::hir::ItemKind;
+use rustc::ty::layout::HasDataLayout;
+use rustc::ty::layout::HasTyCtxt;
+use rustc::ty::layout::LayoutOf;
+use rustc::ty::layout::TargetDataLayout;
+use rustc::ty::layout::TyLayout;
+use rustc::ty::ParamEnv;
+use rustc::ty::Ty;
+use rustc::ty::TyCtxt;
+use syntax::ast::Attribute;
+
+pub fn test_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+    if tcx.features().rustc_attrs {
+        // if the `rustc_attrs` feature is not enabled, don't bother testing layout
+        tcx.hir()
+            .krate()
+            .visit_all_item_likes(&mut VarianceTest { tcx });
+    }
+}
+
+struct VarianceTest<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+}
+
+impl<'a, 'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'a, 'tcx> {
+    fn visit_item(&mut self, item: &'tcx hir::Item) {
+        let item_def_id = self.tcx.hir().local_def_id(item.id);
+
+        if let ItemKind::Ty(..) = item.node {
+            for attr in self.tcx.get_attrs(item_def_id).iter() {
+                if attr.check_name("rustc_layout") {
+                    self.dump_layout_of(item_def_id, item, attr);
+                }
+            }
+        }
+    }
+
+    fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
+    fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
+}
+
+impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
+    fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item, attr: &Attribute) {
+        let tcx = self.tcx;
+        let param_env = self.tcx.param_env(item_def_id);
+        let ty = self.tcx.type_of(item_def_id);
+        match self.tcx.layout_of(param_env.and(ty)) {
+            Ok(ty_layout) => {
+                // Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
+                // The `..` are the names of fields to dump.
+                let meta_items = attr.meta_item_list().unwrap_or_default();
+                for meta_item in meta_items {
+                    let name = meta_item.word().map(|mi| mi.name().as_str());
+                    let name = name.as_ref().map(|s| &s[..]).unwrap_or("");
+
+                    match name {
+                        "abi" => {
+                            self.tcx
+                                .sess
+                                .span_err(item.span, &format!("abi: {:?}", ty_layout.abi));
+                        }
+
+                        "align" => {
+                            self.tcx
+                                .sess
+                                .span_err(item.span, &format!("align: {:?}", ty_layout.align));
+                        }
+
+                        "size" => {
+                            self.tcx
+                                .sess
+                                .span_err(item.span, &format!("size: {:?}", ty_layout.size));
+                        }
+
+                        "homogeneous_aggregate" => {
+                            self.tcx.sess.span_err(
+                                item.span,
+                                &format!(
+                                    "homogeneous_aggregate: {:?}",
+                                    ty_layout
+                                        .homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
+                                ),
+                            );
+                        }
+
+                        _ => {
+                            self.tcx.sess.span_err(
+                                meta_item.span,
+                                &format!("unrecognized field name `{}`", name),
+                            );
+                        }
+                    }
+                }
+            }
+
+            Err(layout_error) => {
+                self.tcx
+                    .sess
+                    .span_err(item.span, &format!("layout error: {:?}", layout_error));
+            }
+        }
+    }
+}
+
+struct UnwrapLayoutCx<'me, 'tcx> {
+    tcx: TyCtxt<'me, 'tcx, 'tcx>,
+    param_env: ParamEnv<'tcx>,
+}
+
+impl<'me, 'tcx> LayoutOf for UnwrapLayoutCx<'me, 'tcx> {
+    type Ty = Ty<'tcx>;
+    type TyLayout = TyLayout<'tcx>;
+
+    fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
+        self.tcx.layout_of(self.param_env.and(ty)).unwrap()
+    }
+}
+
+impl<'me, 'tcx> HasTyCtxt<'tcx> for UnwrapLayoutCx<'me, 'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
+        self.tcx
+    }
+}
+
+impl<'me, 'tcx> HasDataLayout for UnwrapLayoutCx<'me, 'tcx> {
+    fn data_layout(&self) -> &TargetDataLayout {
+        self.tcx.data_layout()
+    }
+}
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index 829d4b34cf7..a181bc7e9b4 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -32,6 +32,7 @@ mod diagnostics;
 pub mod ast_validation;
 pub mod rvalue_promotion;
 pub mod hir_stats;
+pub mod layout_test;
 pub mod loops;
 
 __build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
diff --git a/src/librustc_target/abi/call/aarch64.rs b/src/librustc_target/abi/call/aarch64.rs
index 121f80c75b9..9f9bba14b96 100644
--- a/src/librustc_target/abi/call/aarch64.rs
+++ b/src/librustc_target/abi/call/aarch64.rs
@@ -6,7 +6,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         let size = arg.layout.size;
 
         // Ensure we have at most four uniquely addressable members.
diff --git a/src/librustc_target/abi/call/arm.rs b/src/librustc_target/abi/call/arm.rs
index 2b3588d31bf..228dd362161 100644
--- a/src/librustc_target/abi/call/arm.rs
+++ b/src/librustc_target/abi/call/arm.rs
@@ -7,7 +7,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         let size = arg.layout.size;
 
         // Ensure we have at most four uniquely addressable members.
diff --git a/src/librustc_target/abi/call/asmjs.rs b/src/librustc_target/abi/call/asmjs.rs
index 32c6634b3d2..85444500c5e 100644
--- a/src/librustc_target/abi/call/asmjs.rs
+++ b/src/librustc_target/abi/call/asmjs.rs
@@ -11,7 +11,7 @@ fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>)
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
     if ret.layout.is_aggregate() {
-        if let Some(unit) = ret.layout.homogeneous_aggregate(cx) {
+        if let Some(unit) = ret.layout.homogeneous_aggregate(cx).unit() {
             let size = ret.layout.size;
             if unit.size == size {
                 ret.cast_to(Uniform {
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index 37bf6e68247..0d50439c67e 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -228,6 +228,33 @@ impl CastTarget {
     }
 }
 
+/// Return value from the `homogeneous_aggregate` test function.
+#[derive(Copy, Clone, Debug)]
+pub enum HomogeneousAggregate {
+    /// Yes, all the "leaf fields" of this struct are passed in the
+    /// same way (specified in the `Reg` value).
+    Homogeneous(Reg),
+
+    /// There are distinct leaf fields passed in different ways,
+    /// or this is uninhabited.
+    Heterogeneous,
+
+    /// There are no leaf fields at all.
+    NoData,
+}
+
+impl HomogeneousAggregate {
+    /// If this is a homogeneous aggregate, returns the homogeneous
+    /// unit, else `None`.
+    pub fn unit(self) -> Option<Reg> {
+        if let HomogeneousAggregate::Homogeneous(r) = self {
+            Some(r)
+        } else {
+            None
+        }
+    }
+}
+
 impl<'a, Ty> TyLayout<'a, Ty> {
     fn is_aggregate(&self) -> bool {
         match self.abi {
@@ -239,11 +266,21 @@ impl<'a, Ty> TyLayout<'a, Ty> {
         }
     }
 
-    fn homogeneous_aggregate<C>(&self, cx: &C) -> Option<Reg>
+    /// True if this layout is an aggregate containing fields of only
+    /// a single type (e.g., `(u32, u32)`). Such aggregates are often
+    /// special-cased in ABIs.
+    ///
+    /// Note: We generally ignore fields of zero-sized type when computing
+    /// this value (cc #56877).
+    ///
+    /// This is public so that it can be used in unit tests, but
+    /// should generally only be relevant to the ABI details of
+    /// specific targets.
+    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> HomogeneousAggregate
         where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf<Ty = Ty, TyLayout = Self>
     {
         match self.abi {
-            Abi::Uninhabited => None,
+            Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,
 
             // The primitive for this algorithm.
             Abi::Scalar(ref scalar) => {
@@ -252,14 +289,15 @@ impl<'a, Ty> TyLayout<'a, Ty> {
                     abi::Pointer => RegKind::Integer,
                     abi::Float(_) => RegKind::Float,
                 };
-                Some(Reg {
+                HomogeneousAggregate::Homogeneous(Reg {
                     kind,
                     size: self.size
                 })
             }
 
             Abi::Vector { .. } => {
-                Some(Reg {
+                assert!(!self.is_zst());
+                HomogeneousAggregate::Homogeneous(Reg {
                     kind: RegKind::Vector,
                     size: self.size
                 })
@@ -275,7 +313,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
                         if count > 0 {
                             return self.field(cx, 0).homogeneous_aggregate(cx);
                         } else {
-                            return None;
+                            return HomogeneousAggregate::NoData;
                         }
                     }
                     FieldPlacement::Union(_) => true,
@@ -284,21 +322,27 @@ impl<'a, Ty> TyLayout<'a, Ty> {
 
                 for i in 0..self.fields.count() {
                     if !is_union && total != self.fields.offset(i) {
-                        return None;
+                        return HomogeneousAggregate::Heterogeneous;
                     }
 
                     let field = self.field(cx, i);
+
                     match (result, field.homogeneous_aggregate(cx)) {
-                        // The field itself must be a homogeneous aggregate.
-                        (_, None) => return None,
+                        (_, HomogeneousAggregate::NoData) => {
+                            // Ignore fields that have no data
+                        }
+                        (_, HomogeneousAggregate::Heterogeneous) => {
+                            // The field itself must be a homogeneous aggregate.
+                            return HomogeneousAggregate::Heterogeneous;
+                        }
                         // If this is the first field, record the unit.
-                        (None, Some(unit)) => {
+                        (None, HomogeneousAggregate::Homogeneous(unit)) => {
                             result = Some(unit);
                         }
                         // For all following fields, the unit must be the same.
-                        (Some(prev_unit), Some(unit)) => {
+                        (Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => {
                             if prev_unit != unit {
-                                return None;
+                                return HomogeneousAggregate::Heterogeneous;
                             }
                         }
                     }
@@ -314,9 +358,18 @@ impl<'a, Ty> TyLayout<'a, Ty> {
 
                 // There needs to be no padding.
                 if total != self.size {
-                    None
+                    HomogeneousAggregate::Heterogeneous
                 } else {
-                    result
+                    match result {
+                        Some(reg) => {
+                            assert_ne!(total, Size::ZERO);
+                            HomogeneousAggregate::Homogeneous(reg)
+                        }
+                        None => {
+                            assert_eq!(total, Size::ZERO);
+                            HomogeneousAggregate::NoData
+                        }
+                    }
                 }
             }
         }
diff --git a/src/librustc_target/abi/call/powerpc64.rs b/src/librustc_target/abi/call/powerpc64.rs
index 3ae9ab9ac5e..305a2d42250 100644
--- a/src/librustc_target/abi/call/powerpc64.rs
+++ b/src/librustc_target/abi/call/powerpc64.rs
@@ -18,7 +18,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>, abi: A
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         // ELFv1 only passes one-member aggregates transparently.
         // ELFv2 passes up to eight uniquely addressable members.
         if (abi == ELFv1 && arg.layout.size > unit.size)
diff --git a/src/librustc_target/abi/call/sparc64.rs b/src/librustc_target/abi/call/sparc64.rs
index bb0c9b7967a..150b48a8d02 100644
--- a/src/librustc_target/abi/call/sparc64.rs
+++ b/src/librustc_target/abi/call/sparc64.rs
@@ -8,7 +8,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         // Ensure we have at most eight uniquely addressable members.
         if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
             return None;
diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs
index 748bef4de66..648a4b5bb9d 100644
--- a/src/librustc_target/abi/call/x86.rs
+++ b/src/librustc_target/abi/call/x86.rs
@@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
             };
 
             // At this point we know this must be a primitive of sorts.
-            let unit = arg.layout.homogeneous_aggregate(cx).unwrap();
+            let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap();
             assert_eq!(unit.size, arg.layout.size);
             if unit.kind == RegKind::Float {
                 continue;
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9e107fee5ba..85e80f7bdaf 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -938,6 +938,13 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
                                       is just used for rustc unit tests \
                                       and will never be stable",
                                      cfg_fn!(rustc_attrs))),
+    ("rustc_layout", Normal, template!(List: "field1, field2, ..."),
+     Gated(Stability::Unstable,
+           "rustc_attrs",
+           "the `#[rustc_layout]` attribute \
+            is just used for rustc unit tests \
+            and will never be stable",
+           cfg_fn!(rustc_attrs))),
     ("rustc_regions", Normal, template!(Word), Gated(Stability::Unstable,
                                     "rustc_attrs",
                                     "the `#[rustc_regions]` attribute \
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
new file mode 100644
index 00000000000..622709e7de5
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
@@ -0,0 +1,36 @@
+#![feature(rustc_attrs)]
+
+// Show that `homogeneous_aggregate` code ignores zero-length C
+// arrays.  This matches the recent C standard, though not the
+// behavior of all older compilers, which somtimes consider `T[0]` to
+// be a "flexible array member" (see discussion on #56877 for
+// details).
+
+#[repr(C)]
+pub struct Foo {
+    x: u32
+}
+
+#[repr(C)]
+pub struct Middle {
+    pub a: f32,
+    pub foo: [Foo; 0],
+    pub b: f32,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type TestMiddle = Middle;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+pub struct Final {
+    pub a: f32,
+    pub b: f32,
+    pub foo: [Foo; 0],
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type TestFinal = Final;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+fn main() { }
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
new file mode 100644
index 00000000000..0d442606351
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
@@ -0,0 +1,14 @@
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:22:1
+   |
+LL | pub type TestMiddle = Middle;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:33:1
+   |
+LL | pub type TestFinal = Final;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
new file mode 100644
index 00000000000..4b429412aeb
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
@@ -0,0 +1,73 @@
+#![feature(rustc_attrs)]
+
+// Regression test for #56877. We want to ensure that the presence of
+// `PhantomData` does not prevent `Bar` from being considered a
+// homogeneous aggregate.
+
+#[repr(C)]
+pub struct BaseCase {
+    pub a: f32,
+    pub b: f32,
+}
+
+#[repr(C)]
+pub struct WithPhantomData {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: std::marker::PhantomData<()>,
+}
+
+pub struct EmptyRustStruct {
+}
+
+#[repr(C)]
+pub struct WithEmptyRustStruct {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: EmptyRustStruct,
+}
+
+pub struct TransitivelyEmptyRustStruct {
+    field: EmptyRustStruct,
+    array: [u32; 0],
+}
+
+#[repr(C)]
+pub struct WithTransitivelyEmptyRustStruct {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: TransitivelyEmptyRustStruct,
+}
+
+pub enum EmptyRustEnum {
+    Dummy,
+}
+
+#[repr(C)]
+pub struct WithEmptyRustEnum {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: EmptyRustEnum,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test1 = BaseCase;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test2 = WithPhantomData;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test3 = WithEmptyRustStruct;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test4 = WithTransitivelyEmptyRustStruct;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test5 = WithEmptyRustEnum;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+fn main() { }
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
new file mode 100644
index 00000000000..be04ba3e7f6
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
@@ -0,0 +1,32 @@
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:54:1
+   |
+LL | pub type Test1 = BaseCase;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:58:1
+   |
+LL | pub type Test2 = WithPhantomData;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:62:1
+   |
+LL | pub type Test3 = WithEmptyRustStruct;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:66:1
+   |
+LL | pub type Test4 = WithTransitivelyEmptyRustStruct;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:70:1
+   |
+LL | pub type Test5 = WithEmptyRustEnum;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/test/ui/layout/zero-sized-array-union.rs b/src/test/ui/layout/zero-sized-array-union.rs
new file mode 100644
index 00000000000..68b218249eb
--- /dev/null
+++ b/src/test/ui/layout/zero-sized-array-union.rs
@@ -0,0 +1,95 @@
+#![feature(rustc_attrs)]
+
+// Various tests around the behavior of zero-sized arrays and
+// unions. This matches the behavior of modern C compilers, though
+// older compilers (and sometimes clang) treat `T[0]` as a "flexible
+// array member". See more
+// details in #56877.
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty { }
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty2 {
+    e: Empty
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty3 {
+    z: [f32; 0],
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty4 {
+    e: Empty3
+}
+
+#[repr(C)]
+union U1 {
+    s: Empty
+}
+
+#[repr(C)]
+union U2 {
+    s: Empty2
+}
+
+#[repr(C)]
+union U3 {
+    s: Empty3
+}
+
+#[repr(C)]
+union U4 {
+    s: Empty4
+}
+
+#[repr(C)]
+struct Baz1 {
+    x: f32,
+    y: f32,
+    u: U1,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz1 = Baz1;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+struct Baz2 {
+    x: f32,
+    y: f32,
+    u: U2,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz2 = Baz2;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+struct Baz3 {
+    x: f32,
+    y: f32,
+    u: U3,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz3 = Baz3;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+struct Baz4 {
+    x: f32,
+    y: f32,
+    u: U4,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz4 = Baz4;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+fn main() { }
diff --git a/src/test/ui/layout/zero-sized-array-union.stderr b/src/test/ui/layout/zero-sized-array-union.stderr
new file mode 100644
index 00000000000..1bb31aaf7b7
--- /dev/null
+++ b/src/test/ui/layout/zero-sized-array-union.stderr
@@ -0,0 +1,26 @@
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:59:1
+   |
+LL | type TestBaz1 = Baz1;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:70:1
+   |
+LL | type TestBaz2 = Baz2;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:81:1
+   |
+LL | type TestBaz3 = Baz3;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:92:1
+   |
+LL | type TestBaz4 = Baz4;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+