use super::*;
impl StructuredBodyLowerer<'_, '_> {
pub(super) fn build_active_loop_context(
&self,
candidate: &LoopCandidate,
post_loop: BlockRef,
target_overrides: &BTreeMap<TempId, HirLValue>,
states: &[LoopStateSlot],
) -> Option<ActiveLoopContext> {
let downstream_post_loop = self.normalized_post_loop_successor(post_loop);
let mut break_exits = BTreeMap::new();
for exit in candidate
.exits
.iter()
.copied()
.filter(|exit| *exit != post_loop)
{
if block_is_terminal_exit(self.lowering, exit) {
continue;
}
if self.loop_exit_region_is_terminal(candidate, exit, post_loop, downstream_post_loop) {
continue;
}
if downstream_post_loop == Some(exit) {
continue;
}
break_exits.insert(
exit,
self.lower_break_exit_pad(
exit,
post_loop,
downstream_post_loop,
target_overrides,
states,
)?,
);
}
let continue_target = candidate.continue_target;
let continue_sources = continue_target
.map(|target| {
self.lowering
.structure
.goto_requirements
.iter()
.filter(|requirement| {
requirement.reason == crate::structure::GotoReason::UnstructuredContinueLike
&& requirement.to == target
&& candidate.blocks.contains(&requirement.from)
})
.map(|requirement| requirement.from)
.collect::<BTreeSet<_>>()
})
.unwrap_or_default();
Some(ActiveLoopContext {
header: candidate.header,
loop_blocks: BTreeSet::new(),
post_loop,
downstream_post_loop,
continue_target,
continue_sources,
break_exits,
state_slots: Vec::new(),
})
}
pub(super) fn normalized_post_loop_successor(&self, post_loop: BlockRef) -> Option<BlockRef> {
let (_instr_ref, instr) = self.block_terminator(post_loop)?;
let LowInstr::Jump(jump) = instr else {
return None;
};
let target = self.lowering.cfg.instr_to_block[jump.target.index()];
self.lower_block_prefix(post_loop, false, &BTreeMap::new())?;
Some(target)
}
pub(super) fn loop_state_inside_exit_blocks(
&self,
candidate: &LoopCandidate,
post_loop: BlockRef,
) -> Option<BTreeSet<BlockRef>> {
let downstream_post_loop = self.normalized_post_loop_successor(post_loop);
let mut inside_blocks = candidate.blocks.clone();
for exit in candidate
.exits
.iter()
.copied()
.filter(|exit| *exit != post_loop)
{
if block_is_terminal_exit(self.lowering, exit) {
continue;
}
if self.loop_exit_region_is_terminal(candidate, exit, post_loop, downstream_post_loop) {
continue;
}
if downstream_post_loop == Some(exit) {
continue;
}
self.lower_break_exit_pad(
exit,
post_loop,
downstream_post_loop,
&BTreeMap::new(),
&[],
)?;
inside_blocks.insert(exit);
}
Some(inside_blocks)
}
fn loop_exit_region_is_terminal(
&self,
candidate: &LoopCandidate,
exit: BlockRef,
post_loop: BlockRef,
downstream_post_loop: Option<BlockRef>,
) -> bool {
fn visit(
lowerer: &StructuredBodyLowerer<'_, '_>,
candidate: &LoopCandidate,
block: BlockRef,
post_loop: BlockRef,
downstream_post_loop: Option<BlockRef>,
visiting: &mut BTreeSet<BlockRef>,
memo: &mut BTreeMap<BlockRef, bool>,
) -> bool {
if block == post_loop
|| Some(block) == downstream_post_loop
|| candidate.blocks.contains(&block)
|| !lowerer.lowering.cfg.reachable_blocks.contains(&block)
{
return false;
}
if block == lowerer.lowering.cfg.exit_block
|| block_is_terminal_exit(lowerer.lowering, block)
{
return true;
}
if let Some(result) = memo.get(&block).copied() {
return result;
}
if !visiting.insert(block) {
return false;
}
let result = lowerer.lowering.cfg.succs[block.index()]
.iter()
.all(|edge_ref| {
let successor = lowerer.lowering.cfg.edges[edge_ref.index()].to;
visit(
lowerer,
candidate,
successor,
post_loop,
downstream_post_loop,
visiting,
memo,
)
});
visiting.remove(&block);
memo.insert(block, result);
result
}
visit(
self,
candidate,
exit,
post_loop,
downstream_post_loop,
&mut BTreeSet::new(),
&mut BTreeMap::new(),
)
}
}