1
+ use std:: ops:: ControlFlow ;
2
+
1
3
use super :: region_constraints:: RegionSnapshot ;
2
4
use super :: InferCtxt ;
3
5
use rustc_data_structures:: undo_log:: UndoLogs ;
4
- use rustc_middle:: ty;
6
+ use rustc_middle:: ty:: { TypeFoldable , TypeSuperVisitable } ;
7
+ use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeVisitor } ;
5
8
6
9
mod fudge;
7
10
pub ( crate ) mod undo_log;
@@ -57,27 +60,42 @@ impl<'tcx> InferCtxt<'tcx> {
57
60
58
61
/// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`.
59
62
#[ instrument( skip( self , f) , level = "debug" ) ]
60
- pub fn commit_if_ok < T , E , F > ( & self , f : F ) -> Result < T , E >
63
+ pub fn commit_if_ok < T , E , F , CX > ( & self , f : F ) -> Result < T , E >
61
64
where
62
65
F : FnOnce ( & CombinedSnapshot < ' tcx > ) -> Result < T , E > ,
66
+ E : NoSnapshotLeaks < ' tcx , CX >
63
67
{
68
+ let no_leaks_data = E :: mk_data ( self ) ;
64
69
let snapshot = self . start_snapshot ( ) ;
65
70
let r = f ( & snapshot) ;
66
71
debug ! ( "commit_if_ok() -- r.is_ok() = {}" , r. is_ok( ) ) ;
67
72
match r {
68
- Ok ( _ ) => {
73
+ Ok ( value ) => {
69
74
self . commit_from ( snapshot) ;
75
+ Ok ( value)
70
76
}
71
- Err ( _ ) => {
77
+ Err ( err ) => {
72
78
self . rollback_to ( snapshot) ;
79
+ Err ( E :: avoid_leaks ( err, no_leaks_data) )
73
80
}
74
81
}
75
- r
76
82
}
77
83
78
84
/// Execute `f` then unroll any bindings it creates.
79
85
#[ instrument( skip( self , f) , level = "debug" ) ]
80
- pub fn probe < R , F > ( & self , f : F ) -> R
86
+ pub fn probe < R , F , CX > ( & self , f : F ) -> R
87
+ where
88
+ F : FnOnce ( & CombinedSnapshot < ' tcx > ) -> R ,
89
+ R : NoSnapshotLeaks < ' tcx , CX > ,
90
+ {
91
+ let no_leaks_data = R :: mk_data ( self ) ;
92
+ let snapshot = self . start_snapshot ( ) ;
93
+ let r = f ( & snapshot) ;
94
+ self . rollback_to ( snapshot) ;
95
+ R :: avoid_leaks ( r, no_leaks_data)
96
+ }
97
+
98
+ pub fn probe_unchecked < R , F > ( & self , f : F ) -> R
81
99
where
82
100
F : FnOnce ( & CombinedSnapshot < ' tcx > ) -> R ,
83
101
{
@@ -99,4 +117,128 @@ impl<'tcx> InferCtxt<'tcx> {
99
117
pub fn opaque_types_added_in_snapshot ( & self , snapshot : & CombinedSnapshot < ' tcx > ) -> bool {
100
118
self . inner . borrow ( ) . undo_log . opaque_types_in_snapshot ( & snapshot. undo_snapshot )
101
119
}
120
+
121
+ fn variable_lengths ( & self ) -> VariableLengths {
122
+ let mut inner = self . inner . borrow_mut ( ) ;
123
+ VariableLengths {
124
+ type_vars : inner. type_variables ( ) . num_vars ( ) ,
125
+ const_vars : inner. const_unification_table ( ) . len ( ) ,
126
+ int_vars : inner. int_unification_table ( ) . len ( ) ,
127
+ float_vars : inner. float_unification_table ( ) . len ( ) ,
128
+ region_vars : inner. unwrap_region_constraints ( ) . num_region_vars ( ) ,
129
+ }
130
+ }
131
+ }
132
+
133
+ trait NoSnapshotLeaks < ' tcx , CX > {
134
+ fn mk_data ( infcx : & InferCtxt < ' tcx > ) -> CX ;
135
+ fn avoid_leaks ( self , data : CX ) -> Self ;
136
+ }
137
+
138
+ pub struct CheckLeaks ( HasSnapshotLeaksVisitor ) ;
139
+ impl < ' tcx , T : TypeFoldable < TyCtxt < ' tcx > > > NoSnapshotLeaks < ' tcx , CheckLeaks > for T {
140
+ fn mk_data ( infcx : & InferCtxt < ' tcx > ) -> CheckLeaks {
141
+ CheckLeaks ( HasSnapshotLeaksVisitor {
142
+ universe : infcx. universe ( ) ,
143
+ variable_lengths : infcx. variable_lengths ( ) ,
144
+ } )
145
+ }
146
+ fn avoid_leaks ( self , CheckLeaks ( mut visitor) : CheckLeaks ) -> Self {
147
+ if cfg ! ( debug_assertions) && self . visit_with ( & mut visitor) . is_break ( ) {
148
+ bug ! ( "leaking vars from snapshot: {:?}" , self )
149
+ } else {
150
+ self
151
+ }
152
+ }
153
+ }
154
+
155
+ struct VariableLengths {
156
+ region_vars : usize ,
157
+ type_vars : usize ,
158
+ int_vars : usize ,
159
+ float_vars : usize ,
160
+ const_vars : usize ,
161
+ }
162
+
163
+ struct HasSnapshotLeaksVisitor {
164
+ universe : ty:: UniverseIndex ,
165
+ variable_lengths : VariableLengths ,
166
+ }
167
+
168
+ fn continue_if ( b : bool ) -> ControlFlow < ( ) > {
169
+ if b { ControlFlow :: Continue ( ( ) ) } else { ControlFlow :: Continue ( ( ) ) }
170
+ }
171
+
172
+ impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for HasSnapshotLeaksVisitor {
173
+ type Result = ControlFlow < ( ) > ;
174
+
175
+ fn visit_region ( & mut self , r : ty:: Region < ' tcx > ) -> Self :: Result {
176
+ match r. kind ( ) {
177
+ ty:: ReVar ( var) => continue_if ( var. as_usize ( ) < self . variable_lengths . region_vars ) ,
178
+ ty:: RePlaceholder ( p) => continue_if ( self . universe . can_name ( p. universe ) ) ,
179
+ ty:: ReEarlyParam ( _)
180
+ | ty:: ReBound ( _, _)
181
+ | ty:: ReLateParam ( _)
182
+ | ty:: ReStatic
183
+ | ty:: ReErased
184
+ | ty:: ReError ( _) => ControlFlow :: Continue ( ( ) ) ,
185
+ }
186
+ }
187
+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> Self :: Result {
188
+ match t. kind ( ) {
189
+ ty:: Infer ( ty:: TyVar ( var) ) => {
190
+ continue_if ( var. as_usize ( ) < self . variable_lengths . type_vars )
191
+ }
192
+ ty:: Infer ( ty:: IntVar ( var) ) => {
193
+ continue_if ( var. as_usize ( ) < self . variable_lengths . int_vars )
194
+ }
195
+ ty:: Infer ( ty:: FloatVar ( var) ) => {
196
+ continue_if ( var. as_usize ( ) < self . variable_lengths . float_vars )
197
+ }
198
+ ty:: Placeholder ( p) => continue_if ( self . universe . can_name ( p. universe ) ) ,
199
+ ty:: Infer ( ty:: FreshTy ( ..) | ty:: FreshIntTy ( ..) | ty:: FreshFloatTy ( ..) )
200
+ | ty:: Bool
201
+ | ty:: Char
202
+ | ty:: Int ( _)
203
+ | ty:: Uint ( _)
204
+ | ty:: Float ( _)
205
+ | ty:: Adt ( _, _)
206
+ | ty:: Foreign ( _)
207
+ | ty:: Str
208
+ | ty:: Array ( _, _)
209
+ | ty:: Slice ( _)
210
+ | ty:: RawPtr ( _)
211
+ | ty:: Ref ( _, _, _)
212
+ | ty:: FnDef ( _, _)
213
+ | ty:: FnPtr ( _)
214
+ | ty:: Dynamic ( _, _, _)
215
+ | ty:: Closure ( _, _)
216
+ | ty:: CoroutineClosure ( _, _)
217
+ | ty:: Coroutine ( _, _)
218
+ | ty:: CoroutineWitness ( _, _)
219
+ | ty:: Never
220
+ | ty:: Tuple ( _)
221
+ | ty:: Alias ( _, _)
222
+ | ty:: Param ( _)
223
+ | ty:: Bound ( _, _)
224
+ | ty:: Error ( _) => t. super_visit_with ( self ) ,
225
+ }
226
+ }
227
+ fn visit_const ( & mut self , c : ty:: Const < ' tcx > ) -> Self :: Result {
228
+ match c. kind ( ) {
229
+ ty:: ConstKind :: Infer ( ty:: InferConst :: Var ( var) ) => {
230
+ continue_if ( var. as_usize ( ) < self . variable_lengths . const_vars )
231
+ }
232
+ // FIXME(const_trait_impl): need to handle effect vars here and in `fudge_inference_if_ok`.
233
+ ty:: ConstKind :: Infer ( ty:: InferConst :: EffectVar ( _) ) => ControlFlow :: Continue ( ( ) ) ,
234
+ ty:: ConstKind :: Placeholder ( p) => continue_if ( self . universe . can_name ( p. universe ) ) ,
235
+ ty:: ConstKind :: Infer ( ty:: InferConst :: Fresh ( _) )
236
+ | ty:: ConstKind :: Param ( _)
237
+ | ty:: ConstKind :: Bound ( _, _)
238
+ | ty:: ConstKind :: Unevaluated ( _)
239
+ | ty:: ConstKind :: Value ( _)
240
+ | ty:: ConstKind :: Expr ( _)
241
+ | ty:: ConstKind :: Error ( _) => c. super_visit_with ( self ) ,
242
+ }
243
+ }
102
244
}
0 commit comments