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?
1679a7647d/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...
This commit is contained in:
commit
96bb8b31c8
24 changed files with 163 additions and 34 deletions
|
@ -0,0 +1,7 @@
|
|||
# `const_eval_limit`
|
||||
|
||||
The tracking issue for this feature is: [#67217]
|
||||
|
||||
[#67217]: https://github.com/rust-lang/rust/issues/67217
|
||||
|
||||
The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.
|
|
@ -1,9 +1,9 @@
|
|||
// Recursion limit.
|
||||
//
|
||||
// There are various parts of the compiler that must impose arbitrary limits
|
||||
// on how deeply they recurse to prevent stack overflow. Users can override
|
||||
// this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
|
||||
// just peeks and looks for that attribute.
|
||||
//! Registering limits, recursion_limit, type_length_limit and const_eval_limit
|
||||
//!
|
||||
//! There are various parts of the compiler that must impose arbitrary limits
|
||||
//! on how deeply they recurse to prevent stack overflow. Users can override
|
||||
//! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
|
||||
//! just peeks and looks for that attribute.
|
||||
|
||||
use crate::session::Session;
|
||||
use core::num::IntErrorKind;
|
||||
|
@ -16,6 +16,7 @@ use rustc_data_structures::sync::Once;
|
|||
pub fn update_limits(sess: &Session, krate: &ast::Crate) {
|
||||
update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
|
||||
update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
|
||||
update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
|
||||
}
|
||||
|
||||
fn update_limit(
|
||||
|
@ -37,10 +38,8 @@ fn update_limit(
|
|||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
let mut err = sess.struct_span_err(
|
||||
attr.span,
|
||||
"`recursion_limit` must be a non-negative integer",
|
||||
);
|
||||
let mut err =
|
||||
sess.struct_span_err(attr.span, "`limit` must be a non-negative integer");
|
||||
|
||||
let value_span = attr
|
||||
.meta()
|
||||
|
@ -49,11 +48,11 @@ fn update_limit(
|
|||
.unwrap_or(attr.span);
|
||||
|
||||
let error_str = match e.kind() {
|
||||
IntErrorKind::Overflow => "`recursion_limit` is too large",
|
||||
IntErrorKind::Empty => "`recursion_limit` must be a non-negative integer",
|
||||
IntErrorKind::Overflow => "`limit` is too large",
|
||||
IntErrorKind::Empty => "`limit` must be a non-negative integer",
|
||||
IntErrorKind::InvalidDigit => "not a valid integer",
|
||||
IntErrorKind::Underflow => bug!("`recursion_limit` should never underflow"),
|
||||
IntErrorKind::Zero => bug!("zero is a valid `recursion_limit`"),
|
||||
IntErrorKind::Underflow => bug!("`limit` should never underflow"),
|
||||
IntErrorKind::Zero => bug!("zero is a valid `limit`"),
|
||||
kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
|
||||
};
|
||||
|
|
@ -28,8 +28,8 @@ pub mod lib_features {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub mod limits;
|
||||
pub mod privacy;
|
||||
pub mod recursion_limit;
|
||||
pub mod region;
|
||||
pub mod resolve_lifetime;
|
||||
pub mod stability;
|
||||
|
|
|
@ -552,6 +552,9 @@ declare_features! (
|
|||
/// Allows the use of `no_sanitize` attribute.
|
||||
(active, no_sanitize, "1.42.0", Some(39699), None),
|
||||
|
||||
// Allows limiting the evaluation steps of const expressions
|
||||
(active, const_eval_limit, "1.43.0", Some(67217), None),
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// feature-group-end: actual feature gates
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -239,6 +239,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
// Limits:
|
||||
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")),
|
||||
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N")),
|
||||
gated!(
|
||||
const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
|
||||
experimental!(const_eval_limit)
|
||||
),
|
||||
|
||||
// Entry point:
|
||||
ungated!(main, Normal, template!(Word)),
|
||||
|
|
|
@ -189,7 +189,7 @@ pub fn register_plugins<'a>(
|
|||
}
|
||||
|
||||
sess.time("recursion_limit", || {
|
||||
middle::recursion_limit::update_limits(sess, &krate);
|
||||
middle::limits::update_limits(sess, &krate);
|
||||
});
|
||||
|
||||
let mut lint_store = rustc_lint::new_lint_store(
|
||||
|
|
|
@ -89,7 +89,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
|
|||
InterpCx::new(
|
||||
tcx.at(span),
|
||||
param_env,
|
||||
CompileTimeInterpreter::new(),
|
||||
CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()),
|
||||
MemoryExtra { can_access_statics },
|
||||
)
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ pub fn const_eval_raw_provider<'tcx>(
|
|||
let mut ecx = InterpCx::new(
|
||||
tcx.at(span),
|
||||
key.param_env,
|
||||
CompileTimeInterpreter::new(),
|
||||
CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()),
|
||||
MemoryExtra { can_access_statics: is_static },
|
||||
);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use rustc::ty::layout::HasTyCtxt;
|
|||
use rustc::ty::{self, Ty};
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::convert::TryFrom;
|
||||
use std::hash::Hash;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
@ -85,9 +86,6 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Number of steps until the detector even starts doing anything.
|
||||
/// Also, a warning is shown to the user when this number is reached.
|
||||
const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
|
||||
/// The number of steps between loop detector snapshots.
|
||||
/// Should be a power of two for performance reasons.
|
||||
const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
|
||||
|
@ -100,6 +98,8 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
|
|||
/// detector period.
|
||||
pub(super) steps_since_detector_enabled: isize,
|
||||
|
||||
pub(super) is_detector_enabled: bool,
|
||||
|
||||
/// Extra state to detect loops.
|
||||
pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
|
||||
}
|
||||
|
@ -111,10 +111,14 @@ pub struct MemoryExtra {
|
|||
}
|
||||
|
||||
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
|
||||
pub(super) fn new() -> Self {
|
||||
pub(super) fn new(const_eval_limit: usize) -> Self {
|
||||
let steps_until_detector_enabled =
|
||||
isize::try_from(const_eval_limit).unwrap_or(std::isize::MAX);
|
||||
|
||||
CompileTimeInterpreter {
|
||||
loop_detector: Default::default(),
|
||||
steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
|
||||
steps_since_detector_enabled: -steps_until_detector_enabled,
|
||||
is_detector_enabled: const_eval_limit != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -343,6 +347,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
}
|
||||
|
||||
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
|
||||
if !ecx.machine.is_detector_enabled {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
{
|
||||
let steps = &mut ecx.machine.steps_since_detector_enabled;
|
||||
|
||||
|
|
|
@ -88,6 +88,9 @@ pub struct Session {
|
|||
/// The maximum length of types during monomorphization.
|
||||
pub type_length_limit: Once<usize>,
|
||||
|
||||
/// The maximum blocks a const expression can evaluate.
|
||||
pub const_eval_limit: Once<usize>,
|
||||
|
||||
/// Map from imported macro spans (which consist of
|
||||
/// the localized span for the macro body) to the
|
||||
/// macro name and definition span in the source crate.
|
||||
|
@ -1053,6 +1056,7 @@ fn build_session_(
|
|||
features: Once::new(),
|
||||
recursion_limit: Once::new(),
|
||||
type_length_limit: Once::new(),
|
||||
const_eval_limit: Once::new(),
|
||||
imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
|
||||
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
|
||||
cgu_reuse_tracker,
|
||||
|
|
|
@ -208,6 +208,7 @@ symbols! {
|
|||
console,
|
||||
const_compare_raw_pointers,
|
||||
const_constructor,
|
||||
const_eval_limit,
|
||||
const_extern_fn,
|
||||
const_fn,
|
||||
const_fn_union,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// check-pass
|
||||
#![feature(const_eval_limit)]
|
||||
#![const_eval_limit="1000"]
|
||||
|
||||
const CONSTANT: usize = limit();
|
||||
|
||||
fn main() {
|
||||
assert_eq!(CONSTANT, 1764);
|
||||
}
|
||||
|
||||
const fn limit() -> usize {
|
||||
let x = 42;
|
||||
|
||||
x * 42
|
||||
}
|
15
src/test/ui/consts/const_limit/const_eval_limit_overflow.rs
Normal file
15
src/test/ui/consts/const_limit/const_eval_limit_overflow.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
#![feature(const_eval_limit)]
|
||||
#![const_eval_limit="18_446_744_073_709_551_615"]
|
||||
//~^ ERROR `limit` must be a non-negative integer
|
||||
|
||||
const CONSTANT: usize = limit();
|
||||
|
||||
fn main() {
|
||||
assert_eq!(CONSTANT, 1764);
|
||||
}
|
||||
|
||||
const fn limit() -> usize {
|
||||
let x = 42;
|
||||
|
||||
x * 42
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
error: `limit` must be a non-negative integer
|
||||
--> $DIR/const_eval_limit_overflow.rs:2:1
|
||||
|
|
||||
LL | #![const_eval_limit="18_446_744_073_709_551_615"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^----------------------------^
|
||||
| |
|
||||
| not a valid integer
|
||||
|
||||
error: aborting due to previous error
|
||||
|
21
src/test/ui/consts/const_limit/const_eval_limit_reached.rs
Normal file
21
src/test/ui/consts/const_limit/const_eval_limit_reached.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// ignore-tidy-linelength
|
||||
// only-x86_64
|
||||
// check-pass
|
||||
// NOTE: We always compile this test with -Copt-level=0 because higher opt-levels
|
||||
// optimize away the const function
|
||||
// compile-flags:-Copt-level=0
|
||||
#![feature(const_eval_limit)]
|
||||
#![const_eval_limit="2"]
|
||||
|
||||
const CONSTANT: usize = limit();
|
||||
//~^ WARNING Constant evaluating a complex constant, this might take some time
|
||||
|
||||
fn main() {
|
||||
assert_eq!(CONSTANT, 1764);
|
||||
}
|
||||
|
||||
const fn limit() -> usize { //~ WARNING Constant evaluating a complex constant, this might take some time
|
||||
let x = 42;
|
||||
|
||||
x * 42
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
warning: Constant evaluating a complex constant, this might take some time
|
||||
--> $DIR/const_eval_limit_reached.rs:17:1
|
||||
|
|
||||
LL | / const fn limit() -> usize {
|
||||
LL | | let x = 42;
|
||||
LL | |
|
||||
LL | | x * 42
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
warning: Constant evaluating a complex constant, this might take some time
|
||||
--> $DIR/const_eval_limit_reached.rs:10:1
|
||||
|
|
||||
LL | const CONSTANT: usize = limit();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#![const_eval_limit="42"]
|
||||
//~^ ERROR the `#[const_eval_limit]` attribute is an experimental feature [E0658]
|
||||
|
||||
const CONSTANT: usize = limit();
|
||||
|
||||
fn main() {
|
||||
assert_eq!(CONSTANT, 1764);
|
||||
}
|
||||
|
||||
const fn limit() -> usize {
|
||||
let x = 42;
|
||||
|
||||
x * 42
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
error[E0658]: the `#[const_eval_limit]` attribute is an experimental feature
|
||||
--> $DIR/feature-gate-const_eval_limit.rs:1:1
|
||||
|
|
||||
LL | #![const_eval_limit="42"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #67217 <https://github.com/rust-lang/rust/issues/67217> for more information
|
||||
= help: add `#![feature(const_eval_limit)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
|
@ -1,6 +1,6 @@
|
|||
// Test the parse error for an empty recursion_limit
|
||||
|
||||
#![recursion_limit = ""] //~ ERROR `recursion_limit` must be a non-negative integer
|
||||
//~| `recursion_limit` must be a non-negative integer
|
||||
#![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer
|
||||
//~| `limit` must be a non-negative integer
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
error: `recursion_limit` must be a non-negative integer
|
||||
error: `limit` must be a non-negative integer
|
||||
--> $DIR/empty.rs:3:1
|
||||
|
|
||||
LL | #![recursion_limit = ""]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^--^
|
||||
| |
|
||||
| `recursion_limit` must be a non-negative integer
|
||||
| `limit` must be a non-negative integer
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Test the parse error for an invalid digit in recursion_limit
|
||||
|
||||
#![recursion_limit = "-100"] //~ ERROR `recursion_limit` must be a non-negative integer
|
||||
#![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer
|
||||
//~| not a valid integer
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: `recursion_limit` must be a non-negative integer
|
||||
error: `limit` must be a non-negative integer
|
||||
--> $DIR/invalid_digit.rs:3:1
|
||||
|
|
||||
LL | #![recursion_limit = "-100"]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Test the parse error for an overflowing recursion_limit
|
||||
|
||||
#![recursion_limit = "999999999999999999999999"]
|
||||
//~^ ERROR `recursion_limit` must be a non-negative integer
|
||||
//~| `recursion_limit` is too large
|
||||
//~^ ERROR `limit` must be a non-negative integer
|
||||
//~| `limit` is too large
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
error: `recursion_limit` must be a non-negative integer
|
||||
error: `limit` must be a non-negative integer
|
||||
--> $DIR/overflow.rs:3:1
|
||||
|
|
||||
LL | #![recursion_limit = "999999999999999999999999"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^--------------------------^
|
||||
| |
|
||||
| `recursion_limit` is too large
|
||||
| `limit` is too large
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Test that a `recursion_limit` of 0 is valid
|
||||
// Test that a `limit` of 0 is valid
|
||||
|
||||
#![recursion_limit = "0"]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue