Skip to content

Commit 161a8bf

Browse files
authored
Rollup merge of #73133 - doctorn:unwind-mir-validation, r=jonas-schievink
Enforce unwind invariants I had a quick look at #72959. The failure message probably needs to be more detailed but I just wanted to check I got the right idea. I have no idea how to right a test for this either... r? @jonas-schievink Resolves #72959 (hypothetically)
2 parents 4ac3efa + 9495ee2 commit 161a8bf

File tree

1 file changed

+48
-19
lines changed

1 file changed

+48
-19
lines changed

src/librustc_mir/transform/validate.rs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ use rustc_middle::{
1111
};
1212
use rustc_span::def_id::DefId;
1313

14+
#[derive(Copy, Clone, Debug)]
15+
enum EdgeKind {
16+
Unwind,
17+
Normal,
18+
}
19+
1420
pub struct Validator {
1521
/// Describes at which point in the pipeline this validation is happening.
1622
pub when: String,
@@ -49,8 +55,31 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
4955
);
5056
}
5157

52-
fn check_bb(&self, location: Location, bb: BasicBlock) {
53-
if self.body.basic_blocks().get(bb).is_none() {
58+
fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
59+
if let Some(bb) = self.body.basic_blocks().get(bb) {
60+
let src = self.body.basic_blocks().get(location.block).unwrap();
61+
match (src.is_cleanup, bb.is_cleanup, edge_kind) {
62+
// Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
63+
(false, false, EdgeKind::Normal)
64+
// Non-cleanup blocks can jump to cleanup blocks along unwind edges
65+
| (false, true, EdgeKind::Unwind)
66+
// Cleanup blocks can jump to cleanup blocks along non-unwind edges
67+
| (true, true, EdgeKind::Normal) => {}
68+
// All other jumps are invalid
69+
_ => {
70+
self.fail(
71+
location,
72+
format!(
73+
"{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
74+
edge_kind,
75+
bb,
76+
src.is_cleanup,
77+
bb.is_cleanup,
78+
)
79+
)
80+
}
81+
}
82+
} else {
5483
self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
5584
}
5685
}
@@ -92,7 +121,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
92121
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
93122
match &terminator.kind {
94123
TerminatorKind::Goto { target } => {
95-
self.check_bb(location, *target);
124+
self.check_edge(location, *target, EdgeKind::Normal);
96125
}
97126
TerminatorKind::SwitchInt { targets, values, .. } => {
98127
if targets.len() != values.len() + 1 {
@@ -106,19 +135,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
106135
);
107136
}
108137
for target in targets {
109-
self.check_bb(location, *target);
138+
self.check_edge(location, *target, EdgeKind::Normal);
110139
}
111140
}
112141
TerminatorKind::Drop { target, unwind, .. } => {
113-
self.check_bb(location, *target);
142+
self.check_edge(location, *target, EdgeKind::Normal);
114143
if let Some(unwind) = unwind {
115-
self.check_bb(location, *unwind);
144+
self.check_edge(location, *unwind, EdgeKind::Unwind);
116145
}
117146
}
118147
TerminatorKind::DropAndReplace { target, unwind, .. } => {
119-
self.check_bb(location, *target);
148+
self.check_edge(location, *target, EdgeKind::Normal);
120149
if let Some(unwind) = unwind {
121-
self.check_bb(location, *unwind);
150+
self.check_edge(location, *unwind, EdgeKind::Unwind);
122151
}
123152
}
124153
TerminatorKind::Call { func, destination, cleanup, .. } => {
@@ -131,10 +160,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
131160
),
132161
}
133162
if let Some((_, target)) = destination {
134-
self.check_bb(location, *target);
163+
self.check_edge(location, *target, EdgeKind::Normal);
135164
}
136165
if let Some(cleanup) = cleanup {
137-
self.check_bb(location, *cleanup);
166+
self.check_edge(location, *cleanup, EdgeKind::Unwind);
138167
}
139168
}
140169
TerminatorKind::Assert { cond, target, cleanup, .. } => {
@@ -148,30 +177,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
148177
),
149178
);
150179
}
151-
self.check_bb(location, *target);
180+
self.check_edge(location, *target, EdgeKind::Normal);
152181
if let Some(cleanup) = cleanup {
153-
self.check_bb(location, *cleanup);
182+
self.check_edge(location, *cleanup, EdgeKind::Unwind);
154183
}
155184
}
156185
TerminatorKind::Yield { resume, drop, .. } => {
157-
self.check_bb(location, *resume);
186+
self.check_edge(location, *resume, EdgeKind::Normal);
158187
if let Some(drop) = drop {
159-
self.check_bb(location, *drop);
188+
self.check_edge(location, *drop, EdgeKind::Normal);
160189
}
161190
}
162191
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
163-
self.check_bb(location, *real_target);
164-
self.check_bb(location, *imaginary_target);
192+
self.check_edge(location, *real_target, EdgeKind::Normal);
193+
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
165194
}
166195
TerminatorKind::FalseUnwind { real_target, unwind } => {
167-
self.check_bb(location, *real_target);
196+
self.check_edge(location, *real_target, EdgeKind::Normal);
168197
if let Some(unwind) = unwind {
169-
self.check_bb(location, *unwind);
198+
self.check_edge(location, *unwind, EdgeKind::Unwind);
170199
}
171200
}
172201
TerminatorKind::InlineAsm { destination, .. } => {
173202
if let Some(destination) = destination {
174-
self.check_bb(location, *destination);
203+
self.check_edge(location, *destination, EdgeKind::Normal);
175204
}
176205
}
177206
// Nothing to validate for these.

0 commit comments

Comments
 (0)