1
1
//! Validates the MIR to ensure that invariants are upheld.
2
2
3
- use rustc_data_structures:: fx:: FxHashSet ;
3
+ use std:: collections:: hash_map:: Entry ;
4
+
5
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
4
6
use rustc_index:: bit_set:: BitSet ;
5
7
use rustc_infer:: traits:: Reveal ;
6
8
use rustc_middle:: mir:: interpret:: Scalar ;
@@ -18,7 +20,7 @@ use rustc_mir_dataflow::storage::always_storage_live_locals;
18
20
use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
19
21
use rustc_target:: abi:: { Size , VariantIdx } ;
20
22
21
- #[ derive( Copy , Clone , Debug ) ]
23
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
22
24
enum EdgeKind {
23
25
Unwind ,
24
26
Normal ,
@@ -57,7 +59,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
57
59
. iterate_to_fixpoint ( )
58
60
. into_results_cursor ( body) ;
59
61
60
- TypeChecker {
62
+ let mut checker = TypeChecker {
61
63
when : & self . when ,
62
64
body,
63
65
tcx,
@@ -67,8 +69,9 @@ impl<'tcx> MirPass<'tcx> for Validator {
67
69
storage_liveness,
68
70
place_cache : Vec :: new ( ) ,
69
71
value_cache : Vec :: new ( ) ,
70
- }
71
- . visit_body ( body) ;
72
+ } ;
73
+ checker. visit_body ( body) ;
74
+ checker. check_cleanup_control_flow ( ) ;
72
75
}
73
76
}
74
77
@@ -134,6 +137,55 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
134
137
}
135
138
}
136
139
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
+
137
189
/// Check if src can be assigned into dest.
138
190
/// This is not precise, it will accept some incorrect assignments.
139
191
fn mir_assign_valid_types ( & self , src : Ty < ' tcx > , dest : Ty < ' tcx > ) -> bool {
0 commit comments