Skip to content

Commit 96bb8b3

Browse files
committed
Auto merge of #67260 - TheSamsa:const-limit, r=oli-obk
const limit for CTFE I tried to tackle the first steps for this issue. The active feature flag does link to the issue below, I think this has to change, because there should be a tracking issue? https://github.com/TheSamsa/rust/blob/1679a7647da0de672bac26b716db82d16f3896a8/src/librustc_feature/active.rs#L530 Also, I only put up the storage of the limit like "recursion_limit" but created a seperate file in the same place. Since I guess the invocation happens seperately. https://github.com/TheSamsa/rust/blob/const-limit/src/librustc/middle/const_limit.rs If this does not hold up for the issue and since there is a time pressure, just reject it. hopefully this does not put more load on you than I expected...
2 parents c79f5f0 + 527456e commit 96bb8b3

24 files changed

+163
-34
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# `const_eval_limit`
2+
3+
The tracking issue for this feature is: [#67217]
4+
5+
[#67217]: https://github.com/rust-lang/rust/issues/67217
6+
7+
The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.

src/librustc/middle/recursion_limit.rs src/librustc/middle/limits.rs

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// Recursion limit.
2-
//
3-
// There are various parts of the compiler that must impose arbitrary limits
4-
// on how deeply they recurse to prevent stack overflow. Users can override
5-
// this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
6-
// just peeks and looks for that attribute.
1+
//! Registering limits, recursion_limit, type_length_limit and const_eval_limit
2+
//!
3+
//! There are various parts of the compiler that must impose arbitrary limits
4+
//! on how deeply they recurse to prevent stack overflow. Users can override
5+
//! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
6+
//! just peeks and looks for that attribute.
77
88
use crate::session::Session;
99
use core::num::IntErrorKind;
@@ -16,6 +16,7 @@ use rustc_data_structures::sync::Once;
1616
pub fn update_limits(sess: &Session, krate: &ast::Crate) {
1717
update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
1818
update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
19+
update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
1920
}
2021

2122
fn update_limit(
@@ -37,10 +38,8 @@ fn update_limit(
3738
return;
3839
}
3940
Err(e) => {
40-
let mut err = sess.struct_span_err(
41-
attr.span,
42-
"`recursion_limit` must be a non-negative integer",
43-
);
41+
let mut err =
42+
sess.struct_span_err(attr.span, "`limit` must be a non-negative integer");
4443

4544
let value_span = attr
4645
.meta()
@@ -49,11 +48,11 @@ fn update_limit(
4948
.unwrap_or(attr.span);
5049

5150
let error_str = match e.kind() {
52-
IntErrorKind::Overflow => "`recursion_limit` is too large",
53-
IntErrorKind::Empty => "`recursion_limit` must be a non-negative integer",
51+
IntErrorKind::Overflow => "`limit` is too large",
52+
IntErrorKind::Empty => "`limit` must be a non-negative integer",
5453
IntErrorKind::InvalidDigit => "not a valid integer",
55-
IntErrorKind::Underflow => bug!("`recursion_limit` should never underflow"),
56-
IntErrorKind::Zero => bug!("zero is a valid `recursion_limit`"),
54+
IntErrorKind::Underflow => bug!("`limit` should never underflow"),
55+
IntErrorKind::Zero => bug!("zero is a valid `limit`"),
5756
kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
5857
};
5958

src/librustc/middle/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ pub mod lib_features {
2828
}
2929
}
3030
}
31+
pub mod limits;
3132
pub mod privacy;
32-
pub mod recursion_limit;
3333
pub mod region;
3434
pub mod resolve_lifetime;
3535
pub mod stability;

src/librustc_feature/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,9 @@ declare_features! (
552552
/// Allows the use of `no_sanitize` attribute.
553553
(active, no_sanitize, "1.42.0", Some(39699), None),
554554

555+
// Allows limiting the evaluation steps of const expressions
556+
(active, const_eval_limit, "1.43.0", Some(67217), None),
557+
555558
// -------------------------------------------------------------------------
556559
// feature-group-end: actual feature gates
557560
// -------------------------------------------------------------------------

src/librustc_feature/builtin_attrs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
239239
// Limits:
240240
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")),
241241
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N")),
242+
gated!(
243+
const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
244+
experimental!(const_eval_limit)
245+
),
242246

243247
// Entry point:
244248
ungated!(main, Normal, template!(Word)),

src/librustc_interface/passes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ pub fn register_plugins<'a>(
189189
}
190190

191191
sess.time("recursion_limit", || {
192-
middle::recursion_limit::update_limits(sess, &krate);
192+
middle::limits::update_limits(sess, &krate);
193193
});
194194

195195
let mut lint_store = rustc_lint::new_lint_store(

src/librustc_mir/const_eval/eval_queries.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
8989
InterpCx::new(
9090
tcx.at(span),
9191
param_env,
92-
CompileTimeInterpreter::new(),
92+
CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()),
9393
MemoryExtra { can_access_statics },
9494
)
9595
}
@@ -297,7 +297,7 @@ pub fn const_eval_raw_provider<'tcx>(
297297
let mut ecx = InterpCx::new(
298298
tcx.at(span),
299299
key.param_env,
300-
CompileTimeInterpreter::new(),
300+
CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()),
301301
MemoryExtra { can_access_statics: is_static },
302302
);
303303

src/librustc_mir/const_eval/machine.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ 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;
67
use std::hash::Hash;
78

89
use rustc_data_structures::fx::FxHashMap;
@@ -85,9 +86,6 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
8586
}
8687
}
8788

88-
/// Number of steps until the detector even starts doing anything.
89-
/// Also, a warning is shown to the user when this number is reached.
90-
const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
9189
/// The number of steps between loop detector snapshots.
9290
/// Should be a power of two for performance reasons.
9391
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
@@ -100,6 +98,8 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
10098
/// detector period.
10199
pub(super) steps_since_detector_enabled: isize,
102100

101+
pub(super) is_detector_enabled: bool,
102+
103103
/// Extra state to detect loops.
104104
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
105105
}
@@ -111,10 +111,14 @@ pub struct MemoryExtra {
111111
}
112112

113113
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
114-
pub(super) fn new() -> Self {
114+
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+
115118
CompileTimeInterpreter {
116119
loop_detector: Default::default(),
117-
steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
120+
steps_since_detector_enabled: -steps_until_detector_enabled,
121+
is_detector_enabled: const_eval_limit != 0,
118122
}
119123
}
120124
}
@@ -343,6 +347,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
343347
}
344348

345349
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
350+
if !ecx.machine.is_detector_enabled {
351+
return Ok(());
352+
}
353+
346354
{
347355
let steps = &mut ecx.machine.steps_since_detector_enabled;
348356

src/librustc_session/session.rs

+4
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ pub struct Session {
8888
/// The maximum length of types during monomorphization.
8989
pub type_length_limit: Once<usize>,
9090

91+
/// The maximum blocks a const expression can evaluate.
92+
pub const_eval_limit: Once<usize>,
93+
9194
/// Map from imported macro spans (which consist of
9295
/// the localized span for the macro body) to the
9396
/// macro name and definition span in the source crate.
@@ -1053,6 +1056,7 @@ fn build_session_(
10531056
features: Once::new(),
10541057
recursion_limit: Once::new(),
10551058
type_length_limit: Once::new(),
1059+
const_eval_limit: Once::new(),
10561060
imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
10571061
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
10581062
cgu_reuse_tracker,

src/librustc_span/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ symbols! {
208208
console,
209209
const_compare_raw_pointers,
210210
const_constructor,
211+
const_eval_limit,
211212
const_extern_fn,
212213
const_fn,
213214
const_fn_union,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// check-pass
2+
#![feature(const_eval_limit)]
3+
#![const_eval_limit="1000"]
4+
5+
const CONSTANT: usize = limit();
6+
7+
fn main() {
8+
assert_eq!(CONSTANT, 1764);
9+
}
10+
11+
const fn limit() -> usize {
12+
let x = 42;
13+
14+
x * 42
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(const_eval_limit)]
2+
#![const_eval_limit="18_446_744_073_709_551_615"]
3+
//~^ ERROR `limit` must be a non-negative integer
4+
5+
const CONSTANT: usize = limit();
6+
7+
fn main() {
8+
assert_eq!(CONSTANT, 1764);
9+
}
10+
11+
const fn limit() -> usize {
12+
let x = 42;
13+
14+
x * 42
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: `limit` must be a non-negative integer
2+
--> $DIR/const_eval_limit_overflow.rs:2:1
3+
|
4+
LL | #![const_eval_limit="18_446_744_073_709_551_615"]
5+
| ^^^^^^^^^^^^^^^^^^^^----------------------------^
6+
| |
7+
| not a valid integer
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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
7+
#![feature(const_eval_limit)]
8+
#![const_eval_limit="2"]
9+
10+
const CONSTANT: usize = limit();
11+
//~^ WARNING Constant evaluating a complex constant, this might take some time
12+
13+
fn main() {
14+
assert_eq!(CONSTANT, 1764);
15+
}
16+
17+
const fn limit() -> usize { //~ WARNING Constant evaluating a complex constant, this might take some time
18+
let x = 42;
19+
20+
x * 42
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
warning: Constant evaluating a complex constant, this might take some time
2+
--> $DIR/const_eval_limit_reached.rs:17:1
3+
|
4+
LL | / const fn limit() -> usize {
5+
LL | | let x = 42;
6+
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
13+
|
14+
LL | const CONSTANT: usize = limit();
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![const_eval_limit="42"]
2+
//~^ ERROR the `#[const_eval_limit]` attribute is an experimental feature [E0658]
3+
4+
const CONSTANT: usize = limit();
5+
6+
fn main() {
7+
assert_eq!(CONSTANT, 1764);
8+
}
9+
10+
const fn limit() -> usize {
11+
let x = 42;
12+
13+
x * 42
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: the `#[const_eval_limit]` attribute is an experimental feature
2+
--> $DIR/feature-gate-const_eval_limit.rs:1:1
3+
|
4+
LL | #![const_eval_limit="42"]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #67217 <https://github.com/rust-lang/rust/issues/67217> for more information
8+
= help: add `#![feature(const_eval_limit)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

src/test/ui/recursion_limit/empty.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Test the parse error for an empty recursion_limit
22

3-
#![recursion_limit = ""] //~ ERROR `recursion_limit` must be a non-negative integer
4-
//~| `recursion_limit` must be a non-negative integer
3+
#![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer
4+
//~| `limit` must be a non-negative integer
55

66
fn main() {}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: `recursion_limit` must be a non-negative integer
1+
error: `limit` must be a non-negative integer
22
--> $DIR/empty.rs:3:1
33
|
44
LL | #![recursion_limit = ""]
55
| ^^^^^^^^^^^^^^^^^^^^^--^
66
| |
7-
| `recursion_limit` must be a non-negative integer
7+
| `limit` must be a non-negative integer
88

99
error: aborting due to previous error
1010

Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Test the parse error for an invalid digit in recursion_limit
22

3-
#![recursion_limit = "-100"] //~ ERROR `recursion_limit` must be a non-negative integer
3+
#![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer
44
//~| not a valid integer
55

66
fn main() {}

src/test/ui/recursion_limit/invalid_digit.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: `recursion_limit` must be a non-negative integer
1+
error: `limit` must be a non-negative integer
22
--> $DIR/invalid_digit.rs:3:1
33
|
44
LL | #![recursion_limit = "-100"]
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Test the parse error for an overflowing recursion_limit
22

33
#![recursion_limit = "999999999999999999999999"]
4-
//~^ ERROR `recursion_limit` must be a non-negative integer
5-
//~| `recursion_limit` is too large
4+
//~^ ERROR `limit` must be a non-negative integer
5+
//~| `limit` is too large
66

77
fn main() {}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: `recursion_limit` must be a non-negative integer
1+
error: `limit` must be a non-negative integer
22
--> $DIR/overflow.rs:3:1
33
|
44
LL | #![recursion_limit = "999999999999999999999999"]
55
| ^^^^^^^^^^^^^^^^^^^^^--------------------------^
66
| |
7-
| `recursion_limit` is too large
7+
| `limit` is too large
88

99
error: aborting due to previous error
1010

src/test/ui/recursion_limit/zero.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Test that a `recursion_limit` of 0 is valid
1+
// Test that a `limit` of 0 is valid
22

33
#![recursion_limit = "0"]
44

0 commit comments

Comments
 (0)