Skip to content

Refactor enum layout calculation - #163749

Open
susitsm wants to merge 17 commits into
rust-lang:mainfrom
susitsm:refactor-enum-layout
Open

susitsm wants to merge 17 commits into
rust-lang:mainfrom
susitsm:refactor-enum-layout

Conversation

@susitsm

@susitsm susitsm commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Moves layout calculation of enums to its own file, chunks large functions into smaller ones and makes some variables immutable

Motivation: Surely I wasn't the only one who tried to understand niche optimization and enum layout calculation, only to find a 500 lines long function full of mutable variables.

It is a +600-500 PR but reviewing commit-by-commit should be manageable. Commits compile and refactor a single part of layout calculation.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 4, 2026
@rustbot

rustbot commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

r? @jieyouxu

rustbot has assigned @jieyouxu.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: codegen, compiler
  • codegen, compiler expanded to 77 candidates
  • Random selection from 18 candidates

@rust-bors

This comment has been minimized.

@susitsm
susitsm force-pushed the refactor-enum-layout branch from c8a5f50 to 80bc421 Compare October 9, 2026 05:17
@rustbot

This comment has been minimized.

@WaffleLapkin WaffleLapkin self-assigned this Oct 16, 2026
@rust-bors

This comment has been minimized.

@jieyouxu jieyouxu left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Broadly seems like a good readability improvement. Some nits. I left review comments commit-by-commit so might show up weirdly.

@rustbot author

View changes since this review

Comment on lines +384 to +385
//
// How can this be nonzero when everything else is a ZST? `others_zst` is true here

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't others_zst about the other variants rather than fields of the largest variant?

E.g. if you have

#[repr(C)]
struct Foo(u32, bool);

then Some variant of Option<Foo> would use the bool as the niche at offset 4. None would be the "others ZST" here.

align_abi: Align,
}

fn try_fixup_non_niche_variants<VariantIdx: Idx, FieldIdx: Idx>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by this name 🤔 What this loop is doing is that we're trying to fit niche variants1.

I think there might be a bit of a terminology thing here: say if we have

Option<Foo>
    Some { a: u32, b: bool }
    None

here None is the niche variant. Some with the bool would be the untagged_variant. Perhaps a better name for this would be try_fit_niche_variants? Or likely more accurate but maybe less straightforward, try_fit_other_variants 🤷 This could really use a doc comment TL;DR-ing the intent, i.e. trying to fit the variants that are not the untagged_variant.

Footnotes

  1. That's a bit of a lie, this also handles absent2 variants I think, so it's technically "everything that's not untagged_variant". But like calling it non_untagged_variants is probably trolling. Perhaps confusingly, this is specifically for niche encoding, so absent variants outside of niche_variants doesn't need a tag value. ↩

  2. There's more nuance here that absent variants != ZST, and for a variant to qualify as an absent variant it seems that it needs to both satisfy (1) have at least one uninhabited field so it's never constructible, and (2) every field need to be 1-ZST. ↩

Comment on lines +38 to +54
let best_layout = match (tagged_layout, niche_filling_layout) {
(tl, Some(nl)) => {
// Pick the smaller layout; otherwise,
// pick the layout with the larger niche; otherwise,
// pick tagged as it has simpler codegen.
use cmp::Ordering::*;
let niche_size = |l: &LayoutData<FieldIdx, VariantIdx>| {
l.largest_niche.map_or(0, |n| n.available(dl))
};
match (tl.size.cmp(&nl.size), niche_size(&tl).cmp(&niche_size(&nl))) {
(Greater, _) => nl,
(Equal, Less) => nl,
_ => tl,
}
}
(tl, None) => tl,
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can probably pull out the niche_filling_layout as an early return, i.e. let Some(nl) = niche_filling_layout else { return Ok(tagged_layout) };. This match seems a bit unnecessarily complex.

Ok(best_layout)
}

fn constructable_variant<'a, VariantIdx, FieldIdx, F>(variant: &IndexSlice<FieldIdx, F>) -> bool

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's usually spelled constructible. Also, I would call this instead variant_is_inhabited, since I find it a bit more ambiguous as there's also the concept of "absent" in layout.

}
}
}
let abi = calculate_tagged_abi(calculator, &layout_variants, &variants, size, align, tag);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you can probably lose the borrow on variants, since it's already &IndexSlice<...>

Comment on lines +230 to +231
// However, it is fine to make discr type however large (as an optimisation)
// after this point – we’ll just truncate the value we load in codegen.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'd move this comment back to the call-site after sanity_check_ity, it's more like a positional invariant that holds after the sanity check on the call-site.

Comment on lines 366 to 367
// If the alignment is not larger than the chosen discriminant size,
// don't use the alignment as the final size.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment no longer belongs here, right? Maybe move it to the .max()?

Comment on lines +304 to +311
let prefix_align = if repr.c() {
variants
.iter()
.flatten()
.fold(min_ity.align(dl).abi, |prefix_align, field| prefix_align.max(field.align.abi))
} else {
min_ity.align(dl).abi
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe pull out a local, like let tag_align = min_ity.align(dl).abi;, it's a bit of a mouthful repeating here.

}
}

fn calculate_tagged_tag<FieldIdx, VariantIdx>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also adjusts variant field offsets/sizes 🤔 Maybe call this sth like finalize_tag_and_patch_variants (not a fantastic name, but sth to that effect).

};
trace!(?min_negative, ?max_positive);

// Integer::discr_range_of_repr(tcx, ty, &repr, min, max);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably leftover debugging code or sth, drop it?

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 5, 2026
@rustbot

rustbot commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@susitsm
susitsm force-pushed the refactor-enum-layout branch from 80bc421 to ce001ad Compare November 5, 2026 13:39
@rustbot

rustbot commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants