From 8e4c57fca2f4bae61f7b567e7c72f96245f385bc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 Jan 2019 15:16:32 -0500 Subject: [PATCH] distinguish "no data" from "heterogeneous" for ABI purposes Also, add a testing infrastructure and tests that lets us dump layout. --- src/librustc_driver/driver.rs | 5 +- src/librustc_passes/layout_test.rs | 132 ++++++++++++++++++ src/librustc_passes/lib.rs | 1 + src/librustc_target/abi/call/aarch64.rs | 2 +- src/librustc_target/abi/call/arm.rs | 2 +- src/librustc_target/abi/call/asmjs.rs | 2 +- src/librustc_target/abi/call/mod.rs | 79 +++++++++-- src/librustc_target/abi/call/powerpc64.rs | 2 +- src/librustc_target/abi/call/sparc64.rs | 2 +- src/librustc_target/abi/call/x86.rs | 2 +- src/libsyntax/feature_gate.rs | 7 + .../homogeneous-aggr-zero-sized-c-struct.rs | 36 +++++ ...omogeneous-aggr-zero-sized-c-struct.stderr | 14 ++ .../homogeneous-aggr-zero-sized-repr-rust.rs | 73 ++++++++++ ...mogeneous-aggr-zero-sized-repr-rust.stderr | 32 +++++ src/test/ui/layout/zero-sized-array-union.rs | 95 +++++++++++++ .../ui/layout/zero-sized-array-union.stderr | 26 ++++ 17 files changed, 492 insertions(+), 20 deletions(-) create mode 100644 src/librustc_passes/layout_test.rs create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr create mode 100644 src/test/ui/layout/zero-sized-array-union.rs create mode 100644 src/test/ui/layout/zero-sized-array-union.stderr 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> + 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> + 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> + 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 { + 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(&self, cx: &C) -> Option + /// 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(&self, cx: &C) -> HomogeneousAggregate where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf { 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> + 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> + 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 +