1
1
use std:: fmt:: Write ;
2
+ use std:: hash:: { Hash , Hasher } ;
2
3
use std:: mem;
3
4
4
5
use rustc:: hir:: def_id:: DefId ;
@@ -9,6 +10,7 @@ use rustc::ty::layout::{self, Size, Align, HasDataLayout, IntegerExt, LayoutOf,
9
10
use rustc:: ty:: subst:: { Subst , Substs } ;
10
11
use rustc:: ty:: { self , Ty , TyCtxt , TypeAndMut } ;
11
12
use rustc:: ty:: query:: TyCtxtAt ;
13
+ use rustc_data_structures:: fx:: { FxHashSet , FxHasher } ;
12
14
use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
13
15
use rustc:: mir:: interpret:: {
14
16
FrameInfo , GlobalId , Value , Scalar ,
@@ -41,13 +43,17 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
41
43
/// The maximum number of stack frames allowed
42
44
pub ( crate ) stack_limit : usize ,
43
45
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 > ,
48
53
}
49
54
50
55
/// A stack frame.
56
+ #[ derive( Clone ) ]
51
57
pub struct Frame < ' mir , ' tcx : ' mir > {
52
58
////////////////////////////////////////////////////////////////////////////////
53
59
// Function and callsite information
@@ -89,6 +95,121 @@ pub struct Frame<'mir, 'tcx: 'mir> {
89
95
pub stmt : usize ,
90
96
}
91
97
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
+
92
213
#[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
93
214
pub enum StackPopCleanup {
94
215
/// 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
173
294
}
174
295
}
175
296
176
- const MAX_TERMINATORS : usize = 1_000_000 ;
297
+ const STEPS_UNTIL_DETECTOR_ENABLED : isize = 1_000_000 ;
177
298
178
299
impl < ' a , ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > EvalContext < ' a , ' mir , ' tcx , M > {
179
300
pub fn new (
@@ -189,16 +310,17 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
189
310
memory : Memory :: new ( tcx, memory_data) ,
190
311
stack : Vec :: new ( ) ,
191
312
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 ,
193
315
}
194
316
}
195
317
196
318
pub ( crate ) fn with_fresh_body < F : FnOnce ( & mut Self ) -> R , R > ( & mut self , f : F ) -> R {
197
319
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 ) ;
199
321
let r = f ( self ) ;
200
322
self . stack = stack;
201
- self . terminators_remaining = terminators_remaining ;
323
+ self . steps_since_detector_enabled = steps ;
202
324
r
203
325
}
204
326
@@ -538,8 +660,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
538
660
}
539
661
540
662
Aggregate ( ref kind, ref operands) => {
541
- self . inc_step_counter_and_check_limit ( operands. len ( ) ) ;
542
-
543
663
let ( dest, active_field_index) = match * * kind {
544
664
mir:: AggregateKind :: Adt ( adt_def, variant_index, _, active_field_index) => {
545
665
self . write_discriminant_value ( dest_ty, dest, variant_index) ?;
0 commit comments