@@ -149,18 +149,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
149
149
// sensitive check here. But we can at least rule out functions that are not const
150
150
// at all.
151
151
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) ? {
164
155
return Ok ( None ) ;
165
156
}
166
157
} else {
@@ -326,3 +317,43 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
326
317
}
327
318
}
328
319
}
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