summaryrefslogtreecommitdiffstats
path: root/src/test/ui/print_type_sizes
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/ui/print_type_sizes')
-rw-r--r--src/test/ui/print_type_sizes/anonymous.rs23
-rw-r--r--src/test/ui/print_type_sizes/generics.rs71
-rw-r--r--src/test/ui/print_type_sizes/generics.stdout14
-rw-r--r--src/test/ui/print_type_sizes/multiple_types.rs23
-rw-r--r--src/test/ui/print_type_sizes/multiple_types.stdout10
-rw-r--r--src/test/ui/print_type_sizes/niche-filling.rs101
-rw-r--r--src/test/ui/print_type_sizes/niche-filling.stdout112
-rw-r--r--src/test/ui/print_type_sizes/no_duplicates.rs23
-rw-r--r--src/test/ui/print_type_sizes/no_duplicates.stdout2
-rw-r--r--src/test/ui/print_type_sizes/packed.rs69
-rw-r--r--src/test/ui/print_type_sizes/packed.stdout31
-rw-r--r--src/test/ui/print_type_sizes/padding.rs34
-rw-r--r--src/test/ui/print_type_sizes/padding.stdout23
-rw-r--r--src/test/ui/print_type_sizes/repr-align.rs38
-rw-r--r--src/test/ui/print_type_sizes/repr-align.stdout16
-rw-r--r--src/test/ui/print_type_sizes/repr_int_c.rs25
-rw-r--r--src/test/ui/print_type_sizes/repr_int_c.stdout12
-rw-r--r--src/test/ui/print_type_sizes/uninhabited.rs15
-rw-r--r--src/test/ui/print_type_sizes/uninhabited.stdout6
-rw-r--r--src/test/ui/print_type_sizes/variants.rs26
-rw-r--r--src/test/ui/print_type_sizes/variants.stdout10
-rw-r--r--src/test/ui/print_type_sizes/zero-sized-fields.rs47
-rw-r--r--src/test/ui/print_type_sizes/zero-sized-fields.stdout16
23 files changed, 747 insertions, 0 deletions
diff --git a/src/test/ui/print_type_sizes/anonymous.rs b/src/test/ui/print_type_sizes/anonymous.rs
new file mode 100644
index 000000000..2b008ca3b
--- /dev/null
+++ b/src/test/ui/print_type_sizes/anonymous.rs
@@ -0,0 +1,23 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+
+// All of the types that occur in this function are uninteresting, in
+// that one cannot control the sizes of these types with the same sort
+// of enum-variant manipulation tricks.
+
+#![feature(start)]
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _byte: u8 = 0;
+ let _word: usize = 0;
+ let _tuple: (u8, usize)= (0, 0);
+ let _array: [u8; 128] = [0; 128];
+ let _fn: fn (u8) -> u8 = id;
+ let _diverging: fn (u8) -> ! = bye;
+
+ fn id(x: u8) -> u8 { x };
+ fn bye(_: u8) -> ! { loop { } }
+
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/generics.rs b/src/test/ui/print_type_sizes/generics.rs
new file mode 100644
index 000000000..3ef7b60db
--- /dev/null
+++ b/src/test/ui/print_type_sizes/generics.rs
@@ -0,0 +1,71 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+// FIXME: consider using an attribute instead of side-effects.
+
+// This file illustrates how generics are handled: types have to be
+// monomorphized, in the MIR of the original function in which they
+// occur, to have their size reported.
+
+#![feature(start)]
+
+// In an ad-hoc attempt to avoid the injection of unwinding code
+// (which clutters the output of `-Z print-type-sizes` with types from
+// `unwind::libunwind`):
+//
+// * I am not using Default to build values because that seems to
+// cause the injection of unwinding code. (Instead I just make `fn new`
+// methods.)
+//
+// * Pair derive Copy to ensure that we don't inject
+// unwinding code into generic uses of Pair when T itself is also
+// Copy.
+//
+// (I suspect this reflect some naivety within the rust compiler
+// itself; it should be checking for drop glue, i.e., a destructor
+// somewhere in the monomorphized types. It should not matter whether
+// the type is Copy.)
+#[derive(Copy, Clone)]
+pub struct Pair<T> {
+ _car: T,
+ _cdr: T,
+}
+
+impl<T> Pair<T> {
+ fn new(a: T, d: T) -> Self {
+ Pair {
+ _car: a,
+ _cdr: d,
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct SevenBytes([u8; 7]);
+pub struct FiftyBytes([u8; 50]);
+
+pub struct ZeroSized;
+
+impl SevenBytes {
+ fn new() -> Self { SevenBytes([0; 7]) }
+}
+
+impl FiftyBytes {
+ fn new() -> Self { FiftyBytes([0; 50]) }
+}
+
+pub fn f1<T:Copy>(x: T) {
+ let _v: Pair<T> = Pair::new(x, x);
+ let _v2: Pair<FiftyBytes> =
+ Pair::new(FiftyBytes::new(), FiftyBytes::new());
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _b: Pair<u8> = Pair::new(0, 0);
+ let _s: Pair<SevenBytes> = Pair::new(SevenBytes::new(), SevenBytes::new());
+ let ref _z: ZeroSized = ZeroSized;
+ f1::<SevenBytes>(SevenBytes::new());
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/generics.stdout b/src/test/ui/print_type_sizes/generics.stdout
new file mode 100644
index 000000000..0f02f3979
--- /dev/null
+++ b/src/test/ui/print_type_sizes/generics.stdout
@@ -0,0 +1,14 @@
+print-type-size type: `Pair<FiftyBytes>`: 100 bytes, alignment: 1 bytes
+print-type-size field `._car`: 50 bytes
+print-type-size field `._cdr`: 50 bytes
+print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes
+print-type-size field `.0`: 50 bytes
+print-type-size type: `Pair<SevenBytes>`: 14 bytes, alignment: 1 bytes
+print-type-size field `._car`: 7 bytes
+print-type-size field `._cdr`: 7 bytes
+print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
+print-type-size field `.0`: 7 bytes
+print-type-size type: `Pair<u8>`: 2 bytes, alignment: 1 bytes
+print-type-size field `._car`: 1 bytes
+print-type-size field `._cdr`: 1 bytes
+print-type-size type: `ZeroSized`: 0 bytes, alignment: 1 bytes
diff --git a/src/test/ui/print_type_sizes/multiple_types.rs b/src/test/ui/print_type_sizes/multiple_types.rs
new file mode 100644
index 000000000..f1ad27ec1
--- /dev/null
+++ b/src/test/ui/print_type_sizes/multiple_types.rs
@@ -0,0 +1,23 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+
+// This file illustrates that when multiple structural types occur in
+// a function, every one of them is included in the output.
+
+#![feature(start)]
+
+pub struct SevenBytes([u8; 7]);
+pub struct FiftyBytes([u8; 50]);
+
+pub enum Enum {
+ Small(SevenBytes),
+ Large(FiftyBytes),
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _e: Enum;
+ let _f: FiftyBytes;
+ let _s: SevenBytes;
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/multiple_types.stdout b/src/test/ui/print_type_sizes/multiple_types.stdout
new file mode 100644
index 000000000..641188154
--- /dev/null
+++ b/src/test/ui/print_type_sizes/multiple_types.stdout
@@ -0,0 +1,10 @@
+print-type-size type: `Enum`: 51 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Large`: 50 bytes
+print-type-size field `.0`: 50 bytes
+print-type-size variant `Small`: 7 bytes
+print-type-size field `.0`: 7 bytes
+print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes
+print-type-size field `.0`: 50 bytes
+print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
+print-type-size field `.0`: 7 bytes
diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs
new file mode 100644
index 000000000..0716cee21
--- /dev/null
+++ b/src/test/ui/print_type_sizes/niche-filling.rs
@@ -0,0 +1,101 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+// FIXME: consider using an attribute instead of side-effects.
+
+// This file illustrates how niche-filling enums are handled,
+// modelled after cases like `Option<&u32>`, `Option<bool>` and such.
+//
+// It uses NonZeroU32 rather than `&_` or `Unique<_>`, because
+// the test is not set up to deal with target-dependent pointer width.
+//
+// It avoids using u64/i64 because on some targets that is only 4-byte
+// aligned (while on most it is 8-byte aligned) and so the resulting
+// padding and overall computed sizes can be quite different.
+
+#![feature(start)]
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+use std::num::NonZeroU32;
+
+pub enum MyOption<T> { None, Some(T) }
+
+#[rustc_layout_scalar_valid_range_start(0)]
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+pub struct MyNotNegativeOne {
+ _i: i32,
+}
+
+impl<T> Default for MyOption<T> {
+ fn default() -> Self { MyOption::None }
+}
+
+pub enum EmbeddedDiscr {
+ None,
+ Record { pre: u8, val: NonZeroU32, post: u16 },
+}
+
+impl Default for EmbeddedDiscr {
+ fn default() -> Self { EmbeddedDiscr::None }
+}
+
+#[derive(Default)]
+pub struct IndirectNonZero {
+ pre: u8,
+ nested: NestedNonZero,
+ post: u16,
+}
+
+pub struct NestedNonZero {
+ pre: u8,
+ val: NonZeroU32,
+ post: u16,
+}
+
+impl Default for NestedNonZero {
+ fn default() -> Self {
+ NestedNonZero { pre: 0, val: NonZeroU32::new(1).unwrap(), post: 0 }
+ }
+}
+
+pub enum Enum4<A, B, C, D> {
+ One(A),
+ Two(B),
+ Three(C),
+ Four(D)
+}
+
+pub union Union1<A: Copy> {
+ a: A,
+}
+
+pub union Union2<A: Copy, B: Copy> {
+ a: A,
+ b: B,
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _x: MyOption<NonZeroU32> = Default::default();
+ let _y: EmbeddedDiscr = Default::default();
+ let _z: MyOption<IndirectNonZero> = Default::default();
+ let _a: MyOption<bool> = Default::default();
+ let _b: MyOption<char> = Default::default();
+ let _c: MyOption<std::cmp::Ordering> = Default::default();
+ let _d: MyOption<MyOption<u8>> = Default::default();
+ let _e: Enum4<(), char, (), ()> = Enum4::One(());
+ let _f: Enum4<(), (), bool, ()> = Enum4::One(());
+ let _g: Enum4<(), (), (), MyOption<u8>> = Enum4::One(());
+ let _h: MyOption<MyNotNegativeOne> = Default::default();
+
+ // Unions do not currently participate in niche filling.
+ let _i: MyOption<Union2<NonZeroU32, u32>> = Default::default();
+
+ // ...even when theoretically possible.
+ let _j: MyOption<Union1<NonZeroU32>> = Default::default();
+ let _k: MyOption<Union2<NonZeroU32, NonZeroU32>> = Default::default();
+
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout
new file mode 100644
index 000000000..d1753c26c
--- /dev/null
+++ b/src/test/ui/print_type_sizes/niche-filling.stdout
@@ -0,0 +1,112 @@
+print-type-size type: `IndirectNonZero`: 12 bytes, alignment: 4 bytes
+print-type-size field `.nested`: 8 bytes
+print-type-size field `.post`: 2 bytes
+print-type-size field `.pre`: 1 bytes
+print-type-size end padding: 1 bytes
+print-type-size type: `MyOption<IndirectNonZero>`: 12 bytes, alignment: 4 bytes
+print-type-size variant `Some`: 12 bytes
+print-type-size field `.0`: 12 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Record`: 7 bytes
+print-type-size field `.pre`: 1 bytes
+print-type-size field `.post`: 2 bytes
+print-type-size field `.val`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<Union1<std::num::NonZeroU32>>`: 8 bytes, alignment: 4 bytes
+print-type-size discriminant: 4 bytes
+print-type-size variant `Some`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<Union2<std::num::NonZeroU32, std::num::NonZeroU32>>`: 8 bytes, alignment: 4 bytes
+print-type-size discriminant: 4 bytes
+print-type-size variant `Some`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<Union2<std::num::NonZeroU32, u32>>`: 8 bytes, alignment: 4 bytes
+print-type-size discriminant: 4 bytes
+print-type-size variant `Some`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `NestedNonZero`: 8 bytes, alignment: 4 bytes
+print-type-size field `.val`: 4 bytes
+print-type-size field `.post`: 2 bytes
+print-type-size field `.pre`: 1 bytes
+print-type-size end padding: 1 bytes
+print-type-size type: `Enum4<(), char, (), ()>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Two`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `One`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Three`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Four`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size type: `MyNotNegativeOne`: 4 bytes, alignment: 4 bytes
+print-type-size field `._i`: 4 bytes
+print-type-size type: `MyOption<MyNotNegativeOne>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Some`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<char>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Some`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<std::num::NonZeroU32>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Some`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `Union1<std::num::NonZeroU32>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Union1`: 4 bytes
+print-type-size field `.a`: 4 bytes
+print-type-size type: `Union2<std::num::NonZeroU32, std::num::NonZeroU32>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Union2`: 4 bytes
+print-type-size field `.a`: 4 bytes
+print-type-size field `.b`: 4 bytes, offset: 0 bytes, alignment: 4 bytes
+print-type-size type: `Union2<std::num::NonZeroU32, u32>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Union2`: 4 bytes
+print-type-size field `.a`: 4 bytes
+print-type-size field `.b`: 4 bytes, offset: 0 bytes, alignment: 4 bytes
+print-type-size type: `std::num::NonZeroU32`: 4 bytes, alignment: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size type: `Enum4<(), (), (), MyOption<u8>>`: 2 bytes, alignment: 1 bytes
+print-type-size variant `Four`: 2 bytes
+print-type-size field `.0`: 2 bytes
+print-type-size variant `One`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Two`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Three`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size type: `MyOption<MyOption<u8>>`: 2 bytes, alignment: 1 bytes
+print-type-size variant `Some`: 2 bytes
+print-type-size field `.0`: 2 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<u8>`: 2 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Some`: 1 bytes
+print-type-size field `.0`: 1 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `Enum4<(), (), bool, ()>`: 1 bytes, alignment: 1 bytes
+print-type-size variant `Three`: 1 bytes
+print-type-size field `.0`: 1 bytes
+print-type-size variant `One`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Two`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Four`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size type: `MyOption<bool>`: 1 bytes, alignment: 1 bytes
+print-type-size variant `Some`: 1 bytes
+print-type-size field `.0`: 1 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `MyOption<std::cmp::Ordering>`: 1 bytes, alignment: 1 bytes
+print-type-size variant `Some`: 1 bytes
+print-type-size field `.0`: 1 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `std::cmp::Ordering`: 1 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Less`: 0 bytes
+print-type-size variant `Equal`: 0 bytes
+print-type-size variant `Greater`: 0 bytes
diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs
new file mode 100644
index 000000000..e45e4794a
--- /dev/null
+++ b/src/test/ui/print_type_sizes/no_duplicates.rs
@@ -0,0 +1,23 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+// FIXME: consider using an attribute instead of side-effects.
+
+// This file illustrates that when the same type occurs repeatedly
+// (even if multiple functions), it is only printed once in the
+// print-type-sizes output.
+
+#![feature(start)]
+
+pub struct SevenBytes([u8; 7]);
+
+pub fn f1() {
+ let _s: SevenBytes = SevenBytes([0; 7]);
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _s: SevenBytes = SevenBytes([0; 7]);
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/no_duplicates.stdout b/src/test/ui/print_type_sizes/no_duplicates.stdout
new file mode 100644
index 000000000..50180f356
--- /dev/null
+++ b/src/test/ui/print_type_sizes/no_duplicates.stdout
@@ -0,0 +1,2 @@
+print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
+print-type-size field `.0`: 7 bytes
diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs
new file mode 100644
index 000000000..269cc3cc2
--- /dev/null
+++ b/src/test/ui/print_type_sizes/packed.rs
@@ -0,0 +1,69 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+// FIXME: consider using an attribute instead of side-effects.
+
+// This file illustrates how packing is handled; it should cause
+// the elimination of padding that would normally be introduced
+// to satisfy alignment desirata.
+//
+// It avoids using u64/i64 because on some targets that is only 4-byte
+// aligned (while on most it is 8-byte aligned) and so the resulting
+// padding and overall computed sizes can be quite different.
+
+#![allow(dead_code)]
+#![feature(start)]
+
+#[derive(Default)]
+#[repr(packed)]
+struct Packed1 {
+ a: u8,
+ b: u8,
+ g: i32,
+ c: u8,
+ h: i16,
+ d: u8,
+}
+
+#[derive(Default)]
+#[repr(packed(2))]
+struct Packed2 {
+ a: u8,
+ b: u8,
+ g: i32,
+ c: u8,
+ h: i16,
+ d: u8,
+}
+
+#[derive(Default)]
+#[repr(packed(2))]
+#[repr(C)]
+struct Packed2C {
+ a: u8,
+ b: u8,
+ g: i32,
+ c: u8,
+ h: i16,
+ d: u8,
+}
+
+#[derive(Default)]
+struct Padded {
+ a: u8,
+ b: u8,
+ g: i32,
+ c: u8,
+ h: i16,
+ d: u8,
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _c: Packed1 = Default::default();
+ let _d: Packed2 = Default::default();
+ let _e: Packed2C = Default::default();
+ let _f: Padded = Default::default();
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/packed.stdout b/src/test/ui/print_type_sizes/packed.stdout
new file mode 100644
index 000000000..58e1bac9e
--- /dev/null
+++ b/src/test/ui/print_type_sizes/packed.stdout
@@ -0,0 +1,31 @@
+print-type-size type: `Packed2C`: 12 bytes, alignment: 2 bytes
+print-type-size field `.a`: 1 bytes
+print-type-size field `.b`: 1 bytes
+print-type-size field `.g`: 4 bytes
+print-type-size field `.c`: 1 bytes
+print-type-size padding: 1 bytes
+print-type-size field `.h`: 2 bytes
+print-type-size field `.d`: 1 bytes
+print-type-size end padding: 1 bytes
+print-type-size type: `Padded`: 12 bytes, alignment: 4 bytes
+print-type-size field `.g`: 4 bytes
+print-type-size field `.h`: 2 bytes
+print-type-size field `.a`: 1 bytes
+print-type-size field `.b`: 1 bytes
+print-type-size field `.c`: 1 bytes
+print-type-size field `.d`: 1 bytes
+print-type-size end padding: 2 bytes
+print-type-size type: `Packed1`: 10 bytes, alignment: 1 bytes
+print-type-size field `.a`: 1 bytes
+print-type-size field `.b`: 1 bytes
+print-type-size field `.g`: 4 bytes
+print-type-size field `.c`: 1 bytes
+print-type-size field `.h`: 2 bytes
+print-type-size field `.d`: 1 bytes
+print-type-size type: `Packed2`: 10 bytes, alignment: 2 bytes
+print-type-size field `.g`: 4 bytes
+print-type-size field `.h`: 2 bytes
+print-type-size field `.a`: 1 bytes
+print-type-size field `.b`: 1 bytes
+print-type-size field `.c`: 1 bytes
+print-type-size field `.d`: 1 bytes
diff --git a/src/test/ui/print_type_sizes/padding.rs b/src/test/ui/print_type_sizes/padding.rs
new file mode 100644
index 000000000..d1acad16d
--- /dev/null
+++ b/src/test/ui/print_type_sizes/padding.rs
@@ -0,0 +1,34 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+
+// This file illustrates how padding is handled: alignment
+// requirements can lead to the introduction of padding, either before
+// fields or at the end of the structure as a whole.
+//
+// It avoids using u64/i64 because on some targets that is only 4-byte
+// aligned (while on most it is 8-byte aligned) and so the resulting
+// padding and overall computed sizes can be quite different.
+
+#![feature(start)]
+#![allow(dead_code)]
+
+struct S {
+ a: bool,
+ b: bool,
+ g: i32,
+}
+
+enum E1 {
+ A(i32, i8),
+ B(S),
+}
+
+enum E2 {
+ A(i8, i32),
+ B(S),
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/padding.stdout b/src/test/ui/print_type_sizes/padding.stdout
new file mode 100644
index 000000000..9afdf7624
--- /dev/null
+++ b/src/test/ui/print_type_sizes/padding.stdout
@@ -0,0 +1,23 @@
+print-type-size type: `E1`: 12 bytes, alignment: 4 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `B`: 11 bytes
+print-type-size padding: 3 bytes
+print-type-size field `.0`: 8 bytes, alignment: 4 bytes
+print-type-size variant `A`: 7 bytes
+print-type-size field `.1`: 1 bytes
+print-type-size padding: 2 bytes
+print-type-size field `.0`: 4 bytes, alignment: 4 bytes
+print-type-size type: `E2`: 12 bytes, alignment: 4 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `B`: 11 bytes
+print-type-size padding: 3 bytes
+print-type-size field `.0`: 8 bytes, alignment: 4 bytes
+print-type-size variant `A`: 7 bytes
+print-type-size field `.0`: 1 bytes
+print-type-size padding: 2 bytes
+print-type-size field `.1`: 4 bytes, alignment: 4 bytes
+print-type-size type: `S`: 8 bytes, alignment: 4 bytes
+print-type-size field `.g`: 4 bytes
+print-type-size field `.a`: 1 bytes
+print-type-size field `.b`: 1 bytes
+print-type-size end padding: 2 bytes
diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs
new file mode 100644
index 000000000..07544935b
--- /dev/null
+++ b/src/test/ui/print_type_sizes/repr-align.rs
@@ -0,0 +1,38 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+// FIXME: consider using an attribute instead of side-effects.
+
+// This file illustrates how padding is handled: alignment
+// requirements can lead to the introduction of padding, either before
+// fields or at the end of the structure as a whole.
+//
+// It avoids using u64/i64 because on some targets that is only 4-byte
+// aligned (while on most it is 8-byte aligned) and so the resulting
+// padding and overall computed sizes can be quite different.
+#![feature(start)]
+#![allow(dead_code)]
+
+#[repr(align(16))]
+#[derive(Default)]
+struct A(i32);
+
+enum E {
+ A(i32),
+ B(A)
+}
+
+#[derive(Default)]
+struct S {
+ a: i32,
+ b: i32,
+ c: A,
+ d: i8,
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _s: S = Default::default();
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/repr-align.stdout b/src/test/ui/print_type_sizes/repr-align.stdout
new file mode 100644
index 000000000..33671bd8e
--- /dev/null
+++ b/src/test/ui/print_type_sizes/repr-align.stdout
@@ -0,0 +1,16 @@
+print-type-size type: `E`: 32 bytes, alignment: 16 bytes
+print-type-size discriminant: 4 bytes
+print-type-size variant `B`: 28 bytes
+print-type-size padding: 12 bytes
+print-type-size field `.0`: 16 bytes, alignment: 16 bytes
+print-type-size variant `A`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size type: `S`: 32 bytes, alignment: 16 bytes
+print-type-size field `.c`: 16 bytes
+print-type-size field `.a`: 4 bytes
+print-type-size field `.b`: 4 bytes
+print-type-size field `.d`: 1 bytes
+print-type-size end padding: 7 bytes
+print-type-size type: `A`: 16 bytes, alignment: 16 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size end padding: 12 bytes
diff --git a/src/test/ui/print_type_sizes/repr_int_c.rs b/src/test/ui/print_type_sizes/repr_int_c.rs
new file mode 100644
index 000000000..b8067c112
--- /dev/null
+++ b/src/test/ui/print_type_sizes/repr_int_c.rs
@@ -0,0 +1,25 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+
+// This test makes sure that the tag is not grown for `repr(C)` or `repr(u8)`
+// variants (see https://github.com/rust-lang/rust/issues/50098 for the original bug).
+
+#![feature(start)]
+#![allow(dead_code)]
+
+#[repr(C, u8)]
+enum ReprCu8 {
+ A(u16),
+ B,
+}
+
+#[repr(u8)]
+enum Repru8 {
+ A(u16),
+ B,
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/repr_int_c.stdout b/src/test/ui/print_type_sizes/repr_int_c.stdout
new file mode 100644
index 000000000..254b3c7a8
--- /dev/null
+++ b/src/test/ui/print_type_sizes/repr_int_c.stdout
@@ -0,0 +1,12 @@
+print-type-size type: `ReprCu8`: 4 bytes, alignment: 2 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `A`: 3 bytes
+print-type-size padding: 1 bytes
+print-type-size field `.0`: 2 bytes, alignment: 2 bytes
+print-type-size variant `B`: 1 bytes
+print-type-size type: `Repru8`: 4 bytes, alignment: 2 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `A`: 3 bytes
+print-type-size padding: 1 bytes
+print-type-size field `.0`: 2 bytes, alignment: 2 bytes
+print-type-size variant `B`: 0 bytes
diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs
new file mode 100644
index 000000000..06a62db4e
--- /dev/null
+++ b/src/test/ui/print_type_sizes/uninhabited.rs
@@ -0,0 +1,15 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+// FIXME: consider using an attribute instead of side-effects.
+
+#![feature(never_type)]
+#![feature(start)]
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _x: Option<!> = None;
+ let _y: Result<u32, !> = Ok(42);
+ let _z: Result<!, !> = loop {};
+}
diff --git a/src/test/ui/print_type_sizes/uninhabited.stdout b/src/test/ui/print_type_sizes/uninhabited.stdout
new file mode 100644
index 000000000..5eb5384bc
--- /dev/null
+++ b/src/test/ui/print_type_sizes/uninhabited.stdout
@@ -0,0 +1,6 @@
+print-type-size type: `std::result::Result<u32, !>`: 4 bytes, alignment: 4 bytes
+print-type-size variant `Ok`: 4 bytes
+print-type-size field `.0`: 4 bytes
+print-type-size type: `std::option::Option<!>`: 0 bytes, alignment: 1 bytes
+print-type-size variant `None`: 0 bytes
+print-type-size type: `std::result::Result<!, !>`: 0 bytes, alignment: 1 bytes
diff --git a/src/test/ui/print_type_sizes/variants.rs b/src/test/ui/print_type_sizes/variants.rs
new file mode 100644
index 000000000..6c8553cc2
--- /dev/null
+++ b/src/test/ui/print_type_sizes/variants.rs
@@ -0,0 +1,26 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+
+// This file illustrates two things:
+//
+// 1. Only types that appear in a monomorphized function appear in the
+// print-type-sizes output, and
+//
+// 2. For an enum, the print-type-sizes output will also include the
+// size of each variant.
+
+#![feature(start)]
+
+pub struct SevenBytes([u8; 7]);
+pub struct FiftyBytes([u8; 50]);
+
+pub enum Enum {
+ Small(SevenBytes),
+ Large(FiftyBytes),
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _e: Enum;
+ 0
+}
diff --git a/src/test/ui/print_type_sizes/variants.stdout b/src/test/ui/print_type_sizes/variants.stdout
new file mode 100644
index 000000000..641188154
--- /dev/null
+++ b/src/test/ui/print_type_sizes/variants.stdout
@@ -0,0 +1,10 @@
+print-type-size type: `Enum`: 51 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Large`: 50 bytes
+print-type-size field `.0`: 50 bytes
+print-type-size variant `Small`: 7 bytes
+print-type-size field `.0`: 7 bytes
+print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes
+print-type-size field `.0`: 50 bytes
+print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes
+print-type-size field `.0`: 7 bytes
diff --git a/src/test/ui/print_type_sizes/zero-sized-fields.rs b/src/test/ui/print_type_sizes/zero-sized-fields.rs
new file mode 100644
index 000000000..e02a33109
--- /dev/null
+++ b/src/test/ui/print_type_sizes/zero-sized-fields.rs
@@ -0,0 +1,47 @@
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-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
+}
diff --git a/src/test/ui/print_type_sizes/zero-sized-fields.stdout b/src/test/ui/print_type_sizes/zero-sized-fields.stdout
new file mode 100644
index 000000000..72f59c4bb
--- /dev/null
+++ b/src/test/ui/print_type_sizes/zero-sized-fields.stdout
@@ -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