Skip to content

distinguish "no data" from "heterogeneous" in ABI #57645

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 26, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
@@ -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
132 changes: 132 additions & 0 deletions src/librustc_passes/layout_test.rs
Original file line number Diff line number Diff line change
@@ -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()
}
}
1 change: 1 addition & 0 deletions src/librustc_passes/lib.rs
Original file line number Diff line number Diff line change
@@ -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 }
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/aarch64.rs
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/arm.rs
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/asmjs.rs
Original file line number Diff line number Diff line change
@@ -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 {
79 changes: 66 additions & 13 deletions src/librustc_target/abi/call/mod.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/powerpc64.rs
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/sparc64.rs
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion src/librustc_target/abi/call/x86.rs
Original file line number Diff line number Diff line change
@@ -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;
7 changes: 7 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -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 \
36 changes: 36 additions & 0 deletions src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
Original file line number Diff line number Diff line change
@@ -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() { }
14 changes: 14 additions & 0 deletions src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
Original file line number Diff line number Diff line change
@@ -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

73 changes: 73 additions & 0 deletions src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
Original file line number Diff line number Diff line change
@@ -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() { }
32 changes: 32 additions & 0 deletions src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
Original file line number Diff line number Diff line change
@@ -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

95 changes: 95 additions & 0 deletions src/test/ui/layout/zero-sized-array-union.rs
Original file line number Diff line number Diff line change
@@ -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() { }
26 changes: 26 additions & 0 deletions src/test/ui/layout/zero-sized-array-union.stderr
Original file line number Diff line number Diff line change
@@ -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