@@ -22,7 +22,7 @@ use rustc::ty::subst::Subst;
22
22
use rustc_data_structures:: indexed_vec:: IndexVec ;
23
23
24
24
use syntax:: ast:: Mutability ;
25
- use syntax:: source_map:: Span ;
25
+ use syntax:: source_map:: { Span , DUMMY_SP } ;
26
26
27
27
use rustc:: mir:: interpret:: {
28
28
EvalResult , EvalError , EvalErrorKind , GlobalId ,
@@ -31,17 +31,25 @@ use rustc::mir::interpret::{
31
31
use interpret:: { self ,
32
32
Place , PlaceTy , MemPlace , OpTy , Operand , Value ,
33
33
EvalContext , StackPopCleanup , MemoryKind ,
34
+ snapshot,
34
35
} ;
35
36
37
+ /// Number of steps until the detector even starts doing anything.
38
+ /// Also, a warning is shown to the user when this number is reached.
39
+ const STEPS_UNTIL_DETECTOR_ENABLED : isize = 1_000_000 ;
40
+ /// The number of steps between loop detector snapshots.
41
+ /// Should be a power of two for performance reasons.
42
+ const DETECTOR_SNAPSHOT_PERIOD : isize = 256 ;
43
+
36
44
pub fn mk_borrowck_eval_cx < ' a , ' mir , ' tcx > (
37
45
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
38
46
instance : Instance < ' tcx > ,
39
47
mir : & ' mir mir:: Mir < ' tcx > ,
40
48
span : Span ,
41
- ) -> EvalResult < ' tcx , EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > > {
49
+ ) -> EvalResult < ' tcx , CompileTimeEvalContext < ' a , ' mir , ' tcx > > {
42
50
debug ! ( "mk_borrowck_eval_cx: {:?}" , instance) ;
43
51
let param_env = tcx. param_env ( instance. def_id ( ) ) ;
44
- let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator , ( ) ) ;
52
+ let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeInterpreter :: new ( ) , ( ) ) ;
45
53
// insert a stack frame so any queries have the correct substs
46
54
ecx. stack . push ( interpret:: Frame {
47
55
block : mir:: START_BLOCK ,
@@ -60,10 +68,10 @@ pub fn mk_eval_cx<'a, 'tcx>(
60
68
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
61
69
instance : Instance < ' tcx > ,
62
70
param_env : ty:: ParamEnv < ' tcx > ,
63
- ) -> EvalResult < ' tcx , EvalContext < ' a , ' tcx , ' tcx , CompileTimeEvaluator > > {
71
+ ) -> EvalResult < ' tcx , CompileTimeEvalContext < ' a , ' tcx , ' tcx > > {
64
72
debug ! ( "mk_eval_cx: {:?}, {:?}" , instance, param_env) ;
65
73
let span = tcx. def_span ( instance. def_id ( ) ) ;
66
- let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator , ( ) ) ;
74
+ let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeInterpreter :: new ( ) , ( ) ) ;
67
75
let mir = ecx. load_mir ( instance. def ) ?;
68
76
// insert a stack frame so any queries have the correct substs
69
77
ecx. push_stack_frame (
@@ -76,19 +84,18 @@ pub fn mk_eval_cx<'a, 'tcx>(
76
84
Ok ( ecx)
77
85
}
78
86
79
- pub fn eval_promoted < ' a , ' mir , ' tcx > (
80
- ecx : & mut EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > ,
87
+ pub ( crate ) fn eval_promoted < ' a , ' mir , ' tcx > (
88
+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
81
89
cid : GlobalId < ' tcx > ,
82
90
mir : & ' mir mir:: Mir < ' tcx > ,
83
91
param_env : ty:: ParamEnv < ' tcx > ,
84
92
) -> EvalResult < ' tcx , OpTy < ' tcx > > {
85
- ecx. with_fresh_body ( |ecx| {
86
- eval_body_using_ecx ( ecx, cid, Some ( mir) , param_env)
87
- } )
93
+ let mut ecx = mk_borrowck_eval_cx ( tcx, cid. instance , mir, DUMMY_SP ) . unwrap ( ) ;
94
+ eval_body_using_ecx ( & mut ecx, cid, Some ( mir) , param_env)
88
95
}
89
96
90
97
pub fn op_to_const < ' tcx > (
91
- ecx : & EvalContext < ' _ , ' _ , ' tcx , CompileTimeEvaluator > ,
98
+ ecx : & CompileTimeEvalContext < ' _ , ' _ , ' tcx > ,
92
99
op : OpTy < ' tcx > ,
93
100
normalize : bool ,
94
101
) -> EvalResult < ' tcx , & ' tcx ty:: Const < ' tcx > > {
@@ -128,19 +135,19 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
128
135
cid : GlobalId < ' tcx > ,
129
136
mir : Option < & ' mir mir:: Mir < ' tcx > > ,
130
137
param_env : ty:: ParamEnv < ' tcx > ,
131
- ) -> ( EvalResult < ' tcx , OpTy < ' tcx > > , EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > ) {
138
+ ) -> ( EvalResult < ' tcx , OpTy < ' tcx > > , CompileTimeEvalContext < ' a , ' mir , ' tcx > ) {
132
139
// we start out with the best span we have
133
140
// and try improving it down the road when more information is available
134
141
let span = tcx. def_span ( cid. instance . def_id ( ) ) ;
135
142
let span = mir. map ( |mir| mir. span ) . unwrap_or ( span) ;
136
- let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator , ( ) ) ;
143
+ let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeInterpreter :: new ( ) , ( ) ) ;
137
144
let r = eval_body_using_ecx ( & mut ecx, cid, mir, param_env) ;
138
145
( r, ecx)
139
146
}
140
147
141
148
// Returns a pointer to where the result lives
142
- fn eval_body_using_ecx < ' a , ' mir , ' tcx > (
143
- ecx : & mut EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > ,
149
+ fn eval_body_using_ecx < ' mir , ' tcx > (
150
+ ecx : & mut CompileTimeEvalContext < ' _ , ' mir , ' tcx > ,
144
151
cid : GlobalId < ' tcx > ,
145
152
mir : Option < & ' mir mir:: Mir < ' tcx > > ,
146
153
param_env : ty:: ParamEnv < ' tcx > ,
@@ -187,17 +194,12 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
187
194
Ok ( ret. into ( ) )
188
195
}
189
196
190
- #[ derive( Debug , Clone , Eq , PartialEq , Hash ) ]
191
- pub struct CompileTimeEvaluator ;
192
-
193
197
impl < ' tcx > Into < EvalError < ' tcx > > for ConstEvalError {
194
198
fn into ( self ) -> EvalError < ' tcx > {
195
199
EvalErrorKind :: MachineError ( self . to_string ( ) ) . into ( )
196
200
}
197
201
}
198
202
199
- impl_stable_hash_for ! ( struct CompileTimeEvaluator { } ) ;
200
-
201
203
#[ derive( Clone , Debug ) ]
202
204
enum ConstEvalError {
203
205
NeedsRfc ( String ) ,
@@ -234,14 +236,39 @@ impl Error for ConstEvalError {
234
236
}
235
237
}
236
238
237
- impl < ' mir , ' tcx > interpret:: Machine < ' mir , ' tcx > for CompileTimeEvaluator {
239
+ // Extra machine state for CTFE, and the Machine instance
240
+ pub struct CompileTimeInterpreter < ' a , ' mir , ' tcx : ' a +' mir > {
241
+ /// When this value is negative, it indicates the number of interpreter
242
+ /// steps *until* the loop detector is enabled. When it is positive, it is
243
+ /// the number of steps after the detector has been enabled modulo the loop
244
+ /// detector period.
245
+ pub ( super ) steps_since_detector_enabled : isize ,
246
+
247
+ /// Extra state to detect loops.
248
+ pub ( super ) loop_detector : snapshot:: InfiniteLoopDetector < ' a , ' mir , ' tcx > ,
249
+ }
250
+
251
+ impl < ' a , ' mir , ' tcx > CompileTimeInterpreter < ' a , ' mir , ' tcx > {
252
+ fn new ( ) -> Self {
253
+ CompileTimeInterpreter {
254
+ loop_detector : Default :: default ( ) ,
255
+ steps_since_detector_enabled : -STEPS_UNTIL_DETECTOR_ENABLED ,
256
+ }
257
+ }
258
+ }
259
+
260
+ type CompileTimeEvalContext < ' a , ' mir , ' tcx > =
261
+ EvalContext < ' a , ' mir , ' tcx , CompileTimeInterpreter < ' a , ' mir , ' tcx > > ;
262
+
263
+ impl < ' a , ' mir , ' tcx > interpret:: Machine < ' a , ' mir , ' tcx >
264
+ for CompileTimeInterpreter < ' a , ' mir , ' tcx >
265
+ {
238
266
type MemoryData = ( ) ;
239
267
type MemoryKinds = !;
240
268
241
269
const MUT_STATIC_KIND : Option < !> = None ; // no mutating of statics allowed
242
- const DETECT_LOOPS : bool = true ;
243
270
244
- fn find_fn < ' a > (
271
+ fn find_fn (
245
272
ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
246
273
instance : ty:: Instance < ' tcx > ,
247
274
args : & [ OpTy < ' tcx > ] ,
@@ -275,7 +302,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
275
302
} ) )
276
303
}
277
304
278
- fn call_intrinsic < ' a > (
305
+ fn call_intrinsic (
279
306
ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
280
307
instance : ty:: Instance < ' tcx > ,
281
308
args : & [ OpTy < ' tcx > ] ,
@@ -291,7 +318,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
291
318
)
292
319
}
293
320
294
- fn ptr_op < ' a > (
321
+ fn ptr_op (
295
322
_ecx : & EvalContext < ' a , ' mir , ' tcx , Self > ,
296
323
_bin_op : mir:: BinOp ,
297
324
_left : Scalar ,
@@ -304,21 +331,45 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
304
331
)
305
332
}
306
333
307
- fn find_foreign_static < ' a > (
334
+ fn find_foreign_static (
308
335
_tcx : TyCtxtAt < ' a , ' tcx , ' tcx > ,
309
336
_def_id : DefId ,
310
337
) -> EvalResult < ' tcx , & ' tcx Allocation > {
311
338
err ! ( ReadForeignStatic )
312
339
}
313
340
314
- fn box_alloc < ' a > (
341
+ fn box_alloc (
315
342
_ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
316
343
_dest : PlaceTy < ' tcx > ,
317
344
) -> EvalResult < ' tcx > {
318
345
Err (
319
346
ConstEvalError :: NeedsRfc ( "heap allocations via `box` keyword" . to_string ( ) ) . into ( ) ,
320
347
)
321
348
}
349
+
350
+ fn before_terminator ( ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ) -> EvalResult < ' tcx > {
351
+ {
352
+ let steps = & mut ecx. machine . steps_since_detector_enabled ;
353
+
354
+ * steps += 1 ;
355
+ if * steps < 0 {
356
+ return Ok ( ( ) ) ;
357
+ }
358
+
359
+ * steps %= DETECTOR_SNAPSHOT_PERIOD ;
360
+ if * steps != 0 {
361
+ return Ok ( ( ) ) ;
362
+ }
363
+ }
364
+
365
+ let span = ecx. frame ( ) . span ;
366
+ ecx. machine . loop_detector . observe_and_analyze (
367
+ & ecx. tcx ,
368
+ span,
369
+ & ecx. memory ,
370
+ & ecx. stack [ ..] ,
371
+ )
372
+ }
322
373
}
323
374
324
375
/// Project to a field of a (variant of a) const
0 commit comments