@@ -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,18 @@ use rustc::mir::interpret::{
3131use interpret:: { self ,
3232 Place , PlaceTy , MemPlace , OpTy , Operand , Value ,
3333 EvalContext , StackPopCleanup , MemoryKind ,
34+ snapshot,
3435} ;
3536
3637pub fn mk_borrowck_eval_cx < ' a , ' mir , ' tcx > (
3738 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
3839 instance : Instance < ' tcx > ,
3940 mir : & ' mir mir:: Mir < ' tcx > ,
4041 span : Span ,
41- ) -> EvalResult < ' tcx , EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > > {
42+ ) -> EvalResult < ' tcx , CompileTimeEvalContext < ' a , ' mir , ' tcx > > {
4243 debug ! ( "mk_borrowck_eval_cx: {:?}" , instance) ;
4344 let param_env = tcx. param_env ( instance. def_id ( ) ) ;
44- let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator , ( ) ) ;
45+ let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator :: new ( ) , ( ) ) ;
4546 // insert a stack frame so any queries have the correct substs
4647 ecx. stack . push ( interpret:: Frame {
4748 block : mir:: START_BLOCK ,
@@ -60,10 +61,10 @@ pub fn mk_eval_cx<'a, 'tcx>(
6061 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
6162 instance : Instance < ' tcx > ,
6263 param_env : ty:: ParamEnv < ' tcx > ,
63- ) -> EvalResult < ' tcx , EvalContext < ' a , ' tcx , ' tcx , CompileTimeEvaluator > > {
64+ ) -> EvalResult < ' tcx , CompileTimeEvalContext < ' a , ' tcx , ' tcx > > {
6465 debug ! ( "mk_eval_cx: {:?}, {:?}" , instance, param_env) ;
6566 let span = tcx. def_span ( instance. def_id ( ) ) ;
66- let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator , ( ) ) ;
67+ let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator :: new ( ) , ( ) ) ;
6768 let mir = ecx. load_mir ( instance. def ) ?;
6869 // insert a stack frame so any queries have the correct substs
6970 ecx. push_stack_frame (
@@ -77,18 +78,17 @@ pub fn mk_eval_cx<'a, 'tcx>(
7778}
7879
7980pub fn eval_promoted < ' a , ' mir , ' tcx > (
80- ecx : & mut EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > ,
81+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
8182 cid : GlobalId < ' tcx > ,
8283 mir : & ' mir mir:: Mir < ' tcx > ,
8384 param_env : ty:: ParamEnv < ' tcx > ,
8485) -> EvalResult < ' tcx , OpTy < ' tcx > > {
85- ecx. with_fresh_body ( |ecx| {
86- eval_body_using_ecx ( ecx, cid, Some ( mir) , param_env)
87- } )
86+ let mut ecx = mk_borrowck_eval_cx ( tcx, cid. instance , mir, DUMMY_SP ) . unwrap ( ) ;
87+ eval_body_using_ecx ( & mut ecx, cid, Some ( mir) , param_env)
8888}
8989
9090pub fn op_to_const < ' tcx > (
91- ecx : & EvalContext < ' _ , ' _ , ' tcx , CompileTimeEvaluator > ,
91+ ecx : & CompileTimeEvalContext < ' _ , ' _ , ' tcx > ,
9292 op : OpTy < ' tcx > ,
9393 normalize : bool ,
9494) -> EvalResult < ' tcx , & ' tcx ty:: Const < ' tcx > > {
@@ -128,19 +128,19 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
128128 cid : GlobalId < ' tcx > ,
129129 mir : Option < & ' mir mir:: Mir < ' tcx > > ,
130130 param_env : ty:: ParamEnv < ' tcx > ,
131- ) -> ( EvalResult < ' tcx , OpTy < ' tcx > > , EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator > ) {
131+ ) -> ( EvalResult < ' tcx , OpTy < ' tcx > > , CompileTimeEvalContext < ' a , ' mir , ' tcx > ) {
132132 // we start out with the best span we have
133133 // and try improving it down the road when more information is available
134134 let span = tcx. def_span ( cid. instance . def_id ( ) ) ;
135135 let span = mir. map ( |mir| mir. span ) . unwrap_or ( span) ;
136- let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator , ( ) ) ;
136+ let mut ecx = EvalContext :: new ( tcx. at ( span) , param_env, CompileTimeEvaluator :: new ( ) , ( ) ) ;
137137 let r = eval_body_using_ecx ( & mut ecx, cid, mir, param_env) ;
138138 ( r, ecx)
139139}
140140
141141// 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 > ,
142+ fn eval_body_using_ecx < ' mir , ' tcx > (
143+ ecx : & mut CompileTimeEvalContext < ' _ , ' mir , ' tcx > ,
144144 cid : GlobalId < ' tcx > ,
145145 mir : Option < & ' mir mir:: Mir < ' tcx > > ,
146146 param_env : ty:: ParamEnv < ' tcx > ,
@@ -187,17 +187,12 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
187187 Ok ( ret. into ( ) )
188188}
189189
190- #[ derive( Debug , Clone , Eq , PartialEq , Hash ) ]
191- pub struct CompileTimeEvaluator ;
192-
193190impl < ' tcx > Into < EvalError < ' tcx > > for ConstEvalError {
194191 fn into ( self ) -> EvalError < ' tcx > {
195192 EvalErrorKind :: MachineError ( self . to_string ( ) ) . into ( )
196193 }
197194}
198195
199- impl_stable_hash_for ! ( struct CompileTimeEvaluator { } ) ;
200-
201196#[ derive( Clone , Debug ) ]
202197enum ConstEvalError {
203198 NeedsRfc ( String ) ,
@@ -234,14 +229,39 @@ impl Error for ConstEvalError {
234229 }
235230}
236231
237- impl < ' mir , ' tcx > interpret:: Machine < ' mir , ' tcx > for CompileTimeEvaluator {
232+ // Extra machine state for CTFE, and the Machine instance
233+ pub struct CompileTimeEvaluator < ' a , ' mir , ' tcx : ' a +' mir > {
234+ /// When this value is negative, it indicates the number of interpreter
235+ /// steps *until* the loop detector is enabled. When it is positive, it is
236+ /// the number of steps after the detector has been enabled modulo the loop
237+ /// detector period.
238+ pub ( super ) steps_since_detector_enabled : isize ,
239+
240+ /// Extra state to detect loops.
241+ pub ( super ) loop_detector : snapshot:: InfiniteLoopDetector < ' a , ' mir , ' tcx > ,
242+ }
243+
244+ impl < ' a , ' mir , ' tcx > CompileTimeEvaluator < ' a , ' mir , ' tcx > {
245+ fn new ( ) -> Self {
246+ CompileTimeEvaluator {
247+ loop_detector : Default :: default ( ) ,
248+ steps_since_detector_enabled : -snapshot:: STEPS_UNTIL_DETECTOR_ENABLED ,
249+ }
250+ }
251+ }
252+
253+ type CompileTimeEvalContext < ' a , ' mir , ' tcx > =
254+ EvalContext < ' a , ' mir , ' tcx , CompileTimeEvaluator < ' a , ' mir , ' tcx > > ;
255+
256+ impl < ' a , ' mir , ' tcx > interpret:: Machine < ' a , ' mir , ' tcx >
257+ for CompileTimeEvaluator < ' a , ' mir , ' tcx >
258+ {
238259 type MemoryData = ( ) ;
239260 type MemoryKinds = !;
240261
241262 const MUT_STATIC_KIND : Option < !> = None ; // no mutating of statics allowed
242- const DETECT_LOOPS : bool = true ;
243263
244- fn find_fn < ' a > (
264+ fn find_fn (
245265 ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
246266 instance : ty:: Instance < ' tcx > ,
247267 args : & [ OpTy < ' tcx > ] ,
@@ -275,7 +295,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
275295 } ) )
276296 }
277297
278- fn call_intrinsic < ' a > (
298+ fn call_intrinsic (
279299 ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
280300 instance : ty:: Instance < ' tcx > ,
281301 args : & [ OpTy < ' tcx > ] ,
@@ -291,7 +311,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
291311 )
292312 }
293313
294- fn ptr_op < ' a > (
314+ fn ptr_op (
295315 _ecx : & EvalContext < ' a , ' mir , ' tcx , Self > ,
296316 _bin_op : mir:: BinOp ,
297317 _left : Scalar ,
@@ -304,21 +324,51 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeEvaluator {
304324 )
305325 }
306326
307- fn find_foreign_static < ' a > (
327+ fn find_foreign_static (
308328 _tcx : TyCtxtAt < ' a , ' tcx , ' tcx > ,
309329 _def_id : DefId ,
310330 ) -> EvalResult < ' tcx , & ' tcx Allocation > {
311331 err ! ( ReadForeignStatic )
312332 }
313333
314- fn box_alloc < ' a > (
334+ fn box_alloc (
315335 _ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ,
316336 _dest : PlaceTy < ' tcx > ,
317337 ) -> EvalResult < ' tcx > {
318338 Err (
319339 ConstEvalError :: NeedsRfc ( "heap allocations via `box` keyword" . to_string ( ) ) . into ( ) ,
320340 )
321341 }
342+
343+ fn before_terminator ( ecx : & mut EvalContext < ' a , ' mir , ' tcx , Self > ) -> EvalResult < ' tcx > {
344+ {
345+ let steps = & mut ecx. machine . steps_since_detector_enabled ;
346+
347+ * steps += 1 ;
348+ if * steps < 0 {
349+ return Ok ( ( ) ) ;
350+ }
351+
352+ * steps %= snapshot:: DETECTOR_SNAPSHOT_PERIOD ;
353+ if * steps != 0 {
354+ return Ok ( ( ) ) ;
355+ }
356+ }
357+
358+ if ecx. machine . loop_detector . is_empty ( ) {
359+ // First run of the loop detector
360+
361+ // FIXME(#49980): make this warning a lint
362+ ecx. tcx . sess . span_warn ( ecx. frame ( ) . span ,
363+ "Constant evaluating a complex constant, this might take some time" ) ;
364+ }
365+
366+ ecx. machine . loop_detector . observe_and_analyze (
367+ & ecx. tcx ,
368+ & ecx. memory ,
369+ & ecx. stack [ ..] ,
370+ )
371+ }
322372}
323373
324374/// Project to a field of a (variant of a) const
0 commit comments