@@ -11,6 +11,12 @@ use rustc_middle::{
11
11
} ;
12
12
use rustc_span:: def_id:: DefId ;
13
13
14
+ #[ derive( Copy , Clone , Debug ) ]
15
+ enum EdgeKind {
16
+ Unwind ,
17
+ Normal ,
18
+ }
19
+
14
20
pub struct Validator {
15
21
/// Describes at which point in the pipeline this validation is happening.
16
22
pub when : String ,
@@ -49,8 +55,31 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
49
55
) ;
50
56
}
51
57
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 {
54
83
self . fail ( location, format ! ( "encountered jump to invalid basic block {:?}" , bb) )
55
84
}
56
85
}
@@ -92,7 +121,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
92
121
fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
93
122
match & terminator. kind {
94
123
TerminatorKind :: Goto { target } => {
95
- self . check_bb ( location, * target) ;
124
+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
96
125
}
97
126
TerminatorKind :: SwitchInt { targets, values, .. } => {
98
127
if targets. len ( ) != values. len ( ) + 1 {
@@ -106,19 +135,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
106
135
) ;
107
136
}
108
137
for target in targets {
109
- self . check_bb ( location, * target) ;
138
+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
110
139
}
111
140
}
112
141
TerminatorKind :: Drop { target, unwind, .. } => {
113
- self . check_bb ( location, * target) ;
142
+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
114
143
if let Some ( unwind) = unwind {
115
- self . check_bb ( location, * unwind) ;
144
+ self . check_edge ( location, * unwind, EdgeKind :: Unwind ) ;
116
145
}
117
146
}
118
147
TerminatorKind :: DropAndReplace { target, unwind, .. } => {
119
- self . check_bb ( location, * target) ;
148
+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
120
149
if let Some ( unwind) = unwind {
121
- self . check_bb ( location, * unwind) ;
150
+ self . check_edge ( location, * unwind, EdgeKind :: Unwind ) ;
122
151
}
123
152
}
124
153
TerminatorKind :: Call { func, destination, cleanup, .. } => {
@@ -131,10 +160,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
131
160
) ,
132
161
}
133
162
if let Some ( ( _, target) ) = destination {
134
- self . check_bb ( location, * target) ;
163
+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
135
164
}
136
165
if let Some ( cleanup) = cleanup {
137
- self . check_bb ( location, * cleanup) ;
166
+ self . check_edge ( location, * cleanup, EdgeKind :: Unwind ) ;
138
167
}
139
168
}
140
169
TerminatorKind :: Assert { cond, target, cleanup, .. } => {
@@ -148,30 +177,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
148
177
) ,
149
178
) ;
150
179
}
151
- self . check_bb ( location, * target) ;
180
+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
152
181
if let Some ( cleanup) = cleanup {
153
- self . check_bb ( location, * cleanup) ;
182
+ self . check_edge ( location, * cleanup, EdgeKind :: Unwind ) ;
154
183
}
155
184
}
156
185
TerminatorKind :: Yield { resume, drop, .. } => {
157
- self . check_bb ( location, * resume) ;
186
+ self . check_edge ( location, * resume, EdgeKind :: Normal ) ;
158
187
if let Some ( drop) = drop {
159
- self . check_bb ( location, * drop) ;
188
+ self . check_edge ( location, * drop, EdgeKind :: Normal ) ;
160
189
}
161
190
}
162
191
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 ) ;
165
194
}
166
195
TerminatorKind :: FalseUnwind { real_target, unwind } => {
167
- self . check_bb ( location, * real_target) ;
196
+ self . check_edge ( location, * real_target, EdgeKind :: Normal ) ;
168
197
if let Some ( unwind) = unwind {
169
- self . check_bb ( location, * unwind) ;
198
+ self . check_edge ( location, * unwind, EdgeKind :: Unwind ) ;
170
199
}
171
200
}
172
201
TerminatorKind :: InlineAsm { destination, .. } => {
173
202
if let Some ( destination) = destination {
174
- self . check_bb ( location, * destination) ;
203
+ self . check_edge ( location, * destination, EdgeKind :: Normal ) ;
175
204
}
176
205
}
177
206
// Nothing to validate for these.
0 commit comments