Skip to content

Commit fbe00f0

Browse files
authored
Rollup merge of rust-lang#105623 - compiler-errors:generator-type-size-fix, r=Nilstrieb
Fix `-Z print-type-sizes` for generators with discriminant field ordered first Fixes rust-lang#105589 Fixes rust-lang#105591
2 parents a60bb44 + bdc3c4b commit fbe00f0

17 files changed

+89
-129
lines changed

compiler/rustc_ty_utils/src/layout.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ fn variant_info_for_generator<'tcx>(
919919
def_id: DefId,
920920
substs: ty::SubstsRef<'tcx>,
921921
) -> (Vec<VariantInfo>, Option<Size>) {
922-
let Variants::Multiple { tag, ref tag_encoding, .. } = layout.variants else {
922+
let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else {
923923
return (vec![], None);
924924
};
925925

@@ -975,12 +975,28 @@ fn variant_info_for_generator<'tcx>(
975975
if variant_size == Size::ZERO {
976976
variant_size = upvars_size;
977977
}
978-
// We need to add the discriminant size back into min_size, since it is subtracted
979-
// later during printing.
980-
variant_size += match tag_encoding {
981-
TagEncoding::Direct => tag.size(cx),
982-
_ => Size::ZERO,
983-
};
978+
979+
// This `if` deserves some explanation.
980+
//
981+
// The layout code has a choice of where to place the discriminant of this generator.
982+
// If the discriminant of the generator is placed early in the layout (before the
983+
// variant's own fields), then it'll implicitly be counted towards the size of the
984+
// variant, since we use the maximum offset to calculate size.
985+
// (side-note: I know this is a bit problematic given upvars placement, etc).
986+
//
987+
// This is important, since the layout printing code always subtracts this discriminant
988+
// size from the variant size if the struct is "enum"-like, so failing to account for it
989+
// will either lead to numerical underflow, or an underreported variant size...
990+
//
991+
// However, if the discriminant is placed past the end of the variant, then we need
992+
// to factor in the size of the discriminant manually. This really should be refactored
993+
// better, but this "works" for now.
994+
if layout.fields.offset(tag_field) >= variant_size {
995+
variant_size += match tag_encoding {
996+
TagEncoding::Direct => tag.size(cx),
997+
_ => Size::ZERO,
998+
};
999+
}
9841000

9851001
VariantInfo {
9861002
name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name(variant_idx))),

src/test/ui/print_type_sizes/async.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type lib
22
// edition:2021
33
// build-pass
44
// ignore-pass
55

6-
#![feature(start)]
7-
86
async fn wait() {}
97

10-
async fn test(arg: [u8; 8192]) {
8+
pub async fn test(arg: [u8; 8192]) {
119
wait().await;
1210
drop(arg);
1311
}
14-
15-
#[start]
16-
fn start(_: isize, _: *const *const u8) -> isize {
17-
let _ = test([0; 8192]);
18-
0
19-
}

src/test/ui/print_type_sizes/async.stdout

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
print-type-size type: `[async fn body@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes
1+
print-type-size type: `[async fn body@$DIR/async.rs:8:36: 11:2]`: 16386 bytes, alignment: 1 bytes
22
print-type-size discriminant: 1 bytes
33
print-type-size variant `Suspend0`: 16385 bytes
44
print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
@@ -16,14 +16,14 @@ print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment
1616
print-type-size variant `MaybeUninit`: 8192 bytes
1717
print-type-size field `.uninit`: 0 bytes
1818
print-type-size field `.value`: 8192 bytes
19-
print-type-size type: `[async fn body@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes
19+
print-type-size type: `[async fn body@$DIR/async.rs:6:17: 6:19]`: 1 bytes, alignment: 1 bytes
2020
print-type-size discriminant: 1 bytes
2121
print-type-size variant `Unresumed`: 0 bytes
2222
print-type-size variant `Returned`: 0 bytes
2323
print-type-size variant `Panicked`: 0 bytes
24-
print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes
24+
print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:6:17: 6:19]>`: 1 bytes, alignment: 1 bytes
2525
print-type-size field `.value`: 1 bytes
26-
print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes
26+
print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:6:17: 6:19]>`: 1 bytes, alignment: 1 bytes
2727
print-type-size variant `MaybeUninit`: 1 bytes
2828
print-type-size field `.uninit`: 0 bytes
2929
print-type-size field `.value`: 1 bytes
+3-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33
// ignore-pass
44

5-
#![feature(start, generators, generator_trait)]
5+
#![feature(generators, generator_trait)]
66

77
use std::ops::Generator;
88

@@ -13,8 +13,6 @@ fn generator<const C: usize>(array: [u8; C]) -> impl Generator<Yield = (), Retur
1313
}
1414
}
1515

16-
#[start]
17-
fn start(_: isize, _: *const *const u8) -> isize {
16+
pub fn foo() {
1817
let _ = generator([0; 8192]);
19-
0
2018
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// compile-flags: -Z print-type-sizes --crate-type lib
2+
// build-pass
3+
// ignore-pass
4+
5+
// Tests a generator that has its discriminant as the *final* field.
6+
7+
// Avoid emitting panic handlers, like the rest of these tests...
8+
#![feature(generators)]
9+
10+
pub fn foo() {
11+
let a = || {
12+
{
13+
let w: i32 = 4;
14+
yield;
15+
drop(w);
16+
}
17+
{
18+
let z: i32 = 7;
19+
yield;
20+
drop(z);
21+
}
22+
};
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
print-type-size type: `[generator@$DIR/generator_discr_placement.rs:11:13: 11:15]`: 8 bytes, alignment: 4 bytes
2+
print-type-size discriminant: 1 bytes
3+
print-type-size variant `Suspend0`: 7 bytes
4+
print-type-size padding: 3 bytes
5+
print-type-size field `.w`: 4 bytes, alignment: 4 bytes
6+
print-type-size variant `Suspend1`: 7 bytes
7+
print-type-size padding: 3 bytes
8+
print-type-size field `.z`: 4 bytes, alignment: 4 bytes
9+
print-type-size variant `Unresumed`: 0 bytes
10+
print-type-size variant `Returned`: 0 bytes
11+
print-type-size variant `Panicked`: 0 bytes

src/test/ui/print_type_sizes/generics.rs

+2-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33
// ignore-pass
44
// ^-- needed because `--pass check` does not emit the output needed.
@@ -8,24 +8,6 @@
88
// monomorphized, in the MIR of the original function in which they
99
// occur, to have their size reported.
1010

11-
#![feature(start)]
12-
13-
// In an ad-hoc attempt to avoid the injection of unwinding code
14-
// (which clutters the output of `-Z print-type-sizes` with types from
15-
// `unwind::libunwind`):
16-
//
17-
// * I am not using Default to build values because that seems to
18-
// cause the injection of unwinding code. (Instead I just make `fn new`
19-
// methods.)
20-
//
21-
// * Pair derive Copy to ensure that we don't inject
22-
// unwinding code into generic uses of Pair when T itself is also
23-
// Copy.
24-
//
25-
// (I suspect this reflect some naivety within the rust compiler
26-
// itself; it should be checking for drop glue, i.e., a destructor
27-
// somewhere in the monomorphized types. It should not matter whether
28-
// the type is Copy.)
2911
#[derive(Copy, Clone)]
3012
pub struct Pair<T> {
3113
_car: T,
@@ -61,11 +43,9 @@ pub fn f1<T:Copy>(x: T) {
6143
Pair::new(FiftyBytes::new(), FiftyBytes::new());
6244
}
6345

64-
#[start]
65-
fn start(_: isize, _: *const *const u8) -> isize {
46+
pub fn start() {
6647
let _b: Pair<u8> = Pair::new(0, 0);
6748
let _s: Pair<SevenBytes> = Pair::new(SevenBytes::new(), SevenBytes::new());
6849
let ref _z: ZeroSized = ZeroSized;
6950
f1::<SevenBytes>(SevenBytes::new());
70-
0
7151
}
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33

44
// This file illustrates that when multiple structural types occur in
55
// a function, every one of them is included in the output.
66

7-
#![feature(start)]
8-
97
pub struct SevenBytes([u8; 7]);
108
pub struct FiftyBytes([u8; 50]);
119

1210
pub enum Enum {
1311
Small(SevenBytes),
1412
Large(FiftyBytes),
1513
}
16-
17-
#[start]
18-
fn start(_: isize, _: *const *const u8) -> isize {
19-
let _e: Enum;
20-
let _f: FiftyBytes;
21-
let _s: SevenBytes;
22-
0
23-
}

src/test/ui/print_type_sizes/niche-filling.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33
// ignore-pass
44
// ^-- needed because `--pass check` does not emit the output needed.
@@ -14,7 +14,6 @@
1414
// aligned (while on most it is 8-byte aligned) and so the resulting
1515
// padding and overall computed sizes can be quite different.
1616

17-
#![feature(start)]
1817
#![feature(rustc_attrs)]
1918
#![allow(dead_code)]
2019

@@ -56,7 +55,7 @@ pub struct NestedNonZero {
5655

5756
impl Default for NestedNonZero {
5857
fn default() -> Self {
59-
NestedNonZero { pre: 0, val: NonZeroU32::new(1).unwrap(), post: 0 }
58+
NestedNonZero { pre: 0, val: unsafe { NonZeroU32::new_unchecked(1) }, post: 0 }
6059
}
6160
}
6261

@@ -76,8 +75,7 @@ pub union Union2<A: Copy, B: Copy> {
7675
b: B,
7776
}
7877

79-
#[start]
80-
fn start(_: isize, _: *const *const u8) -> isize {
78+
pub fn test() {
8179
let _x: MyOption<NonZeroU32> = Default::default();
8280
let _y: EmbeddedDiscr = Default::default();
8381
let _z: MyOption<IndirectNonZero> = Default::default();
@@ -96,6 +94,4 @@ fn start(_: isize, _: *const *const u8) -> isize {
9694
// ...even when theoretically possible.
9795
let _j: MyOption<Union1<NonZeroU32>> = Default::default();
9896
let _k: MyOption<Union2<NonZeroU32, NonZeroU32>> = Default::default();
99-
100-
0
10197
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33
// ignore-pass
44
// ^-- needed because `--pass check` does not emit the output needed.
@@ -8,16 +8,12 @@
88
// (even if multiple functions), it is only printed once in the
99
// print-type-sizes output.
1010

11-
#![feature(start)]
12-
1311
pub struct SevenBytes([u8; 7]);
1412

1513
pub fn f1() {
1614
let _s: SevenBytes = SevenBytes([0; 7]);
1715
}
1816

19-
#[start]
20-
fn start(_: isize, _: *const *const u8) -> isize {
17+
pub fn test() {
2118
let _s: SevenBytes = SevenBytes([0; 7]);
22-
0
2319
}

src/test/ui/print_type_sizes/packed.rs

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33
// ignore-pass
44
// ^-- needed because `--pass check` does not emit the output needed.
@@ -13,11 +13,10 @@
1313
// padding and overall computed sizes can be quite different.
1414

1515
#![allow(dead_code)]
16-
#![feature(start)]
1716

1817
#[derive(Default)]
1918
#[repr(packed)]
20-
struct Packed1 {
19+
pub struct Packed1 {
2120
a: u8,
2221
b: u8,
2322
g: i32,
@@ -28,7 +27,7 @@ struct Packed1 {
2827

2928
#[derive(Default)]
3029
#[repr(packed(2))]
31-
struct Packed2 {
30+
pub struct Packed2 {
3231
a: u8,
3332
b: u8,
3433
g: i32,
@@ -40,7 +39,7 @@ struct Packed2 {
4039
#[derive(Default)]
4140
#[repr(packed(2))]
4241
#[repr(C)]
43-
struct Packed2C {
42+
pub struct Packed2C {
4443
a: u8,
4544
b: u8,
4645
g: i32,
@@ -50,20 +49,11 @@ struct Packed2C {
5049
}
5150

5251
#[derive(Default)]
53-
struct Padded {
52+
pub struct Padded {
5453
a: u8,
5554
b: u8,
5655
g: i32,
5756
c: u8,
5857
h: i16,
5958
d: u8,
6059
}
61-
62-
#[start]
63-
fn start(_: isize, _: *const *const u8) -> isize {
64-
let _c: Packed1 = Default::default();
65-
let _d: Packed2 = Default::default();
66-
let _e: Packed2C = Default::default();
67-
let _f: Padded = Default::default();
68-
0
69-
}

src/test/ui/print_type_sizes/padding.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33

44
// This file illustrates how padding is handled: alignment
@@ -9,7 +9,6 @@
99
// aligned (while on most it is 8-byte aligned) and so the resulting
1010
// padding and overall computed sizes can be quite different.
1111

12-
#![feature(start)]
1312
#![allow(dead_code)]
1413

1514
struct S {
@@ -27,8 +26,3 @@ enum E2 {
2726
A(i8, i32),
2827
B(S),
2928
}
30-
31-
#[start]
32-
fn start(_: isize, _: *const *const u8) -> isize {
33-
0
34-
}

src/test/ui/print_type_sizes/repr-align.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Z print-type-sizes
1+
// compile-flags: -Z print-type-sizes --crate-type=lib
22
// build-pass
33
// ignore-pass
44
// ^-- needed because `--pass check` does not emit the output needed.
@@ -11,7 +11,7 @@
1111
// It avoids using u64/i64 because on some targets that is only 4-byte
1212
// aligned (while on most it is 8-byte aligned) and so the resulting
1313
// padding and overall computed sizes can be quite different.
14-
#![feature(start)]
14+
1515
#![allow(dead_code)]
1616

1717
#[repr(align(16))]
@@ -24,15 +24,9 @@ enum E {
2424
}
2525

2626
#[derive(Default)]
27-
struct S {
27+
pub struct S {
2828
a: i32,
2929
b: i32,
3030
c: A,
3131
d: i8,
3232
}
33-
34-
#[start]
35-
fn start(_: isize, _: *const *const u8) -> isize {
36-
let _s: S = Default::default();
37-
0
38-
}

0 commit comments

Comments
 (0)