Skip to content

Commit 72c99f2

Browse files
authoredMar 23, 2020
Rollup merge of rust-lang#70087 - ecstatic-morse:remove-const-eval-loop-detector, r=RalfJung
Remove const eval loop detector Now that there is a configurable instruction limit for CTFE (see rust-lang#67260), we can replace the loop detector with something much simpler. See rust-lang#66946 for more discussion about this. Although the instruction limit is nightly-only, the only practical way to reach the default limit uses nightly-only features as well (although CTFE will still execute code using such features inside an array initializer on stable). This will at the very least require a crater run, since it will result in an error wherever the "long running const eval" warning appeared before. We may need to increase the default for `const_eval_limit` to work around this. Resolves rust-lang#54384 cc rust-lang#49980 r? @oli-obk cc @RalfJung
2 parents a73ed5a + b5636b8 commit 72c99f2

File tree

12 files changed

+73
-564
lines changed

12 files changed

+73
-564
lines changed
 

‎src/librustc/mir/interpret/error.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,10 @@ impl fmt::Debug for UnsupportedOpInfo {
494494
pub enum ResourceExhaustionInfo {
495495
/// The stack grew too big.
496496
StackFrameLimitReached,
497-
/// The program ran into an infinite loop.
498-
InfiniteLoop,
497+
/// The program ran for too long.
498+
///
499+
/// The exact limit is set by the `const_eval_limit` attribute.
500+
StepLimitReached,
499501
}
500502

501503
impl fmt::Debug for ResourceExhaustionInfo {
@@ -505,11 +507,9 @@ impl fmt::Debug for ResourceExhaustionInfo {
505507
StackFrameLimitReached => {
506508
write!(f, "reached the configured maximum number of stack frames")
507509
}
508-
InfiniteLoop => write!(
509-
f,
510-
"duplicate interpreter state observed here, const evaluation will never \
511-
terminate"
512-
),
510+
StepLimitReached => {
511+
write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
512+
}
513513
}
514514
}
515515
}

‎src/librustc_mir/const_eval/machine.rs

+20-47
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use rustc::ty::layout::HasTyCtxt;
33
use rustc::ty::{self, Ty};
44
use std::borrow::{Borrow, Cow};
55
use std::collections::hash_map::Entry;
6-
use std::convert::TryFrom;
76
use std::hash::Hash;
87

98
use rustc_data_structures::fx::FxHashMap;
@@ -13,13 +12,13 @@ use rustc_span::source_map::Span;
1312
use rustc_span::symbol::Symbol;
1413

1514
use crate::interpret::{
16-
self, snapshot, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory,
17-
MemoryKind, OpTy, PlaceTy, Pointer, Scalar,
15+
self, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory, MemoryKind, OpTy,
16+
PlaceTy, Pointer, Scalar,
1817
};
1918

2019
use super::error::*;
2120

22-
impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
21+
impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> {
2322
/// Evaluate a const function where all arguments (if any) are zero-sized types.
2423
/// The evaluation is memoized thanks to the query system.
2524
///
@@ -86,22 +85,13 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
8685
}
8786
}
8887

89-
/// The number of steps between loop detector snapshots.
90-
/// Should be a power of two for performance reasons.
91-
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
92-
93-
// Extra machine state for CTFE, and the Machine instance
94-
pub struct CompileTimeInterpreter<'mir, 'tcx> {
95-
/// When this value is negative, it indicates the number of interpreter
96-
/// steps *until* the loop detector is enabled. When it is positive, it is
97-
/// the number of steps after the detector has been enabled modulo the loop
98-
/// detector period.
99-
pub(super) steps_since_detector_enabled: isize,
100-
101-
pub(super) is_detector_enabled: bool,
102-
103-
/// Extra state to detect loops.
104-
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
88+
/// Extra machine state for CTFE, and the Machine instance
89+
pub struct CompileTimeInterpreter {
90+
/// For now, the number of terminators that can be evaluated before we throw a resource
91+
/// exhuastion error.
92+
///
93+
/// Setting this to `0` disables the limit and allows the interpreter to run forever.
94+
pub steps_remaining: usize,
10595
}
10696

10797
#[derive(Copy, Clone, Debug)]
@@ -110,16 +100,9 @@ pub struct MemoryExtra {
110100
pub(super) can_access_statics: bool,
111101
}
112102

113-
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
103+
impl CompileTimeInterpreter {
114104
pub(super) fn new(const_eval_limit: usize) -> Self {
115-
let steps_until_detector_enabled =
116-
isize::try_from(const_eval_limit).unwrap_or(std::isize::MAX);
117-
118-
CompileTimeInterpreter {
119-
loop_detector: Default::default(),
120-
steps_since_detector_enabled: -steps_until_detector_enabled,
121-
is_detector_enabled: const_eval_limit != 0,
122-
}
105+
CompileTimeInterpreter { steps_remaining: const_eval_limit }
123106
}
124107
}
125108

@@ -173,8 +156,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
173156
}
174157
}
175158

176-
crate type CompileTimeEvalContext<'mir, 'tcx> =
177-
InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>;
159+
crate type CompileTimeEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, CompileTimeInterpreter>;
178160

179161
impl interpret::MayLeak for ! {
180162
#[inline(always)]
@@ -184,7 +166,7 @@ impl interpret::MayLeak for ! {
184166
}
185167
}
186168

187-
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> {
169+
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
188170
type MemoryKinds = !;
189171
type PointerTag = ();
190172
type ExtraFnVal = !;
@@ -345,26 +327,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
345327
}
346328

347329
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
348-
if !ecx.machine.is_detector_enabled {
330+
// The step limit has already been hit in a previous call to `before_terminator`.
331+
if ecx.machine.steps_remaining == 0 {
349332
return Ok(());
350333
}
351334

352-
{
353-
let steps = &mut ecx.machine.steps_since_detector_enabled;
354-
355-
*steps += 1;
356-
if *steps < 0 {
357-
return Ok(());
358-
}
359-
360-
*steps %= DETECTOR_SNAPSHOT_PERIOD;
361-
if *steps != 0 {
362-
return Ok(());
363-
}
335+
ecx.machine.steps_remaining -= 1;
336+
if ecx.machine.steps_remaining == 0 {
337+
throw_exhaust!(StepLimitReached)
364338
}
365339

366-
let span = ecx.frame().span;
367-
ecx.machine.loop_detector.observe_and_analyze(*ecx.tcx, span, &ecx.memory, &ecx.stack[..])
340+
Ok(())
368341
}
369342

370343
#[inline(always)]

‎src/librustc_mir/interpret/memory.rs

-19
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M>
112112
}
113113
}
114114

115-
// FIXME: Really we shouldn't clone memory, ever. Snapshot machinery should instead
116-
// carefully copy only the reachable parts.
117-
impl<'mir, 'tcx, M> Clone for Memory<'mir, 'tcx, M>
118-
where
119-
M: Machine<'mir, 'tcx, PointerTag = (), AllocExtra = ()>,
120-
M::MemoryExtra: Copy,
121-
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
122-
{
123-
fn clone(&self) -> Self {
124-
Memory {
125-
alloc_map: self.alloc_map.clone(),
126-
extra_fn_ptr_map: self.extra_fn_ptr_map.clone(),
127-
dead_alloc_map: self.dead_alloc_map.clone(),
128-
extra: self.extra,
129-
tcx: self.tcx,
130-
}
131-
}
132-
}
133-
134115
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
135116
pub fn new(tcx: TyCtxtAt<'tcx>, extra: M::MemoryExtra) -> Self {
136117
Memory {

‎src/librustc_mir/interpret/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ mod memory;
99
mod operand;
1010
mod operator;
1111
mod place;
12-
pub(crate) mod snapshot; // for const_eval
1312
mod step;
1413
mod terminator;
1514
mod traits;

‎src/librustc_mir/interpret/snapshot.rs

-420
This file was deleted.

‎src/test/ui/consts/const-eval/infinite_loop.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ fn main() {
22
// Tests the Collatz conjecture with an incorrect base case (0 instead of 1).
33
// The value of `n` will loop indefinitely (4 - 2 - 1 - 4).
44
let _ = [(); {
5-
//~^ WARNING Constant evaluating a complex constant, this might take some time
65
let mut n = 113383; // #20 in https://oeis.org/A006884
76
while n != 0 {
87
//~^ ERROR `while` is not allowed in a `const`

‎src/test/ui/consts/const-eval/infinite_loop.stderr

+4-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0658]: `while` is not allowed in a `const`
2-
--> $DIR/infinite_loop.rs:7:9
2+
--> $DIR/infinite_loop.rs:6:9
33
|
44
LL | / while n != 0 {
55
LL | |
@@ -14,32 +14,19 @@ LL | | }
1414
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
1515

1616
error[E0658]: `if` is not allowed in a `const`
17-
--> $DIR/infinite_loop.rs:9:17
17+
--> $DIR/infinite_loop.rs:8:17
1818
|
1919
LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 };
2020
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2121
|
2222
= note: see issue #49146 <https://github.com/rust-lang/rust/issues/49146> for more information
2323
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
2424

25-
warning: Constant evaluating a complex constant, this might take some time
26-
--> $DIR/infinite_loop.rs:4:18
27-
|
28-
LL | let _ = [(); {
29-
| __________________^
30-
LL | |
31-
LL | | let mut n = 113383; // #20 in https://oeis.org/A006884
32-
LL | | while n != 0 {
33-
... |
34-
LL | | n
35-
LL | | }];
36-
| |_____^
37-
3825
error[E0080]: evaluation of constant value failed
39-
--> $DIR/infinite_loop.rs:9:20
26+
--> $DIR/infinite_loop.rs:8:20
4027
|
4128
LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 };
42-
| ^^^^^^^^^^ duplicate interpreter state observed here, const evaluation will never terminate
29+
| ^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
4330

4431
error: aborting due to 3 previous errors
4532

‎src/test/ui/consts/const-eval/issue-52475.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
fn main() {
22
let _ = [(); {
3-
//~^ WARNING Constant evaluating a complex constant, this might take some time
43
let mut x = &0;
54
let mut n = 0;
65
while n < 5 {

‎src/test/ui/consts/const-eval/issue-52475.stderr

+3-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0658]: `while` is not allowed in a `const`
2-
--> $DIR/issue-52475.rs:6:9
2+
--> $DIR/issue-52475.rs:5:9
33
|
44
LL | / while n < 5 {
55
LL | |
@@ -12,24 +12,11 @@ LL | | }
1212
= help: add `#![feature(const_loop)]` to the crate attributes to enable
1313
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
1414

15-
warning: Constant evaluating a complex constant, this might take some time
16-
--> $DIR/issue-52475.rs:2:18
17-
|
18-
LL | let _ = [(); {
19-
| __________________^
20-
LL | |
21-
LL | | let mut x = &0;
22-
LL | | let mut n = 0;
23-
... |
24-
LL | | 0
25-
LL | | }];
26-
| |_____^
27-
2815
error[E0080]: evaluation of constant value failed
29-
--> $DIR/issue-52475.rs:8:17
16+
--> $DIR/issue-52475.rs:7:17
3017
|
3118
LL | n = (n + 1) % 5;
32-
| ^^^^^^^^^^^ duplicate interpreter state observed here, const evaluation will never terminate
19+
| ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
3320

3421
error: aborting due to 2 previous errors
3522

Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
// check-pass
2+
23
#![feature(const_eval_limit)]
3-
#![const_eval_limit="1000"]
4+
#![feature(const_loop, const_if_match)]
45

5-
const CONSTANT: usize = limit();
6+
// This needs to be higher than the number of loop iterations since each pass through the loop may
7+
// hit more than one terminator.
8+
#![const_eval_limit="4000"]
69

7-
fn main() {
8-
assert_eq!(CONSTANT, 1764);
9-
}
10+
const X: usize = {
11+
let mut x = 0;
12+
while x != 1000 {
13+
x += 1;
14+
}
1015

11-
const fn limit() -> usize {
12-
let x = 42;
16+
x
17+
};
1318

14-
x * 42
19+
fn main() {
20+
assert_eq!(X, 1000);
1521
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
// ignore-tidy-linelength
2-
// only-x86_64
3-
// check-pass
4-
// NOTE: We always compile this test with -Copt-level=0 because higher opt-levels
5-
// optimize away the const function
6-
// compile-flags:-Copt-level=0
71
#![feature(const_eval_limit)]
8-
#![const_eval_limit="2"]
2+
#![feature(const_loop, const_if_match)]
93

10-
const CONSTANT: usize = limit();
11-
//~^ WARNING Constant evaluating a complex constant, this might take some time
4+
#![const_eval_limit="500"]
125

13-
fn main() {
14-
assert_eq!(CONSTANT, 1764);
15-
}
6+
const X: usize = {
7+
let mut x = 0;
8+
while x != 1000 {
9+
//~^ ERROR any use of this value will cause an error
10+
x += 1;
11+
}
1612

17-
const fn limit() -> usize { //~ WARNING Constant evaluating a complex constant, this might take some time
18-
let x = 42;
13+
x
14+
};
1915

20-
x * 42
16+
fn main() {
17+
assert_eq!(X, 1000);
2118
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
warning: Constant evaluating a complex constant, this might take some time
2-
--> $DIR/const_eval_limit_reached.rs:17:1
1+
error: any use of this value will cause an error
2+
--> $DIR/const_eval_limit_reached.rs:8:11
33
|
4-
LL | / const fn limit() -> usize {
5-
LL | | let x = 42;
4+
LL | / const X: usize = {
5+
LL | | let mut x = 0;
6+
LL | | while x != 1000 {
7+
| | ^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
68
LL | |
7-
LL | | x * 42
8-
LL | | }
9-
| |_^
10-
11-
warning: Constant evaluating a complex constant, this might take some time
12-
--> $DIR/const_eval_limit_reached.rs:10:1
9+
... |
10+
LL | | x
11+
LL | | };
12+
| |__-
1313
|
14-
LL | const CONSTANT: usize = limit();
15-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
= note: `#[deny(const_err)]` on by default
15+
16+
error: aborting due to previous error
1617

0 commit comments

Comments
 (0)
Please sign in to comment.