mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-04 04:39:16 +00:00
distinguish "no data" from "heterogeneous" for ABI purposes
Also, add a testing infrastructure and tests that lets us dump layout.
This commit is contained in:
parent
01f8e25b15
commit
8e4c57fca2
@ -22,7 +22,7 @@ use rustc_incremental;
|
|||||||
use rustc_metadata::creader::CrateLoader;
|
use rustc_metadata::creader::CrateLoader;
|
||||||
use rustc_metadata::cstore::{self, CStore};
|
use rustc_metadata::cstore::{self, CStore};
|
||||||
use rustc_mir as mir;
|
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 as plugin;
|
||||||
use rustc_plugin::registry::Registry;
|
use rustc_plugin::registry::Registry;
|
||||||
use rustc_privacy;
|
use rustc_privacy;
|
||||||
@ -1287,6 +1287,9 @@ where
|
|||||||
mir::transform::check_unsafety::check_unsafety(tcx, def_id)
|
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.
|
// Avoid overwhelming user with errors if type checking failed.
|
||||||
// I'm not sure how helpful this is, to be honest, but it avoids
|
// I'm not sure how helpful this is, to be honest, but it avoids
|
||||||
// a
|
// a
|
||||||
|
132
src/librustc_passes/layout_test.rs
Normal file
132
src/librustc_passes/layout_test.rs
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@ mod diagnostics;
|
|||||||
pub mod ast_validation;
|
pub mod ast_validation;
|
||||||
pub mod rvalue_promotion;
|
pub mod rvalue_promotion;
|
||||||
pub mod hir_stats;
|
pub mod hir_stats;
|
||||||
|
pub mod layout_test;
|
||||||
pub mod loops;
|
pub mod loops;
|
||||||
|
|
||||||
__build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
|
__build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
|
||||||
|
@ -6,7 +6,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
|
|||||||
where Ty: TyLayoutMethods<'a, C> + Copy,
|
where Ty: TyLayoutMethods<'a, C> + Copy,
|
||||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
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;
|
let size = arg.layout.size;
|
||||||
|
|
||||||
// Ensure we have at most four uniquely addressable members.
|
// Ensure we have at most four uniquely addressable members.
|
||||||
|
@ -7,7 +7,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
|
|||||||
where Ty: TyLayoutMethods<'a, C> + Copy,
|
where Ty: TyLayoutMethods<'a, C> + Copy,
|
||||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
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;
|
let size = arg.layout.size;
|
||||||
|
|
||||||
// Ensure we have at most four uniquely addressable members.
|
// Ensure we have at most four uniquely addressable members.
|
||||||
|
@ -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
|
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
||||||
{
|
{
|
||||||
if ret.layout.is_aggregate() {
|
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;
|
let size = ret.layout.size;
|
||||||
if unit.size == size {
|
if unit.size == size {
|
||||||
ret.cast_to(Uniform {
|
ret.cast_to(Uniform {
|
||||||
|
@ -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> {
|
impl<'a, Ty> TyLayout<'a, Ty> {
|
||||||
fn is_aggregate(&self) -> bool {
|
fn is_aggregate(&self) -> bool {
|
||||||
match self.abi {
|
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>
|
where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf<Ty = Ty, TyLayout = Self>
|
||||||
{
|
{
|
||||||
match self.abi {
|
match self.abi {
|
||||||
Abi::Uninhabited => None,
|
Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,
|
||||||
|
|
||||||
// The primitive for this algorithm.
|
// The primitive for this algorithm.
|
||||||
Abi::Scalar(ref scalar) => {
|
Abi::Scalar(ref scalar) => {
|
||||||
@ -252,14 +289,15 @@ impl<'a, Ty> TyLayout<'a, Ty> {
|
|||||||
abi::Pointer => RegKind::Integer,
|
abi::Pointer => RegKind::Integer,
|
||||||
abi::Float(_) => RegKind::Float,
|
abi::Float(_) => RegKind::Float,
|
||||||
};
|
};
|
||||||
Some(Reg {
|
HomogeneousAggregate::Homogeneous(Reg {
|
||||||
kind,
|
kind,
|
||||||
size: self.size
|
size: self.size
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Abi::Vector { .. } => {
|
Abi::Vector { .. } => {
|
||||||
Some(Reg {
|
assert!(!self.is_zst());
|
||||||
|
HomogeneousAggregate::Homogeneous(Reg {
|
||||||
kind: RegKind::Vector,
|
kind: RegKind::Vector,
|
||||||
size: self.size
|
size: self.size
|
||||||
})
|
})
|
||||||
@ -275,7 +313,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
|
|||||||
if count > 0 {
|
if count > 0 {
|
||||||
return self.field(cx, 0).homogeneous_aggregate(cx);
|
return self.field(cx, 0).homogeneous_aggregate(cx);
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return HomogeneousAggregate::NoData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FieldPlacement::Union(_) => true,
|
FieldPlacement::Union(_) => true,
|
||||||
@ -284,21 +322,27 @@ impl<'a, Ty> TyLayout<'a, Ty> {
|
|||||||
|
|
||||||
for i in 0..self.fields.count() {
|
for i in 0..self.fields.count() {
|
||||||
if !is_union && total != self.fields.offset(i) {
|
if !is_union && total != self.fields.offset(i) {
|
||||||
return None;
|
return HomogeneousAggregate::Heterogeneous;
|
||||||
}
|
}
|
||||||
|
|
||||||
let field = self.field(cx, i);
|
let field = self.field(cx, i);
|
||||||
|
|
||||||
match (result, field.homogeneous_aggregate(cx)) {
|
match (result, field.homogeneous_aggregate(cx)) {
|
||||||
// The field itself must be a homogeneous aggregate.
|
(_, HomogeneousAggregate::NoData) => {
|
||||||
(_, None) => return None,
|
// 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.
|
// If this is the first field, record the unit.
|
||||||
(None, Some(unit)) => {
|
(None, HomogeneousAggregate::Homogeneous(unit)) => {
|
||||||
result = Some(unit);
|
result = Some(unit);
|
||||||
}
|
}
|
||||||
// For all following fields, the unit must be the same.
|
// 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 {
|
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.
|
// There needs to be no padding.
|
||||||
if total != self.size {
|
if total != self.size {
|
||||||
None
|
HomogeneousAggregate::Heterogeneous
|
||||||
} else {
|
} else {
|
||||||
result
|
match result {
|
||||||
|
Some(reg) => {
|
||||||
|
assert_ne!(total, Size::ZERO);
|
||||||
|
HomogeneousAggregate::Homogeneous(reg)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
assert_eq!(total, Size::ZERO);
|
||||||
|
HomogeneousAggregate::NoData
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
where Ty: TyLayoutMethods<'a, C> + Copy,
|
||||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
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.
|
// ELFv1 only passes one-member aggregates transparently.
|
||||||
// ELFv2 passes up to eight uniquely addressable members.
|
// ELFv2 passes up to eight uniquely addressable members.
|
||||||
if (abi == ELFv1 && arg.layout.size > unit.size)
|
if (abi == ELFv1 && arg.layout.size > unit.size)
|
||||||
|
@ -8,7 +8,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
|
|||||||
where Ty: TyLayoutMethods<'a, C> + Copy,
|
where Ty: TyLayoutMethods<'a, C> + Copy,
|
||||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
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.
|
// Ensure we have at most eight uniquely addressable members.
|
||||||
if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
|
if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -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.
|
// 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);
|
assert_eq!(unit.size, arg.layout.size);
|
||||||
if unit.kind == RegKind::Float {
|
if unit.kind == RegKind::Float {
|
||||||
continue;
|
continue;
|
||||||
|
@ -938,6 +938,13 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
|
|||||||
is just used for rustc unit tests \
|
is just used for rustc unit tests \
|
||||||
and will never be stable",
|
and will never be stable",
|
||||||
cfg_fn!(rustc_attrs))),
|
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_regions", Normal, template!(Word), Gated(Stability::Unstable,
|
||||||
"rustc_attrs",
|
"rustc_attrs",
|
||||||
"the `#[rustc_regions]` attribute \
|
"the `#[rustc_regions]` attribute \
|
||||||
|
36
src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
Normal file
36
src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
Normal file
@ -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() { }
|
@ -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
src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
Normal file
73
src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
Normal file
@ -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() { }
|
@ -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
src/test/ui/layout/zero-sized-array-union.rs
Normal file
95
src/test/ui/layout/zero-sized-array-union.rs
Normal file
@ -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
src/test/ui/layout/zero-sized-array-union.stderr
Normal file
26
src/test/ui/layout/zero-sized-array-union.stderr
Normal file
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user