@@ -11,6 +11,11 @@ use rustc_middle::{
11
11
} ;
12
12
use rustc_span:: def_id:: DefId ;
13
13
14
+ enum EdgeKind {
15
+ Unwind ,
16
+ Other ,
17
+ }
18
+
14
19
pub struct Validator {
15
20
/// Describes at which point in the pipeline this validation is happening.
16
21
pub when : String ,
@@ -49,8 +54,25 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
49
54
) ;
50
55
}
51
56
52
- fn check_bb ( & self , location : Location , bb : BasicBlock ) {
53
- if self . body . basic_blocks ( ) . get ( bb) . is_none ( ) {
57
+ fn check_bb ( & self , location : Location , bb : BasicBlock , edge_kind : EdgeKind ) {
58
+ if let Some ( bb) = self . body . basic_blocks ( ) . get ( bb) {
59
+ let src = self . body . basic_blocks ( ) . get ( location. block ) . unwrap ( ) ;
60
+ match ( src. is_cleanup , bb. is_cleanup , edge_kind) {
61
+ // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
62
+ ( false , false , EdgeKind :: Other )
63
+ // Non-cleanup blocks can jump to cleanup blocks along unwind edges
64
+ | ( false , true , EdgeKind :: Unwind )
65
+ // Cleanup blocks can jump to cleanup blocks along unwind edges
66
+ | ( true , true , EdgeKind :: Unwind ) => { }
67
+ // All other jumps are invalid
68
+ _ => {
69
+ self . fail (
70
+ location,
71
+ format ! ( "encountered jump that does not respect unwind invariants {:?}" , bb)
72
+ )
73
+ }
74
+ }
75
+ } else {
54
76
self . fail ( location, format ! ( "encountered jump to invalid basic block {:?}" , bb) )
55
77
}
56
78
}
@@ -92,7 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
92
114
fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
93
115
match & terminator. kind {
94
116
TerminatorKind :: Goto { target } => {
95
- self . check_bb ( location, * target) ;
117
+ self . check_bb ( location, * target, EdgeKind :: Other ) ;
96
118
}
97
119
TerminatorKind :: SwitchInt { targets, values, .. } => {
98
120
if targets. len ( ) != values. len ( ) + 1 {
@@ -106,19 +128,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
106
128
) ;
107
129
}
108
130
for target in targets {
109
- self . check_bb ( location, * target) ;
131
+ self . check_bb ( location, * target, EdgeKind :: Other ) ;
110
132
}
111
133
}
112
134
TerminatorKind :: Drop { target, unwind, .. } => {
113
- self . check_bb ( location, * target) ;
135
+ self . check_bb ( location, * target, EdgeKind :: Other ) ;
114
136
if let Some ( unwind) = unwind {
115
- self . check_bb ( location, * unwind) ;
137
+ self . check_bb ( location, * unwind, EdgeKind :: Unwind ) ;
116
138
}
117
139
}
118
140
TerminatorKind :: DropAndReplace { target, unwind, .. } => {
119
- self . check_bb ( location, * target) ;
141
+ self . check_bb ( location, * target, EdgeKind :: Other ) ;
120
142
if let Some ( unwind) = unwind {
121
- self . check_bb ( location, * unwind) ;
143
+ self . check_bb ( location, * unwind, EdgeKind :: Unwind ) ;
122
144
}
123
145
}
124
146
TerminatorKind :: Call { func, destination, cleanup, .. } => {
@@ -131,10 +153,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
131
153
) ,
132
154
}
133
155
if let Some ( ( _, target) ) = destination {
134
- self . check_bb ( location, * target) ;
156
+ self . check_bb ( location, * target, EdgeKind :: Other ) ;
135
157
}
136
158
if let Some ( cleanup) = cleanup {
137
- self . check_bb ( location, * cleanup) ;
159
+ self . check_bb ( location, * cleanup, EdgeKind :: Unwind ) ;
138
160
}
139
161
}
140
162
TerminatorKind :: Assert { cond, target, cleanup, .. } => {
@@ -148,30 +170,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
148
170
) ,
149
171
) ;
150
172
}
151
- self . check_bb ( location, * target) ;
173
+ self . check_bb ( location, * target, EdgeKind :: Other ) ;
152
174
if let Some ( cleanup) = cleanup {
153
- self . check_bb ( location, * cleanup) ;
175
+ self . check_bb ( location, * cleanup, EdgeKind :: Unwind ) ;
154
176
}
155
177
}
156
178
TerminatorKind :: Yield { resume, drop, .. } => {
157
- self . check_bb ( location, * resume) ;
179
+ self . check_bb ( location, * resume, EdgeKind :: Other ) ;
158
180
if let Some ( drop) = drop {
159
- self . check_bb ( location, * drop) ;
181
+ self . check_bb ( location, * drop, EdgeKind :: Other ) ;
160
182
}
161
183
}
162
184
TerminatorKind :: FalseEdge { real_target, imaginary_target } => {
163
- self . check_bb ( location, * real_target) ;
164
- self . check_bb ( location, * imaginary_target) ;
185
+ self . check_bb ( location, * real_target, EdgeKind :: Other ) ;
186
+ self . check_bb ( location, * imaginary_target, EdgeKind :: Other ) ;
165
187
}
166
188
TerminatorKind :: FalseUnwind { real_target, unwind } => {
167
- self . check_bb ( location, * real_target) ;
189
+ self . check_bb ( location, * real_target, EdgeKind :: Other ) ;
168
190
if let Some ( unwind) = unwind {
169
- self . check_bb ( location, * unwind) ;
191
+ self . check_bb ( location, * unwind, EdgeKind :: Unwind ) ;
170
192
}
171
193
}
172
194
TerminatorKind :: InlineAsm { destination, .. } => {
173
195
if let Some ( destination) = destination {
174
- self . check_bb ( location, * destination) ;
196
+ self . check_bb ( location, * destination, EdgeKind :: Other ) ;
175
197
}
176
198
}
177
199
// Nothing to validate for these.
0 commit comments