@@ -22,7 +22,7 @@ use rustc::ty::subst::Subst;
2222use rustc_data_structures:: indexed_vec:: IndexVec ;
2323
2424use syntax:: ast:: Mutability ;
25- use syntax:: source_map:: Span ;
25+ use syntax:: source_map:: { Span , DUMMY_SP } ;
2626
2727use rustc:: mir:: interpret:: {
2828 EvalResult , EvalError , EvalErrorKind , GlobalId ,
@@ -31,17 +31,25 @@ use rustc::mir::interpret::{
3131use interpret:: { self ,
3232 Place , PlaceTy , MemPlace , OpTy , Operand , Value ,
3333 EvalContext , StackPopCleanup , MemoryKind ,
34+ snapshot,
3435} ;
3536
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+
3644pub fn mk_borrowck_eval_cx < ' a , ' mir , ' tcx > (
3745 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
3846 instance : Instance < ' tcx > ,
3947 mir : & ' mir mir:: Mir < ' tcx > ,
4048 span : Span ,
41- ) -> EvalResult < ' tcx , EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > > {
49+ ) -> EvalResult < ' tcx , CompileTimeEvalContext < ' a , ' mir , ' tcx > > {
4250 debug ! ( "mk_borrowck_eval_cx: {:?}" , instance) ;
4351 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 ( ) , ( ) ) ;
4553 // insert a stack frame so any queries have the correct substs
4654 ecx. stack . push ( interpret:: Frame {
4755 block : mir:: START_BLOCK ,
@@ -60,10 +68,10 @@ pub fn mk_eval_cx<'a, 'tcx>(
6068 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
6169 instance : Instance < ' tcx > ,
6270 param_env : ty:: ParamEnv < ' tcx > ,
63- ) -> EvalResult < ' tcx , EvalContext < ' a , ' tcx , ' tcx , CompileTimeEvaluator > > {
71+ ) -> EvalResult < ' tcx , CompileTimeEvalContext < ' a , ' tcx , ' tcx > > {
6472 debug ! ( "mk_eval_cx: {:?}, {:?}" , instance, param_env) ;
6573 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 ( ) , ( ) ) ;
6775 let mir = ecx. load_mir ( instance. def ) ?;
6876 // insert a stack frame so any queries have the correct substs
6977 ecx. push_stack_frame (
@@ -76,19 +84,18 @@ pub fn mk_eval_cx<'a, 'tcx>(
7684 Ok ( ecx)
7785}
7886
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 > ,
8189 cid : GlobalId < ' tcx > ,
8290 mir : & ' mir mir:: Mir < ' tcx > ,
8391 param_env : ty:: ParamEnv < ' tcx > ,
8492) -> 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)
8895}
8996
9097pub fn op_to_const < ' tcx > (
91- ecx : & EvalContext < ' _ , ' _ , ' tcx , CompileTimeEvaluator > ,
98+ ecx : & CompileTimeEvalContext < ' _ , ' _ , ' tcx > ,
9299 op : OpTy < ' tcx > ,
93100 normalize : bool ,
94101) -> EvalResult < ' tcx , & ' tcx ty:: Const < ' tcx > > {
@@ -128,19 +135,19 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
128135 cid : GlobalId < ' tcx > ,
129136 mir : Option < & ' mir mir:: Mir < ' tcx > > ,
130137 param_env : ty:: ParamEnv < ' tcx > ,
131- ) -> ( EvalResult < ' tcx , OpTy < ' tcx > > , EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > ) {
138+ ) -> ( EvalResult < ' tcx , OpTy < ' tcx > > , CompileTimeEvalContext < ' a , ' mir , ' tcx > ) {
132139 // we start out with the best span we have
133140 // and try improving it down the road when more information is available
134141 let span = tcx. def_span ( cid. instance . def_id ( ) ) ;
135142 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 ( ) , ( ) ) ;
137144 let r = eval_body_using_ecx ( & mut ecx, cid, mir, param_env) ;
138145 ( r, ecx)
139146}
140147
141148// 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 > ,
144151 cid : GlobalId < ' tcx > ,
145152 mir : Option < & ' mir mir:: Mir < ' tcx > > ,
146153 param_env : ty:: ParamEnv < ' tcx > ,
@@ -187,17 +194,12 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
187194 Ok ( ret. into ( ) )
188195}
189196
190- #[ derive( Debug , Clone , Eq , PartialEq , Hash ) ]
191- pub struct CompileTimeEvaluator ;
192-
193197impl < ' tcx > Into < EvalError < ' tcx > > for ConstEvalError {
194198 fn into ( self ) -> EvalError < ' tcx > {
195199 EvalErrorKind :: MachineError ( self . to_string ( ) ) . into ( )
196200 }
197201}
198202
199- impl_stable_hash_for ! ( struct CompileTimeEvaluator { } ) ;
200-
201203#[ derive( Clone , Debug ) ]
202204enum ConstEvalError {
203205 NeedsRfc ( String ) ,
@@ -234,14 +236,39 @@ impl Error for ConstEvalError {
234236 }
235237}
236238
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+ {
238266 type MemoryData = ( ) ;
239267 type MemoryKinds = !;
240268
241269 const MUT_STATIC_KIND : Option < !> = None ; // no mutating of statics allowed
242- const DETECT_LOOPS : bool = true ;
243270
244- fn find_fn < ' a > (
271+ fn find_fn (
245272 ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
246273 instance : ty:: Instance < ' tcx > ,
247274 args : & [ OpTy < ' tcx > ] ,
@@ -275,7 +302,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
275302 } ) )
276303 }
277304
278- fn call_intrinsic < ' a > (
305+ fn call_intrinsic (
279306 ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
280307 instance : ty:: Instance < ' tcx > ,
281308 args : & [ OpTy < ' tcx > ] ,
@@ -291,7 +318,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
291318 )
292319 }
293320
294- fn ptr_op < ' a > (
321+ fn ptr_op (
295322 _ecx : & EvalContext < ' a , ' mir , ' tcx , Self > ,
296323 _bin_op : mir:: BinOp ,
297324 _left : Scalar ,
@@ -304,21 +331,45 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
304331 )
305332 }
306333
307- fn find_foreign_static < ' a > (
334+ fn find_foreign_static (
308335 _tcx : TyCtxtAt < ' a , ' tcx , ' tcx > ,
309336 _def_id : DefId ,
310337 ) -> EvalResult < ' tcx , & ' tcx Allocation > {
311338 err ! ( ReadForeignStatic )
312339 }
313340
314- fn box_alloc < ' a > (
341+ fn box_alloc (
315342 _ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
316343 _dest : PlaceTy < ' tcx > ,
317344 ) -> EvalResult < ' tcx > {
318345 Err (
319346 ConstEvalError :: NeedsRfc ( "heap allocations via `box` keyword" . to_string ( ) ) . into ( ) ,
320347 )
321348 }
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+ }
322373}
323374
324375/// Project to a field of a (variant of a) const
0 commit comments