@@ -10,6 +10,7 @@ use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf,
1010use rustc:: ty:: subst:: { Subst , Substs } ;
1111use rustc:: ty:: { self , Ty , TyCtxt , TypeAndMut } ;
1212use rustc:: ty:: query:: TyCtxtAt ;
13+ use rustc_data_structures:: fx:: { FxHashSet , FxHasher } ;
1314use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
1415use rustc:: mir:: interpret:: {
1516 FrameInfo , GlobalId , Value , Scalar ,
@@ -34,15 +35,16 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
3435 pub param_env : ty:: ParamEnv < ' tcx > ,
3536
3637 /// Virtual memory and call stack.
37- state : EvalState < ' a , ' mir , ' tcx , M > ,
38+ pub ( crate ) state : EvalState < ' a , ' mir , ' tcx , M > ,
3839
3940 /// The maximum number of stack frames allowed
4041 pub ( crate ) stack_limit : usize ,
4142
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 > ,
4648}
4749
4850pub ( 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> {
178180 }
179181}
180182
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+
181233#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
182234pub enum StackPopCleanup {
183235 /// 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
280332 stack : Vec :: new ( ) ,
281333 } ,
282334 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 ,
284337 }
285338 }
286339
287340 pub ( crate ) fn with_fresh_body < F : FnOnce ( & mut Self ) -> R , R > ( & mut self , f : F ) -> R {
288341 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 ) ;
290343 let r = f ( self ) ;
291344 * self . stack_mut ( ) = stack;
292- self . terminators_remaining = terminators_remaining ;
345+ self . steps_until_detector_enabled = steps ;
293346 r
294347 }
295348
@@ -634,7 +687,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
634687 }
635688
636689 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 ( ) ) ;
638691
639692 let ( dest, active_field_index) = match * * kind {
640693 mir:: AggregateKind :: Adt ( adt_def, variant_index, _, active_field_index) => {
0 commit comments