Skip to content

Commit 7779bb9

Browse files
authored
Rollup merge of rust-lang#57645 - nikomatsakis:issue-56877-abi-aggregates, r=eddyb
distinguish "no data" from "heterogeneous" in ABI Ignore zero-sized types when computing whether something is a homogeneous aggregate, except be careful of VLA. cc rust-lang#56877 r? @arielb1 cc @eddyb
2 parents ef5a5ba + 8e4c57f commit 7779bb9

17 files changed

+492
-20
lines changed

src/librustc_driver/driver.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_incremental;
2222
use rustc_metadata::creader::CrateLoader;
2323
use rustc_metadata::cstore::{self, CStore};
2424
use rustc_mir as mir;
25-
use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion};
25+
use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test};
2626
use rustc_plugin as plugin;
2727
use rustc_plugin::registry::Registry;
2828
use rustc_privacy;
@@ -1287,6 +1287,9 @@ where
12871287
mir::transform::check_unsafety::check_unsafety(tcx, def_id)
12881288
}
12891289
});
1290+
1291+
time(sess, "layout testing", || layout_test::test_layout(tcx));
1292+
12901293
// Avoid overwhelming user with errors if type checking failed.
12911294
// I'm not sure how helpful this is, to be honest, but it avoids
12921295
// a

src/librustc_passes/layout_test.rs

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use rustc::hir;
2+
use rustc::hir::def_id::DefId;
3+
use rustc::hir::itemlikevisit::ItemLikeVisitor;
4+
use rustc::hir::ItemKind;
5+
use rustc::ty::layout::HasDataLayout;
6+
use rustc::ty::layout::HasTyCtxt;
7+
use rustc::ty::layout::LayoutOf;
8+
use rustc::ty::layout::TargetDataLayout;
9+
use rustc::ty::layout::TyLayout;
10+
use rustc::ty::ParamEnv;
11+
use rustc::ty::Ty;
12+
use rustc::ty::TyCtxt;
13+
use syntax::ast::Attribute;
14+
15+
pub fn test_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
16+
if tcx.features().rustc_attrs {
17+
// if the `rustc_attrs` feature is not enabled, don't bother testing layout
18+
tcx.hir()
19+
.krate()
20+
.visit_all_item_likes(&mut VarianceTest { tcx });
21+
}
22+
}
23+
24+
struct VarianceTest<'a, 'tcx: 'a> {
25+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
26+
}
27+
28+
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'a, 'tcx> {
29+
fn visit_item(&mut self, item: &'tcx hir::Item) {
30+
let item_def_id = self.tcx.hir().local_def_id(item.id);
31+
32+
if let ItemKind::Ty(..) = item.node {
33+
for attr in self.tcx.get_attrs(item_def_id).iter() {
34+
if attr.check_name("rustc_layout") {
35+
self.dump_layout_of(item_def_id, item, attr);
36+
}
37+
}
38+
}
39+
}
40+
41+
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
42+
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
43+
}
44+
45+
impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
46+
fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item, attr: &Attribute) {
47+
let tcx = self.tcx;
48+
let param_env = self.tcx.param_env(item_def_id);
49+
let ty = self.tcx.type_of(item_def_id);
50+
match self.tcx.layout_of(param_env.and(ty)) {
51+
Ok(ty_layout) => {
52+
// Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
53+
// The `..` are the names of fields to dump.
54+
let meta_items = attr.meta_item_list().unwrap_or_default();
55+
for meta_item in meta_items {
56+
let name = meta_item.word().map(|mi| mi.name().as_str());
57+
let name = name.as_ref().map(|s| &s[..]).unwrap_or("");
58+
59+
match name {
60+
"abi" => {
61+
self.tcx
62+
.sess
63+
.span_err(item.span, &format!("abi: {:?}", ty_layout.abi));
64+
}
65+
66+
"align" => {
67+
self.tcx
68+
.sess
69+
.span_err(item.span, &format!("align: {:?}", ty_layout.align));
70+
}
71+
72+
"size" => {
73+
self.tcx
74+
.sess
75+
.span_err(item.span, &format!("size: {:?}", ty_layout.size));
76+
}
77+
78+
"homogeneous_aggregate" => {
79+
self.tcx.sess.span_err(
80+
item.span,
81+
&format!(
82+
"homogeneous_aggregate: {:?}",
83+
ty_layout
84+
.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
85+
),
86+
);
87+
}
88+
89+
_ => {
90+
self.tcx.sess.span_err(
91+
meta_item.span,
92+
&format!("unrecognized field name `{}`", name),
93+
);
94+
}
95+
}
96+
}
97+
}
98+
99+
Err(layout_error) => {
100+
self.tcx
101+
.sess
102+
.span_err(item.span, &format!("layout error: {:?}", layout_error));
103+
}
104+
}
105+
}
106+
}
107+
108+
struct UnwrapLayoutCx<'me, 'tcx> {
109+
tcx: TyCtxt<'me, 'tcx, 'tcx>,
110+
param_env: ParamEnv<'tcx>,
111+
}
112+
113+
impl<'me, 'tcx> LayoutOf for UnwrapLayoutCx<'me, 'tcx> {
114+
type Ty = Ty<'tcx>;
115+
type TyLayout = TyLayout<'tcx>;
116+
117+
fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
118+
self.tcx.layout_of(self.param_env.and(ty)).unwrap()
119+
}
120+
}
121+
122+
impl<'me, 'tcx> HasTyCtxt<'tcx> for UnwrapLayoutCx<'me, 'tcx> {
123+
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
124+
self.tcx
125+
}
126+
}
127+
128+
impl<'me, 'tcx> HasDataLayout for UnwrapLayoutCx<'me, 'tcx> {
129+
fn data_layout(&self) -> &TargetDataLayout {
130+
self.tcx.data_layout()
131+
}
132+
}

src/librustc_passes/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod diagnostics;
3232
pub mod ast_validation;
3333
pub mod rvalue_promotion;
3434
pub mod hir_stats;
35+
pub mod layout_test;
3536
pub mod loops;
3637

3738
__build_diagnostic_array! { librustc_passes, DIAGNOSTICS }

src/librustc_target/abi/call/aarch64.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
66
where Ty: TyLayoutMethods<'a, C> + Copy,
77
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
88
{
9-
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
9+
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
1010
let size = arg.layout.size;
1111

1212
// Ensure we have at most four uniquely addressable members.

src/librustc_target/abi/call/arm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
77
where Ty: TyLayoutMethods<'a, C> + Copy,
88
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
99
{
10-
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
10+
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
1111
let size = arg.layout.size;
1212

1313
// Ensure we have at most four uniquely addressable members.

src/librustc_target/abi/call/asmjs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>)
1111
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
1212
{
1313
if ret.layout.is_aggregate() {
14-
if let Some(unit) = ret.layout.homogeneous_aggregate(cx) {
14+
if let Some(unit) = ret.layout.homogeneous_aggregate(cx).unit() {
1515
let size = ret.layout.size;
1616
if unit.size == size {
1717
ret.cast_to(Uniform {

src/librustc_target/abi/call/mod.rs

+66-13
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,33 @@ impl CastTarget {
228228
}
229229
}
230230

231+
/// Return value from the `homogeneous_aggregate` test function.
232+
#[derive(Copy, Clone, Debug)]
233+
pub enum HomogeneousAggregate {
234+
/// Yes, all the "leaf fields" of this struct are passed in the
235+
/// same way (specified in the `Reg` value).
236+
Homogeneous(Reg),
237+
238+
/// There are distinct leaf fields passed in different ways,
239+
/// or this is uninhabited.
240+
Heterogeneous,
241+
242+
/// There are no leaf fields at all.
243+
NoData,
244+
}
245+
246+
impl HomogeneousAggregate {
247+
/// If this is a homogeneous aggregate, returns the homogeneous
248+
/// unit, else `None`.
249+
pub fn unit(self) -> Option<Reg> {
250+
if let HomogeneousAggregate::Homogeneous(r) = self {
251+
Some(r)
252+
} else {
253+
None
254+
}
255+
}
256+
}
257+
231258
impl<'a, Ty> TyLayout<'a, Ty> {
232259
fn is_aggregate(&self) -> bool {
233260
match self.abi {
@@ -239,11 +266,21 @@ impl<'a, Ty> TyLayout<'a, Ty> {
239266
}
240267
}
241268

242-
fn homogeneous_aggregate<C>(&self, cx: &C) -> Option<Reg>
269+
/// True if this layout is an aggregate containing fields of only
270+
/// a single type (e.g., `(u32, u32)`). Such aggregates are often
271+
/// special-cased in ABIs.
272+
///
273+
/// Note: We generally ignore fields of zero-sized type when computing
274+
/// this value (cc #56877).
275+
///
276+
/// This is public so that it can be used in unit tests, but
277+
/// should generally only be relevant to the ABI details of
278+
/// specific targets.
279+
pub fn homogeneous_aggregate<C>(&self, cx: &C) -> HomogeneousAggregate
243280
where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf<Ty = Ty, TyLayout = Self>
244281
{
245282
match self.abi {
246-
Abi::Uninhabited => None,
283+
Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,
247284

248285
// The primitive for this algorithm.
249286
Abi::Scalar(ref scalar) => {
@@ -252,14 +289,15 @@ impl<'a, Ty> TyLayout<'a, Ty> {
252289
abi::Pointer => RegKind::Integer,
253290
abi::Float(_) => RegKind::Float,
254291
};
255-
Some(Reg {
292+
HomogeneousAggregate::Homogeneous(Reg {
256293
kind,
257294
size: self.size
258295
})
259296
}
260297

261298
Abi::Vector { .. } => {
262-
Some(Reg {
299+
assert!(!self.is_zst());
300+
HomogeneousAggregate::Homogeneous(Reg {
263301
kind: RegKind::Vector,
264302
size: self.size
265303
})
@@ -275,7 +313,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
275313
if count > 0 {
276314
return self.field(cx, 0).homogeneous_aggregate(cx);
277315
} else {
278-
return None;
316+
return HomogeneousAggregate::NoData;
279317
}
280318
}
281319
FieldPlacement::Union(_) => true,
@@ -284,21 +322,27 @@ impl<'a, Ty> TyLayout<'a, Ty> {
284322

285323
for i in 0..self.fields.count() {
286324
if !is_union && total != self.fields.offset(i) {
287-
return None;
325+
return HomogeneousAggregate::Heterogeneous;
288326
}
289327

290328
let field = self.field(cx, i);
329+
291330
match (result, field.homogeneous_aggregate(cx)) {
292-
// The field itself must be a homogeneous aggregate.
293-
(_, None) => return None,
331+
(_, HomogeneousAggregate::NoData) => {
332+
// Ignore fields that have no data
333+
}
334+
(_, HomogeneousAggregate::Heterogeneous) => {
335+
// The field itself must be a homogeneous aggregate.
336+
return HomogeneousAggregate::Heterogeneous;
337+
}
294338
// If this is the first field, record the unit.
295-
(None, Some(unit)) => {
339+
(None, HomogeneousAggregate::Homogeneous(unit)) => {
296340
result = Some(unit);
297341
}
298342
// For all following fields, the unit must be the same.
299-
(Some(prev_unit), Some(unit)) => {
343+
(Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => {
300344
if prev_unit != unit {
301-
return None;
345+
return HomogeneousAggregate::Heterogeneous;
302346
}
303347
}
304348
}
@@ -314,9 +358,18 @@ impl<'a, Ty> TyLayout<'a, Ty> {
314358

315359
// There needs to be no padding.
316360
if total != self.size {
317-
None
361+
HomogeneousAggregate::Heterogeneous
318362
} else {
319-
result
363+
match result {
364+
Some(reg) => {
365+
assert_ne!(total, Size::ZERO);
366+
HomogeneousAggregate::Homogeneous(reg)
367+
}
368+
None => {
369+
assert_eq!(total, Size::ZERO);
370+
HomogeneousAggregate::NoData
371+
}
372+
}
320373
}
321374
}
322375
}

src/librustc_target/abi/call/powerpc64.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>, abi: A
1818
where Ty: TyLayoutMethods<'a, C> + Copy,
1919
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
2020
{
21-
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
21+
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
2222
// ELFv1 only passes one-member aggregates transparently.
2323
// ELFv2 passes up to eight uniquely addressable members.
2424
if (abi == ELFv1 && arg.layout.size > unit.size)

src/librustc_target/abi/call/sparc64.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
88
where Ty: TyLayoutMethods<'a, C> + Copy,
99
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
1010
{
11-
arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
11+
arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
1212
// Ensure we have at most eight uniquely addressable members.
1313
if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
1414
return None;

src/librustc_target/abi/call/x86.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
9999
};
100100

101101
// At this point we know this must be a primitive of sorts.
102-
let unit = arg.layout.homogeneous_aggregate(cx).unwrap();
102+
let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap();
103103
assert_eq!(unit.size, arg.layout.size);
104104
if unit.kind == RegKind::Float {
105105
continue;

src/libsyntax/feature_gate.rs

+7
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,13 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
938938
is just used for rustc unit tests \
939939
and will never be stable",
940940
cfg_fn!(rustc_attrs))),
941+
("rustc_layout", Normal, template!(List: "field1, field2, ..."),
942+
Gated(Stability::Unstable,
943+
"rustc_attrs",
944+
"the `#[rustc_layout]` attribute \
945+
is just used for rustc unit tests \
946+
and will never be stable",
947+
cfg_fn!(rustc_attrs))),
941948
("rustc_regions", Normal, template!(Word), Gated(Stability::Unstable,
942949
"rustc_attrs",
943950
"the `#[rustc_regions]` attribute \

0 commit comments

Comments
 (0)