11//! Validates the MIR to ensure that invariants are upheld.
22
3- use rustc_data_structures:: fx:: FxHashSet ;
3+ use std:: collections:: hash_map:: Entry ;
4+
5+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
46use rustc_index:: bit_set:: BitSet ;
57use rustc_infer:: traits:: Reveal ;
68use rustc_middle:: mir:: interpret:: Scalar ;
@@ -18,7 +20,7 @@ use rustc_mir_dataflow::storage::always_storage_live_locals;
1820use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
1921use rustc_target:: abi:: { Size , VariantIdx } ;
2022
21- #[ derive( Copy , Clone , Debug ) ]
23+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
2224enum EdgeKind {
2325 Unwind ,
2426 Normal ,
@@ -57,7 +59,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
5759 . iterate_to_fixpoint ( )
5860 . into_results_cursor ( body) ;
5961
60- TypeChecker {
62+ let mut checker = TypeChecker {
6163 when : & self . when ,
6264 body,
6365 tcx,
@@ -67,8 +69,9 @@ impl<'tcx> MirPass<'tcx> for Validator {
6769 storage_liveness,
6870 place_cache : Vec :: new ( ) ,
6971 value_cache : Vec :: new ( ) ,
70- }
71- . visit_body ( body) ;
72+ } ;
73+ checker. visit_body ( body) ;
74+ checker. check_cleanup_control_flow ( ) ;
7275 }
7376}
7477
@@ -134,6 +137,55 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
134137 }
135138 }
136139
140+ fn check_cleanup_control_flow ( & self ) {
141+ let doms = self . body . basic_blocks . dominators ( ) ;
142+ let mut post_contract_node = FxHashMap :: default ( ) ;
143+ 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 {
149+ dom_path. push ( bb) ;
150+ bb = doms. immediate_dominator ( bb) ;
151+ }
152+ let root = * dom_path. last ( ) . unwrap ( ) ;
153+ for bb in dom_path {
154+ post_contract_node. insert ( bb, root) ;
155+ }
156+ root
157+ } ;
158+
159+ let mut parent = FxHashMap :: default ( ) ;
160+ for ( bb, bb_data) in self . body . basic_blocks . iter_enumerated ( ) {
161+ if !bb_data. is_cleanup || !self . reachable_blocks . contains ( bb) {
162+ continue ;
163+ }
164+ let bb = get_post_contract_node ( bb) ;
165+ for s in bb_data. terminator ( ) . successors ( ) {
166+ let s = get_post_contract_node ( s) ;
167+ if s == bb {
168+ continue ;
169+ }
170+ match parent. entry ( bb) {
171+ Entry :: Vacant ( e) => {
172+ e. insert ( s) ;
173+ }
174+ Entry :: Occupied ( e) if s != * e. get ( ) => self . fail (
175+ Location { block : bb, statement_index : 0 } ,
176+ format ! (
177+ "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}" ,
178+ bb,
179+ s,
180+ * e. get( )
181+ )
182+ ) ,
183+ Entry :: Occupied ( _) => ( ) ,
184+ }
185+ }
186+ }
187+ }
188+
137189 /// Check if src can be assigned into dest.
138190 /// This is not precise, it will accept some incorrect assignments.
139191 fn mir_assign_valid_types ( & self , src : Ty < ' tcx > , dest : Ty < ' tcx > ) -> bool {
0 commit comments