Skip to content

Commit 06e9c54

Browse files
committed
Add memoization for const function evaluations
When a const function is being evaluated, as long as all its arguments are zero-sized-types (or it has no arguments) then we can trivially memoize the evaluation result using the existing query mechanism.
1 parent eaac45a commit 06e9c54

10 files changed

+104
-132
lines changed

src/librustc/mir/interpret/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ use rustc_data_structures::tiny_list::TinyList;
123123
use rustc_macros::HashStable;
124124
use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian, BigEndian};
125125

126-
/// Uniquely identifies a specific constant or static.
126+
/// Uniquely identifies one of the following:
127+
/// - A constant
128+
/// - A static
129+
/// - A const fn where all arguments (if any) are zero-sized types
127130
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)]
128131
#[derive(HashStable, Lift)]
129132
pub struct GlobalId<'tcx> {

src/librustc_mir/const_eval.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,16 @@ fn eval_body_using_ecx<'mir, 'tcx>(
146146
let name = ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()));
147147
let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
148148
trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom);
149-
assert!(body.arg_count == 0);
149+
150+
// Assert all args (if any) are zero-sized types; `eval_body_using_ecx` doesn't
151+
// make sense if the body is expecting nontrival arguments.
152+
// (The alternative would be to use `eval_fn_call` with an args slice.)
153+
for arg in body.args_iter() {
154+
let decl = body.local_decls.get(arg).expect("arg missing from local_decls");
155+
let layout = ecx.layout_of(decl.ty.subst(tcx, cid.instance.substs))?;
156+
assert!(layout.is_zst())
157+
};
158+
150159
ecx.push_stack_frame(
151160
cid.instance,
152161
body.span,

src/librustc_mir/interpret/terminator.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use syntax::source_map::Span;
77
use rustc_target::spec::abi::Abi;
88

99
use super::{
10-
InterpResult, PointerArithmetic,
10+
GlobalId, InterpResult, PointerArithmetic,
1111
InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
1212
};
1313

@@ -316,6 +316,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
316316
}
317317
}
318318

319+
// If this function is a `const fn` then as an optimization we can query this
320+
// evaluation immediately.
321+
//
322+
// For the moment we only do this for functions which take no arguments
323+
// (or all arguments are ZSTs) so that we don't memoize too much.
324+
if self.tcx.is_const_fn_raw(instance.def.def_id()) &&
325+
args.iter().all(|a| a.layout.is_zst())
326+
{
327+
let gid = GlobalId { instance, promoted: None };
328+
return self.eval_const_fn_call(gid, dest, ret);
329+
}
330+
319331
// We need MIR for this fn
320332
let body = match M::find_fn(self, instance, args, dest, ret, unwind)? {
321333
Some(body) => body,
@@ -481,6 +493,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
481493
}
482494
}
483495

496+
/// Evaluate a const function where all arguments (if any) are zero-sized types.
497+
/// The evaluation is memoized thanks to the query system.
498+
fn eval_const_fn_call(
499+
&mut self,
500+
gid: GlobalId<'tcx>,
501+
dest: Option<PlaceTy<'tcx, M::PointerTag>>,
502+
ret: Option<mir::BasicBlock>,
503+
) -> InterpResult<'tcx> {
504+
trace!("eval_const_fn_call: {:?}", gid);
505+
506+
let place = self.const_eval_raw(gid)?;
507+
let dest = dest.ok_or_else(|| err_ub!(Unreachable))?;
508+
509+
self.copy_op(place.into(), dest)?;
510+
511+
// No stack frame gets pushed, the main loop will just act as if the
512+
// call completed.
513+
self.goto_block(ret)?;
514+
self.dump_place(*dest);
515+
return Ok(())
516+
}
517+
484518
fn drop_in_place(
485519
&mut self,
486520
place: PlaceTy<'tcx, M::PointerTag>,
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// build-pass
2+
3+
// Check that the evaluation of const-functions with
4+
// zero-sized types as arguments compiles successfully
5+
6+
struct Zst {}
7+
8+
const fn foo(val: Zst) -> Zst { val }
9+
10+
const FOO: Zst = foo(Zst {});
11+
12+
fn main() {
13+
const _: Zst = FOO;
14+
}

src/test/ui/consts/const-size_of-cycle.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{{constant}}#0`
1010
LL | bytes: [u8; std::mem::size_of::<Foo>()]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
note: ...which requires const-evaluating `Foo::bytes::{{constant}}#0`...
13+
--> $DIR/const-size_of-cycle.rs:5:17
14+
|
15+
LL | bytes: [u8; std::mem::size_of::<Foo>()]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
note: ...which requires const-evaluating `std::mem::size_of`...
1318
--> $SRC_DIR/libcore/mem/mod.rs:LL:COL
1419
|
1520
LL | intrinsics::size_of::<T>()
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// compile-fail
22

33
pub const unsafe fn fake_type<T>() -> T {
4-
hint_unreachable() //~ ERROR any use of this value will cause an error
4+
hint_unreachable()
55
}
66

77
pub const unsafe fn hint_unreachable() -> ! {
8-
fake_type()
8+
fake_type() //~ ERROR cycle detected when const-evaluating `hint_unreachable` [E0391]
99
}
1010

1111
trait Const {
@@ -15,5 +15,5 @@ trait Const {
1515
impl <T> Const for T {}
1616

1717
pub fn main() -> () {
18-
dbg!(i32::CONSTANT); //~ ERROR erroneous constant used
18+
dbg!(i32::CONSTANT);
1919
}
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,21 @@
1-
error: any use of this value will cause an error
1+
error[E0391]: cycle detected when const-evaluating `hint_unreachable`
2+
--> $DIR/uninhabited-const-issue-61744.rs:8:5
3+
|
4+
LL | fake_type()
5+
| ^^^^^^^^^^^
6+
|
7+
note: ...which requires const-evaluating `fake_type`...
28
--> $DIR/uninhabited-const-issue-61744.rs:4:5
39
|
410
LL | hint_unreachable()
511
| ^^^^^^^^^^^^^^^^^^
6-
| |
7-
| reached the configured maximum number of stack frames
8-
| inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5
9-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
10-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
11-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
12-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
13-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
14-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
15-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
16-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
17-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
18-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
19-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
20-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
21-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
22-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
23-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
24-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
25-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
26-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
27-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
28-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
29-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
30-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
31-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
32-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
33-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
34-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
35-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
36-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
37-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
38-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
39-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
40-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
41-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
42-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
43-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
44-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
45-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
46-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
47-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
48-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
49-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
50-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
51-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
52-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
53-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
54-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
55-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
56-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
57-
| inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
58-
| inside call to `fake_type::<i32>` at $DIR/uninhabited-const-issue-61744.rs:12:36
59-
...
60-
LL | const CONSTANT: i32 = unsafe { fake_type() };
61-
| ---------------------------------------------
62-
|
63-
= note: `#[deny(const_err)]` on by default
64-
65-
error[E0080]: erroneous constant used
66-
--> $DIR/uninhabited-const-issue-61744.rs:18:10
12+
= note: ...which again requires const-evaluating `hint_unreachable`, completing the cycle
13+
note: cycle used when const-evaluating `fake_type`
14+
--> $DIR/uninhabited-const-issue-61744.rs:4:5
6715
|
68-
LL | dbg!(i32::CONSTANT);
69-
| ^^^^^^^^^^^^^ referenced constant has errors
16+
LL | hint_unreachable()
17+
| ^^^^^^^^^^^^^^^^^^
7018

71-
error: aborting due to 2 previous errors
19+
error: aborting due to previous error
7220

73-
For more information about this error, try `rustc --explain E0080`.
21+
For more information about this error, try `rustc --explain E0391`.

src/test/ui/infinite/infinite-recursion-const-fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//https://github.com/rust-lang/rust/issues/31364
22

3-
const fn a() -> usize { b() } //~ ERROR evaluation of constant value failed
3+
const fn a() -> usize { b() } //~ ERROR cycle detected when const-evaluating `a` [E0391]
44
const fn b() -> usize { a() }
55
const ARR: [i32; a()] = [5; 6];
66

Original file line numberDiff line numberDiff line change
@@ -1,66 +1,21 @@
1-
error[E0080]: evaluation of constant value failed
1+
error[E0391]: cycle detected when const-evaluating `a`
22
--> $DIR/infinite-recursion-const-fn.rs:3:25
33
|
44
LL | const fn a() -> usize { b() }
55
| ^^^
6-
| |
7-
| reached the configured maximum number of stack frames
8-
| inside call to `b` at $DIR/infinite-recursion-const-fn.rs:3:25
6+
|
7+
note: ...which requires const-evaluating `b`...
8+
--> $DIR/infinite-recursion-const-fn.rs:4:25
9+
|
910
LL | const fn b() -> usize { a() }
10-
| ---
11-
| |
12-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
13-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
14-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
15-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
16-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
17-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
18-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
19-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
20-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
21-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
22-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
23-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
24-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
25-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
26-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
27-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
28-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
29-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
30-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
31-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
32-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
33-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
34-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
35-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
36-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
37-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
38-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
39-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
40-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
41-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
42-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
43-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
44-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
45-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
46-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
47-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
48-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
49-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
50-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
51-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
52-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
53-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
54-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
55-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
56-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
57-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
58-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
59-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
60-
| inside call to `a` at $DIR/infinite-recursion-const-fn.rs:4:25
11+
| ^^^
12+
= note: ...which again requires const-evaluating `a`, completing the cycle
13+
note: cycle used when const-evaluating `ARR::{{constant}}#0`
14+
--> $DIR/infinite-recursion-const-fn.rs:5:18
15+
|
6116
LL | const ARR: [i32; a()] = [5; 6];
62-
| --- inside call to `a` at $DIR/infinite-recursion-const-fn.rs:5:18
17+
| ^^^
6318

6419
error: aborting due to previous error
6520

66-
For more information about this error, try `rustc --explain E0080`.
21+
For more information about this error, try `rustc --explain E0391`.

src/test/ui/invalid_const_promotion.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ use std::env;
1717
use std::process::{Command, Stdio};
1818

1919
// this will panic in debug mode and overflow in release mode
20+
//
21+
// NB we give bar an unused argument because otherwise memoization
22+
// of the const fn kicks in, causing a different code path in the
23+
// compiler to be executed (see PR #66294).
2024
#[stable(feature = "rustc", since = "1.0.0")]
2125
#[rustc_promotable]
22-
const fn bar() -> usize { 0 - 1 }
26+
const fn bar(_: bool) -> usize { 0 - 1 }
2327

2428
fn foo() {
25-
let _: &'static _ = &bar();
29+
let _: &'static _ = &bar(true);
2630
}
2731

2832
#[cfg(unix)]

0 commit comments

Comments
 (0)