@@ -10,6 +10,7 @@ use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf,
10
10
use rustc:: ty:: subst:: { Subst , Substs } ;
11
11
use rustc:: ty:: { self , Ty , TyCtxt , TypeAndMut } ;
12
12
use rustc:: ty:: query:: TyCtxtAt ;
13
+ use rustc_data_structures:: fx:: { FxHashSet , FxHasher } ;
13
14
use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
14
15
use rustc:: mir:: interpret:: {
15
16
FrameInfo , GlobalId , Value , Scalar ,
@@ -34,15 +35,16 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
34
35
pub param_env : ty:: ParamEnv < ' tcx > ,
35
36
36
37
/// Virtual memory and call stack.
37
- state : EvalState < ' a , ' mir , ' tcx , M > ,
38
+ pub ( crate ) state : EvalState < ' a , ' mir , ' tcx , M > ,
38
39
39
40
/// The maximum number of stack frames allowed
40
41
pub ( crate ) stack_limit : usize ,
41
42
42
- /// The maximum number of terminators that may be evaluated.
43
- /// This prevents infinite loops and huge computations from freezing up const eval.
44
- /// Remove once halting problem is solved.
45
- pub ( crate ) terminators_remaining : usize ,
43
+ /// The number of terminators to be evaluated before enabling the infinite
44
+ /// loop detector.
45
+ pub ( crate ) steps_until_detector_enabled : usize ,
46
+
47
+ pub ( crate ) loop_detector : InfiniteLoopDetector < ' a , ' mir , ' tcx , M > ,
46
48
}
47
49
48
50
pub ( crate ) struct EvalState < ' a , ' mir , ' tcx : ' a + ' mir , M : Machine < ' mir , ' tcx > > {
@@ -178,6 +180,56 @@ impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> {
178
180
}
179
181
}
180
182
183
+ pub ( crate ) struct InfiniteLoopDetector < ' a , ' mir , ' tcx : ' a + ' mir , M : Machine < ' mir , ' tcx > > {
184
+ /// The set of all `EvalState` *hashes* observed by this detector.
185
+ ///
186
+ /// Not a proper bloom filter.
187
+ bloom : FxHashSet < u64 > ,
188
+
189
+ /// The set of all `EvalState`s observed by this detector.
190
+ ///
191
+ /// An `EvalState` will only be fully cloned once it has caused a collision
192
+ /// in `bloom`. As a result, the detector must observe *two* full cycles of
193
+ /// an infinite loop before it triggers.
194
+ snapshots : FxHashSet < EvalState < ' a , ' mir , ' tcx , M > > ,
195
+ }
196
+
197
+ impl < ' a , ' mir , ' tcx , M > Default for InfiniteLoopDetector < ' a , ' mir , ' tcx , M >
198
+ where M : Machine < ' mir , ' tcx > ,
199
+ ' tcx : ' a + ' mir ,
200
+ {
201
+ fn default ( ) -> Self {
202
+ InfiniteLoopDetector {
203
+ bloom : FxHashSet :: default ( ) ,
204
+ snapshots : FxHashSet :: default ( ) ,
205
+ }
206
+ }
207
+ }
208
+
209
+ impl < ' a , ' mir , ' tcx , M > InfiniteLoopDetector < ' a , ' mir , ' tcx , M >
210
+ where M : Machine < ' mir , ' tcx > ,
211
+ ' tcx : ' a + ' mir ,
212
+ {
213
+ pub fn observe ( & mut self , snapshot : & EvalState < ' a , ' mir , ' tcx , M > ) -> Result < ( ) , ( /*TODO*/ ) > {
214
+ let mut fx = FxHasher :: default ( ) ;
215
+ snapshot. hash ( & mut fx) ;
216
+ let hash = fx. finish ( ) ;
217
+
218
+ if self . bloom . insert ( hash) {
219
+ // No collision
220
+ return Ok ( ( ) )
221
+ }
222
+
223
+ if self . snapshots . insert ( snapshot. clone ( ) ) {
224
+ // Spurious collision or first cycle
225
+ return Ok ( ( ) )
226
+ }
227
+
228
+ // Second cycle,
229
+ Err ( ( ) )
230
+ }
231
+ }
232
+
181
233
#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
182
234
pub enum StackPopCleanup {
183
235
/// The stackframe existed to compute the initial value of a static/constant, make sure it
@@ -280,16 +332,17 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
280
332
stack : Vec :: new ( ) ,
281
333
} ,
282
334
stack_limit : tcx. sess . const_eval_stack_frame_limit ,
283
- terminators_remaining : MAX_TERMINATORS ,
335
+ loop_detector : Default :: default ( ) ,
336
+ steps_until_detector_enabled : MAX_TERMINATORS ,
284
337
}
285
338
}
286
339
287
340
pub ( crate ) fn with_fresh_body < F : FnOnce ( & mut Self ) -> R , R > ( & mut self , f : F ) -> R {
288
341
let stack = mem:: replace ( self . stack_mut ( ) , Vec :: new ( ) ) ;
289
- let terminators_remaining = mem:: replace ( & mut self . terminators_remaining , MAX_TERMINATORS ) ;
342
+ let steps = mem:: replace ( & mut self . steps_until_detector_enabled , MAX_TERMINATORS ) ;
290
343
let r = f ( self ) ;
291
344
* self . stack_mut ( ) = stack;
292
- self . terminators_remaining = terminators_remaining ;
345
+ self . steps_until_detector_enabled = steps ;
293
346
r
294
347
}
295
348
@@ -634,7 +687,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
634
687
}
635
688
636
689
Aggregate ( ref kind, ref operands) => {
637
- self . inc_step_counter_and_check_limit ( operands. len ( ) ) ;
690
+ self . inc_step_counter_and_detect_loops ( operands. len ( ) ) ;
638
691
639
692
let ( dest, active_field_index) = match * * kind {
640
693
mir:: AggregateKind :: Adt ( adt_def, variant_index, _, active_field_index) => {
0 commit comments