@@ -149,18 +149,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
149149 // sensitive check here. But we can at least rule out functions that are not const
150150 // at all.
151151 if ecx. tcx . is_const_fn_raw ( def_id) {
152- // If this function is a `const fn` then as an optimization we can query this
153- // evaluation immediately.
154- //
155- // For the moment we only do this for functions which take no arguments
156- // (or all arguments are ZSTs) so that we don't memoize too much.
157- //
158- // Because `#[track_caller]` adds an implicit non-ZST argument, we also cannot
159- // perform this optimization on items tagged with it.
160- let no_implicit_args = !instance. def . requires_caller_location ( ecx. tcx ( ) ) ;
161- if args. iter ( ) . all ( |a| a. layout . is_zst ( ) ) && no_implicit_args {
162- let gid = GlobalId { instance, promoted : None } ;
163- ecx. eval_const_fn_call ( gid, ret) ?;
152+ // If this function is a `const fn` then under certain circumstances we
153+ // can evaluate call via the query system, thus memoizing all future calls.
154+ if ecx. try_eval_const_fn_call ( instance, ret, args) ? {
164155 return Ok ( None ) ;
165156 }
166157 } else {
@@ -326,3 +317,43 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
326317 }
327318 }
328319}
320+
321+ impl < ' mir , ' tcx > InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > {
322+ /// Evaluate a const function where all arguments (if any) are zero-sized types.
323+ /// The evaluation is memoized thanks to the query system.
324+ ///
325+ /// Returns `true` if the call has been evaluated.
326+ fn try_eval_const_fn_call (
327+ & mut self ,
328+ instance : ty:: Instance < ' tcx > ,
329+ ret : Option < ( PlaceTy < ' tcx > , mir:: BasicBlock ) > ,
330+ args : & [ OpTy < ' tcx > ] ,
331+ ) -> InterpResult < ' tcx , bool > {
332+ trace ! ( "try_eval_const_fn_call: {:?}" , instance) ;
333+ // Because `#[track_caller]` adds an implicit non-ZST argument, we also cannot
334+ // perform this optimization on items tagged with it.
335+ if instance. def . requires_caller_location ( self . tcx ( ) ) {
336+ return Ok ( false ) ;
337+ }
338+ // For the moment we only do this for functions which take no arguments
339+ // (or all arguments are ZSTs) so that we don't memoize too much.
340+ if args. iter ( ) . any ( |a| !a. layout . is_zst ( ) ) {
341+ return Ok ( false ) ;
342+ }
343+
344+ let gid = GlobalId { instance, promoted : None } ;
345+
346+ let place = self . const_eval_raw ( gid) ?;
347+ let dest = match ret {
348+ Some ( ( dest, _) ) => dest,
349+ // Don't memoize diverging function calls.
350+ None => return Ok ( false ) ,
351+ } ;
352+
353+ self . copy_op ( place. into ( ) , dest) ?;
354+
355+ self . return_to_block ( ret. map ( |r| r. 1 ) ) ?;
356+ self . dump_place ( * dest) ;
357+ return Ok ( true ) ;
358+ }
359+ }
0 commit comments