11use std:: fmt:: Write ;
2+ use std:: hash:: { Hash , Hasher } ;
23use std:: mem;
34
45use rustc:: hir:: def_id:: DefId ;
@@ -9,6 +10,7 @@ use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf,
910use rustc:: ty:: subst:: { Subst , Substs } ;
1011use rustc:: ty:: { self , Ty , TyCtxt , TypeAndMut } ;
1112use rustc:: ty:: query:: TyCtxtAt ;
13+ use rustc_data_structures:: fx:: { FxHashSet , FxHasher } ;
1214use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
1315use rustc:: mir:: interpret:: {
1416 FrameInfo , GlobalId , Value , Scalar ,
@@ -41,13 +43,17 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
4143 /// The maximum number of stack frames allowed
4244 pub ( crate ) stack_limit : usize ,
4345
44- /// The maximum number of terminators that may be evaluated.
45- /// This prevents infinite loops and huge computations from freezing up const eval.
46- /// Remove once halting problem is solved.
47- pub ( crate ) terminators_remaining : usize ,
46+ /// When this value is negative, it indicates the number of interpreter
47+ /// steps *until* the loop detector is enabled. When it is positive, it is
48+ /// the number of steps after the detector has been enabled modulo the loop
49+ /// detector period.
50+ pub ( crate ) steps_since_detector_enabled : isize ,
51+
52+ pub ( crate ) loop_detector : InfiniteLoopDetector < ' a , ' mir , ' tcx , M > ,
4853}
4954
5055/// A stack frame.
56+ #[ derive( Clone ) ]
5157pub struct Frame < ' mir , ' tcx : ' mir > {
5258 ////////////////////////////////////////////////////////////////////////////////
5359 // Function and callsite information
@@ -89,6 +95,121 @@ pub struct Frame<'mir, 'tcx: 'mir> {
8995 pub stmt : usize ,
9096}
9197
98+ impl < ' mir , ' tcx : ' mir > Eq for Frame < ' mir , ' tcx > { }
99+
100+ impl < ' mir , ' tcx : ' mir > PartialEq for Frame < ' mir , ' tcx > {
101+ fn eq ( & self , other : & Self ) -> bool {
102+ let Frame {
103+ mir : _,
104+ instance,
105+ span : _,
106+ return_to_block,
107+ return_place,
108+ locals,
109+ block,
110+ stmt,
111+ } = self ;
112+
113+ // Some of these are constant during evaluation, but are included
114+ // anyways for correctness.
115+ * instance == other. instance
116+ && * return_to_block == other. return_to_block
117+ && * return_place == other. return_place
118+ && * locals == other. locals
119+ && * block == other. block
120+ && * stmt == other. stmt
121+ }
122+ }
123+
124+ impl < ' mir , ' tcx : ' mir > Hash for Frame < ' mir , ' tcx > {
125+ fn hash < H : Hasher > ( & self , state : & mut H ) {
126+ let Frame {
127+ mir : _,
128+ instance,
129+ span : _,
130+ return_to_block,
131+ return_place,
132+ locals,
133+ block,
134+ stmt,
135+ } = self ;
136+
137+ instance. hash ( state) ;
138+ return_to_block. hash ( state) ;
139+ return_place. hash ( state) ;
140+ locals. hash ( state) ;
141+ block. hash ( state) ;
142+ stmt. hash ( state) ;
143+ }
144+ }
145+
146+ /// The virtual machine state during const-evaluation at a given point in time.
147+ type EvalSnapshot < ' a , ' mir , ' tcx , M >
148+ = ( M , Vec < Frame < ' mir , ' tcx > > , Memory < ' a , ' mir , ' tcx , M > ) ;
149+
150+ pub ( crate ) struct InfiniteLoopDetector < ' a , ' mir , ' tcx : ' a + ' mir , M : Machine < ' mir , ' tcx > > {
151+ /// The set of all `EvalSnapshot` *hashes* observed by this detector.
152+ ///
153+ /// When a collision occurs in this table, we store the full snapshot in
154+ /// `snapshots`.
155+ hashes : FxHashSet < u64 > ,
156+
157+ /// The set of all `EvalSnapshot`s observed by this detector.
158+ ///
159+ /// An `EvalSnapshot` will only be fully cloned once it has caused a
160+ /// collision in `hashes`. As a result, the detector must observe at least
161+ /// *two* full cycles of an infinite loop before it triggers.
162+ snapshots : FxHashSet < EvalSnapshot < ' a , ' mir , ' tcx , M > > ,
163+ }
164+
165+ impl < ' a , ' mir , ' tcx , M > Default for InfiniteLoopDetector < ' a , ' mir , ' tcx , M >
166+ where M : Machine < ' mir , ' tcx > ,
167+ ' tcx : ' a + ' mir ,
168+ {
169+ fn default ( ) -> Self {
170+ InfiniteLoopDetector {
171+ hashes : FxHashSet :: default ( ) ,
172+ snapshots : FxHashSet :: default ( ) ,
173+ }
174+ }
175+ }
176+
177+ impl < ' a , ' mir , ' tcx , M > InfiniteLoopDetector < ' a , ' mir , ' tcx , M >
178+ where M : Machine < ' mir , ' tcx > ,
179+ ' tcx : ' a + ' mir ,
180+ {
181+ /// Returns `true` if the loop detector has not yet observed a snapshot.
182+ pub fn is_empty ( & self ) -> bool {
183+ self . hashes . is_empty ( )
184+ }
185+
186+ pub fn observe_and_analyze (
187+ & mut self ,
188+ machine : & M ,
189+ stack : & Vec < Frame < ' mir , ' tcx > > ,
190+ memory : & Memory < ' a , ' mir , ' tcx , M > ,
191+ ) -> EvalResult < ' tcx , ( ) > {
192+ let snapshot = ( machine, stack, memory) ;
193+
194+ let mut fx = FxHasher :: default ( ) ;
195+ snapshot. hash ( & mut fx) ;
196+ let hash = fx. finish ( ) ;
197+
198+ if self . hashes . insert ( hash) {
199+ // No collision
200+ return Ok ( ( ) )
201+ }
202+
203+ if self . snapshots . insert ( ( machine. clone ( ) , stack. clone ( ) , memory. clone ( ) ) ) {
204+ // Spurious collision or first cycle
205+ return Ok ( ( ) )
206+ }
207+
208+ // Second cycle
209+ Err ( EvalErrorKind :: InfiniteLoop . into ( ) )
210+ }
211+ }
212+
92213#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
93214pub enum StackPopCleanup {
94215 /// The stackframe existed to compute the initial value of a static/constant, make sure it
@@ -173,7 +294,7 @@ impl<'c, 'b, 'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf
173294 }
174295}
175296
176- const MAX_TERMINATORS : usize = 1_000_000 ;
297+ const STEPS_UNTIL_DETECTOR_ENABLED : isize = 1_000_000 ;
177298
178299impl < ' a , ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > EvalContext < ' a , ' mir , ' tcx , M > {
179300 pub fn new (
@@ -189,16 +310,17 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
189310 memory : Memory :: new ( tcx, memory_data) ,
190311 stack : Vec :: new ( ) ,
191312 stack_limit : tcx. sess . const_eval_stack_frame_limit ,
192- terminators_remaining : MAX_TERMINATORS ,
313+ loop_detector : Default :: default ( ) ,
314+ steps_since_detector_enabled : -STEPS_UNTIL_DETECTOR_ENABLED ,
193315 }
194316 }
195317
196318 pub ( crate ) fn with_fresh_body < F : FnOnce ( & mut Self ) -> R , R > ( & mut self , f : F ) -> R {
197319 let stack = mem:: replace ( & mut self . stack , Vec :: new ( ) ) ;
198- let terminators_remaining = mem:: replace ( & mut self . terminators_remaining , MAX_TERMINATORS ) ;
320+ let steps = mem:: replace ( & mut self . steps_since_detector_enabled , - STEPS_UNTIL_DETECTOR_ENABLED ) ;
199321 let r = f ( self ) ;
200322 self . stack = stack;
201- self . terminators_remaining = terminators_remaining ;
323+ self . steps_since_detector_enabled = steps ;
202324 r
203325 }
204326
@@ -538,8 +660,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
538660 }
539661
540662 Aggregate ( ref kind, ref operands) => {
541- self . inc_step_counter_and_check_limit ( operands. len ( ) ) ;
542-
543663 let ( dest, active_field_index) = match * * kind {
544664 mir:: AggregateKind :: Adt ( adt_def, variant_index, _, active_field_index) => {
545665 self . write_discriminant_value ( dest_ty, dest, variant_index) ?;
0 commit comments