11//! Validates the MIR to ensure that invariants are upheld.
22
3- use std:: collections:: hash_map:: Entry ;
4-
53use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
64use rustc_index:: bit_set:: BitSet ;
5+ use rustc_index:: vec:: IndexVec ;
76use rustc_infer:: traits:: Reveal ;
87use rustc_middle:: mir:: interpret:: Scalar ;
98use rustc_middle:: mir:: visit:: NonUseContext :: VarDebugInfo ;
@@ -140,23 +139,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
140139 fn check_cleanup_control_flow ( & self ) {
141140 let doms = self . body . basic_blocks . dominators ( ) ;
142141 let mut post_contract_node = FxHashMap :: default ( ) ;
142+ // Reusing the allocation across invocations of the closure
143+ let mut dom_path = vec ! [ ] ;
143144 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 ) ;
149150 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 ( ..) {
154157 post_contract_node. insert ( bb, root) ;
155158 }
156159 root
157160 } ;
158161
159- let mut parent = FxHashMap :: default ( ) ;
162+ let mut parent = IndexVec :: from_elem ( None , & self . body . basic_blocks ) ;
160163 for ( bb, bb_data) in self . body . basic_blocks . iter_enumerated ( ) {
161164 if !bb_data. is_cleanup || !self . reachable_blocks . contains ( bb) {
162165 continue ;
@@ -167,23 +170,49 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
167170 if s == bb {
168171 continue ;
169172 }
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) ;
173177 }
174- Entry :: Occupied ( e) if s != * e. get ( ) => self . fail (
178+ Some ( e) if * e == s => ( ) ,
179+ Some ( e) => self . fail (
175180 Location { block : bb, statement_index : 0 } ,
176181 format ! (
177182 "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}" ,
178183 bb,
179184 s,
180- * e. get ( )
185+ * e
181186 )
182187 ) ,
183- Entry :: Occupied ( _) => ( ) ,
184188 }
185189 }
186190 }
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+ }
187216 }
188217
189218 /// Check if src can be assigned into dest.
0 commit comments