Skip to content

Commit 5f5ae17

Browse files
Consider discriminant fields that are ordered before variant fields
1 parent ed620cf commit 5f5ae17

File tree

3 files changed

+59
-7
lines changed

3 files changed

+59
-7
lines changed

compiler/rustc_ty_utils/src/layout.rs

Lines changed: 23 additions & 7 deletions
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))),
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// compile-flags: -Z print-type-sizes
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(start, generators)]
9+
10+
#[start]
11+
fn start(_: isize, _: *const *const u8) -> isize {
12+
let a = || {
13+
{
14+
let w: i32 = 4;
15+
yield;
16+
drop(w);
17+
}
18+
{
19+
let z: i32 = 7;
20+
yield;
21+
drop(z);
22+
}
23+
};
24+
0
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
print-type-size type: `[generator@$DIR/generator_discr_placement.rs:12:13: 12: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

0 commit comments

Comments
 (0)