1
1
//! Validates the MIR to ensure that invariants are upheld.
2
2
3
- use std:: collections:: hash_map:: Entry ;
4
-
5
3
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
6
4
use rustc_index:: bit_set:: BitSet ;
5
+ use rustc_index:: vec:: IndexVec ;
7
6
use rustc_infer:: traits:: Reveal ;
8
7
use rustc_middle:: mir:: interpret:: Scalar ;
9
8
use rustc_middle:: mir:: visit:: NonUseContext :: VarDebugInfo ;
@@ -140,23 +139,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
140
139
fn check_cleanup_control_flow ( & self ) {
141
140
let doms = self . body . basic_blocks . dominators ( ) ;
142
141
let mut post_contract_node = FxHashMap :: default ( ) ;
142
+ // Reusing the allocation across invocations of the closure
143
+ let mut dom_path = vec ! [ ] ;
143
144
let mut get_post_contract_node = |mut bb| {
144
- if let Some ( res ) = post_contract_node . get ( & bb ) {
145
- return * res ;
146
- }
147
- let mut dom_path = vec ! [ ] ;
148
- while self . body . basic_blocks [ bb ] . is_cleanup {
145
+ let root = loop {
146
+ if let Some ( root ) = post_contract_node . get ( & bb ) {
147
+ break * root ;
148
+ }
149
+ let parent = doms . immediate_dominator ( bb ) ;
149
150
dom_path. push ( bb) ;
150
- bb = doms. immediate_dominator ( bb) ;
151
- }
152
- let root = * dom_path. last ( ) . unwrap ( ) ;
153
- for bb in dom_path {
151
+ if !self . body . basic_blocks [ parent] . is_cleanup {
152
+ break bb;
153
+ }
154
+ bb = parent;
155
+ } ;
156
+ for bb in dom_path. drain ( ..) {
154
157
post_contract_node. insert ( bb, root) ;
155
158
}
156
159
root
157
160
} ;
158
161
159
- let mut parent = FxHashMap :: default ( ) ;
162
+ let mut parent = IndexVec :: from_elem ( None , & self . body . basic_blocks ) ;
160
163
for ( bb, bb_data) in self . body . basic_blocks . iter_enumerated ( ) {
161
164
if !bb_data. is_cleanup || !self . reachable_blocks . contains ( bb) {
162
165
continue ;
@@ -167,23 +170,49 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
167
170
if s == bb {
168
171
continue ;
169
172
}
170
- match parent. entry ( bb) {
171
- Entry :: Vacant ( e) => {
172
- e. insert ( s) ;
173
+ let parent = & mut parent[ bb] ;
174
+ match parent {
175
+ None => {
176
+ * parent = Some ( s) ;
173
177
}
174
- Entry :: Occupied ( e) if s != * e. get ( ) => self . fail (
178
+ Some ( e) if * e == s => ( ) ,
179
+ Some ( e) => self . fail (
175
180
Location { block : bb, statement_index : 0 } ,
176
181
format ! (
177
182
"Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}" ,
178
183
bb,
179
184
s,
180
- * e. get ( )
185
+ * e
181
186
)
182
187
) ,
183
- Entry :: Occupied ( _) => ( ) ,
184
188
}
185
189
}
186
190
}
191
+
192
+ // Check for cycles
193
+ let mut stack = FxHashSet :: default ( ) ;
194
+ for i in 0 ..parent. len ( ) {
195
+ let mut bb = BasicBlock :: from_usize ( i) ;
196
+ stack. clear ( ) ;
197
+ stack. insert ( bb) ;
198
+ loop {
199
+ let Some ( parent ) = parent[ bb] . take ( ) else {
200
+ break
201
+ } ;
202
+ let no_cycle = stack. insert ( parent) ;
203
+ if !no_cycle {
204
+ self . fail (
205
+ Location { block : bb, statement_index : 0 } ,
206
+ format ! (
207
+ "Cleanup control flow violation: Cycle involving edge {:?} -> {:?}" ,
208
+ bb, parent,
209
+ ) ,
210
+ ) ;
211
+ break ;
212
+ }
213
+ bb = parent;
214
+ }
215
+ }
187
216
}
188
217
189
218
/// Check if src can be assigned into dest.
0 commit comments