Fix `-Z print-type-sizes`'s handling of zero-sized fields.

Currently, the type `struct S { x: u32, y: u32, tag: () }` is
incorrectly described like this:
```
print-type-size type: `S`: 8 bytes, alignment: 4 bytes
print-type-size     field `.x`: 4 bytes
print-type-size     field `.tag`: 0 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size     padding: 4 bytes
print-type-size     field `.y`: 4 bytes, alignment: 4 bytes
```
Specifically:
- The `padding` line is wrong. (There is no padding.)
- The `offset` and `alignment` on the `.tag` line shouldn't be printed.

The problem is that multiple fields can end up with the same offset, and
the printing code doesn't handle this correctly.

This commit fixes it by adjusting the field sorting so that zero-sized fields
are dealt with before non-zero-sized fields. With that in place, the
printing code works correctly.

The commit also corrects the "something is very wrong" comment.

The new output looks like this:
```
print-type-size type: `S`: 8 bytes, alignment: 4 bytes
print-type-size     field `.tag`: 0 bytes
print-type-size     field `.x`: 4 bytes
print-type-size     field `.y`: 4 bytes
```
This commit is contained in:
Nicholas Nethercote 2019-12-11 14:17:41 +11:00
parent 7dbfb0a8ca
commit c681841ca0
3 changed files with 68 additions and 3 deletions

View File

@ -132,9 +132,12 @@ impl CodeStats {
let mut min_offset = discr_size;
// We want to print fields by increasing offset.
// We want to print fields by increasing offset. We also want
// zero-sized fields before non-zero-sized fields, otherwise
// the loop below goes wrong; hence the `f.size` in the sort
// key.
let mut fields = fields.clone();
fields.sort_by_key(|f| f.offset);
fields.sort_by_key(|f| (f.offset, f.size));
for field in fields.iter() {
let FieldInfo { ref name, offset, size, align } = *field;
@ -146,7 +149,7 @@ impl CodeStats {
}
if offset < min_offset {
// if this happens something is very wrong
// If this happens it's probably a union.
println!("print-type-size {}field `.{}`: {} bytes, \
offset: {} bytes, \
alignment: {} bytes",

View File

@ -0,0 +1,46 @@
// compile-flags: -Z print-type-sizes
// build-pass (FIXME(62277): could be check-pass?)
// At one point, zero-sized fields such as those in this file were causing
// incorrect output from `-Z print-type-sizes`.
#![feature(start)]
struct S1 {
x: u32,
y: u32,
tag: (),
}
struct Void();
struct Empty {}
struct S5<TagW, TagZ> {
tagw: TagW,
w: u32,
unit: (),
x: u32,
void: Void,
y: u32,
empty: Empty,
z: u32,
tagz: TagZ,
}
#[start]
fn start(_: isize, _: *const *const u8) -> isize {
let _s1: S1 = S1 { x: 0, y: 0, tag: () };
let _s5: S5<(), Empty> = S5 {
tagw: (),
w: 1,
unit: (),
x: 2,
void: Void(),
y: 3,
empty: Empty {},
z: 4,
tagz: Empty {},
};
0
}

View File

@ -0,0 +1,16 @@
print-type-size type: `S5<(), Empty>`: 16 bytes, alignment: 4 bytes
print-type-size field `.tagw`: 0 bytes
print-type-size field `.unit`: 0 bytes
print-type-size field `.void`: 0 bytes
print-type-size field `.empty`: 0 bytes
print-type-size field `.tagz`: 0 bytes
print-type-size field `.w`: 4 bytes
print-type-size field `.x`: 4 bytes
print-type-size field `.y`: 4 bytes
print-type-size field `.z`: 4 bytes
print-type-size type: `S1`: 8 bytes, alignment: 4 bytes
print-type-size field `.tag`: 0 bytes
print-type-size field `.x`: 4 bytes
print-type-size field `.y`: 4 bytes
print-type-size type: `Empty`: 0 bytes, alignment: 1 bytes
print-type-size type: `Void`: 0 bytes, alignment: 1 bytes