Change:
Blocks that are part of unwinding code always need to marked as . This seems like an easy thing to check in the validation pass. I believe this would have caught a bug in the generator transform (a pretty long time ago). <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Good point! Moreover, cleanup block terminators should also only jump to cleanup blocks, and other terminators should only jump to non-cleanup blocks. Basically, unwind edges must be the only ones that cross the "cleanup to non-cleanup" boundary. (Miri is already checking this.)
claim

Discussion:
}; use rustc_span::def_id::DefId; <ins> #[derive(Copy, Clone, Debug)] enum EdgeKind { Unwind, Normal, } </ins> pub struct Validator { /// Describes at which point in the pipeline this validation is happening. pub when: String,

); } <del> fn check_bb(&self, location: Location, bb: BasicBlock) { if self.body.basic_blocks().get(bb).is_none() { </del> <ins> fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) { if let Some(bb) = self.body.basic_blocks().get(bb) { let src = self.body.basic_blocks().get(location.block).unwrap(); match (src.is_cleanup, bb.is_cleanup, edge_kind) { // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges (false, false, EdgeKind::Normal) // Non-cleanup blocks can jump to cleanup blocks along unwind edges | (false, true, EdgeKind::Unwind) // Cleanup blocks can jump to cleanup blocks along non-unwind edges | (true, true, EdgeKind::Normal) => {} // All other jumps are invalid _ => { self.fail( location, format!( "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})", edge_kind, bb, src.is_cleanup, bb.is_cleanup, ) ) } } } else { </ins> self.fail(location, format!("encountered jump to invalid basic block {:?}", bb)) } }

fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { match &terminator.kind { TerminatorKind::Goto { target } => { <del> self.check_bb(location, *target); </del> <ins> self.check_edge(location, *target, EdgeKind::Normal); </ins> } TerminatorKind::SwitchInt { targets, values, .. } => { if targets.len() != values.len() + 1 {

); } for target in targets { <del> self.check_bb(location, *target); </del> <ins> self.check_edge(location, *target, EdgeKind::Normal); </ins> } } TerminatorKind::Drop { target, unwind, .. } => { <del> self.check_bb(location, *target); </del> <ins> self.check_edge(location, *target, EdgeKind::Normal); </ins> if let Some(unwind) = unwind { <del> self.check_bb(location, *unwind); </del> <ins> self.check_edge(location, *unwind, EdgeKind::Unwind); </ins> } } TerminatorKind::DropAndReplace { target, unwind, .. } => { <del> self.check_bb(location, *target); </del> <ins> self.check_edge(location, *target, EdgeKind::Normal); </ins> if let Some(unwind) = unwind { <del> self.check_bb(location, *unwind); </del> <ins> self.check_edge(location, *unwind, EdgeKind::Unwind); </ins> } } TerminatorKind::Call { func, destination, cleanup, .. } => {

), } if let Some((_, target)) = destination { <del> self.check_bb(location, *target); </del> <ins> self.check_edge(location, *target, EdgeKind::Normal); </ins> } if let Some(cleanup) = cleanup { <del> self.check_bb(location, *cleanup); </del> <ins> self.check_edge(location, *cleanup, EdgeKind::Unwind); </ins> } } TerminatorKind::Assert { cond, target, cleanup, .. } => {

), ); } <del> self.check_bb(location, *target); </del> <ins> self.check_edge(location, *target, EdgeKind::Normal); </ins> if let Some(cleanup) = cleanup { <del> self.check_bb(location, *cleanup); </del> <ins> self.check_edge(location, *cleanup, EdgeKind::Unwind); </ins> } } TerminatorKind::Yield { resume, drop, .. } => { <del> self.check_bb(location, *resume); </del> <ins> self.check_edge(location, *resume, EdgeKind::Normal); </ins> if let Some(drop) = drop { <del> self.check_bb(location, *drop); </del> <ins> self.check_edge(location, *drop, EdgeKind::Normal); </ins> } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { <del> self.check_bb(location, *real_target); self.check_bb(location, *imaginary_target); </del> <ins> self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *imaginary_target, EdgeKind::Normal); </ins> } TerminatorKind::FalseUnwind { real_target, unwind } => { <del> self.check_bb(location, *real_target); </del> <ins> self.check_edge(location, *real_target, EdgeKind::Normal); </ins> if let Some(unwind) = unwind { <del> self.check_bb(location, *unwind); </del> <ins> self.check_edge(location, *unwind, EdgeKind::Unwind); </ins> } } TerminatorKind::InlineAsm { destination, .. } => { if let Some(destination) = destination { <del> self.check_bb(location, *destination); </del> <ins> self.check_edge(location, *destination, EdgeKind::Normal); </ins> } } // Nothing to validate for these.