Skip to content

Avoid terminator: None in custom mir parser - #165502

Open
panstromek wants to merge 2 commits into
rust-lang:mainfrom
panstromek:new-terminator-option-1
Open

panstromek wants to merge 2 commits into
rust-lang:mainfrom
panstromek:new-terminator-option-1

Conversation

@panstromek

@panstromek panstromek commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

First step towards replacing Option<Terminator> with Terminator in BasicBlockData. See this thread for more info.

This is a slight restructuring of the custom mir parser that avoids creating dummy basic blocks that don't have terminators. More details in individual commits.

@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 Nov 5, 2026
@rustbot

rustbot commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

r? @nnethercote

rustbot has assigned @nnethercote.
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: compiler, mir
  • compiler, mir expanded to 77 candidates
  • Random selection from 20 candidates

@panstromek panstromek changed the title Avoid terminator: None in custom mir parsing Avoid terminator: None in custom mir parser Nov 5, 2026
@rust-log-analyzer

This comment has been minimized.

…minator

This was only needed because `is_cleanup` property was parsed separately from the basic block data, so we do this by parsing `is_cleanup` into a vec of bools first, and later transform it into the bb data when parsing blocks themselves.
@panstromek
panstromek force-pushed the new-terminator-option-1 branch from 16f2ca6 to ca5705a Compare November 5, 2026 15:02

@hanna-kruppe hanna-kruppe left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks correct, but I have two questions.

r? me @rustbot author

View changes since this review

Comment on lines +187 to +189
// just a sanity check to make sure order of bbs
// in cleanup blocks matches the one in the body
assert_eq!(inserted_bb, bb);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand what this checks / what "order of BBs in cleanup blocks" means. We're comparing bb = i, which we get from enumerate of block_defs, with the index in the body's basic block list. That should always be true, unless something sneakily pushes another basic block before this loop or inside parse_block_def. Maybe that's what you had in mind, but either way the comment needs to be clearer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe a better comment would be just "to make sure that these line up correctly." or "to make sure we're adding the correct block id"

But I'll step back and explain the issue, because this is a variation of a problem that happens in multiple places that use terminator: None, and maybe you'll get a better idea for what you would want to see commented here.

This code parses the blocks from two sources in two passes - first there's a list of declarations, parsed by parse_block_decls. We just get block numbers and is_cleanup flags from those. And then there's this code which parses the blocks themselves. Even here, these two list already don't line up. The declaration list omits the entry block, but definition list includes it, that's why we add the empty is_cleanup: false during construction.

The original code only has one place that creates basic blocks and assigns their ids (the declaration parser), and then always refers to existing blocks by id. The new code has (kinda) two places that assign ids. One is in the declaration parser, and one is the definition parser here. The old code implicitly prevented situation where you parse the block definition into incorrect id, the new code ensures that with the assert.

That should always be true, unless something sneakily pushes another basic block before this loop or inside parse_block_def

Yes. I added the assert to avoid this code implicitly assuming that other parts of the pipeline don't add basic blocks in other places (and I've actually hit this assert during refactoring, that's how I found out the implicit basic block added during construction)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Okay, I understand better now, thanks. I think I was also misguided by my knowledge of the surface syntax that mir! accepts. Some unorganized thoughts:

  • It's not just the cleanup block data, block_map also has to match how we actually assign block indices. For example, you can name a (non-entry) block foo and references to block foo elsewhere will be resolved to the index of foo within the list of block declarations (+1 for implicit entry block). That's another reason why order of blocks in body has to match declaration order.
  • I think this isn't entirely a new problem. Before, we had only one loop allocate blocks in body, but we still had the same implicit dependency on how these blocks get initialized in parse_block_defs. Trying to create too many blocks probably would've panicked with an index out of bounds, but initializing fewer than were declared could've gone silently wrong.
  • While block def parsing currently separates "allocate block index in body" from "actually push the block" this does not seem inherent. We could instead call parse_block_def first, get back a BasicBlockData , push it into the body, and then use the index at which it was inserted to look up whether it's a cleanup block. That would remove the "block gets wrong is_cleanup" problem, but won't do anything to ensure the block is created at the right index w.r.t. block_map.
  • It seems more of a coincidence that the declaration phase "allocates block indices" by pushing to the cleanup_blocks vector.
    • We just happen to have another IndexVec indexed by basic blocks at hand, but in any other context, it wouldn't be the canonical source for which blocks exist.
    • Arguably we don't even need the "is cleanup" flag in the declarations and could instead associate it with the block definition.
    • I realize that we need to assign block indices somehow, to populate block_map if nothing else, and so using an IndexVec and populating it during declaration parsing kills two birds with one stone. But the more I think about it, the more it seems "too clever for its own good".

I now have some ideas for how this whole process could be refactored, but that's probably way out of scope for this PR. Instead I would propose:

  1. At the end, assert that checks that all of cleanup_blocks, block_map, and the resulting body agree on the total number of blocks (block_map should have one less because the entry block is anonymous). That won't catch getting the order wrong, but the most likely way to mess up the order is by creating too extra blocks or skipping one.
  2. For this comment, I'd say the main point for me is ensuring we create one block per block_def so that order and indices match the block declarations parsed above. Mentioning only cleanup without names here is misleading, but mentioning both together could be helpful.

thir: &'a Thir<'tcx>,
source_scope: SourceScope,
body: &'a mut Body<'tcx>,
cleanup_blocks: IndexVec<BasicBlock, bool>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this be a GrowableBitSet? The memory footprint doesn't matter, but it seems conceptually more appropriate than IndexVec<_, bool>.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe, I was thinking about it, but I wanted to preserve the hidden assumptions from the original code - this code not only tracks the cleanup flags, but it also allocates basic block ids with this vector.

If there's fewer block declarations then block definitions, accessing this vec will panic when block definitions are parsed. GrowableBitSet wouldn't panic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As mentioned in the other comment I just posted, I think the use of this IndexVec to allocate block indices is too clever. I'm fine with leaving it alone in this PR, but maybe add a comment explaining this dual purpose?

@rustbot rustbot assigned hanna-kruppe and unassigned nnethercote Nov 7, 2026
@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 7, 2026
@rustbot

rustbot commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

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

@rust-bors

rust-bors Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

☔ The latest upstream changes (presumably #165597) made this pull request unmergeable. Please resolve the merge conflicts by rebasing.

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.

5 participants