Auto merge of #103877 - oli-obk:const_eval_step_limit, r=fee1-dead

Replace const eval limit by a lint and add an exponential backoff warning

The lint triggers at the first power of 2 that comes after 1 million function calls or traversed back-edges (takes less than a second on usual programs). After the first emission, an unsilenceable warning is repeated at every following power of 2 terminators, causing it to get reported less and less the longer the evaluation runs.

cc `@rust-lang/wg-const-eval`

fixes #93481
closes #67217
This commit is contained in:
bors 2023-06-01 05:32:00 +00:00
commit 23f93a1266
41 changed files with 325 additions and 261 deletions

View File

@ -10,6 +10,12 @@ const_eval_interior_mutable_data_refer =
This would make multiple uses of a constant to be able to see different values and allow circumventing
the `Send` and `Sync` requirements for shared mutable data, which is unsound.
const_eval_long_running =
constant evaluation is taking a long time
.note = this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
.label = the const evaluator is currently interpreting this expression
.help = the constant being evaluated
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
const_eval_mut_deref =

View File

@ -103,7 +103,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
tcx,
root_span,
param_env,
CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics, CheckAlignment::No),
CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
)
}
@ -306,7 +306,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(
tcx.const_eval_limit(),
/*can_access_statics:*/ is_static,
if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
CheckAlignment::Error

View File

@ -16,11 +16,11 @@ use std::fmt;
use rustc_ast::Mutability;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::AssertMessage;
use rustc_session::Limit;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Align, Size};
use rustc_target::spec::abi::Abi as CallAbi;
use crate::errors::{LongRunning, LongRunningWarn};
use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
@ -28,13 +28,24 @@ use crate::interpret::{
use super::error::*;
/// When hitting this many interpreted terminators we emit a deny by default lint
/// that notfies the user that their constant takes a long time to evaluate. If that's
/// what they intended, they can just allow the lint.
const LINT_TERMINATOR_LIMIT: usize = 2_000_000;
/// The limit used by `-Z tiny-const-eval-limit`. This smaller limit is useful for internal
/// tests not needing to run 30s or more to show some behaviour.
const TINY_LINT_TERMINATOR_LIMIT: usize = 20;
/// After this many interpreted terminators, we start emitting progress indicators at every
/// power of two of interpreted terminators.
const PROGRESS_INDICATOR_START: usize = 4_000_000;
/// Extra machine state for CTFE, and the Machine instance
pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// For now, the number of terminators that can be evaluated before we throw a resource
/// exhaustion error.
/// The number of terminators that have been evaluated.
///
/// Setting this to `0` disables the limit and allows the interpreter to run forever.
pub(super) steps_remaining: usize,
/// This is used to produce lints informing the user that the compiler is not stuck.
/// Set to `usize::MAX` to never report anything.
pub(super) num_evaluated_steps: usize,
/// The virtual call stack.
pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>,
@ -72,13 +83,9 @@ impl CheckAlignment {
}
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new(
const_eval_limit: Limit,
can_access_statics: bool,
check_alignment: CheckAlignment,
) -> Self {
pub(crate) fn new(can_access_statics: bool, check_alignment: CheckAlignment) -> Self {
CompileTimeInterpreter {
steps_remaining: const_eval_limit.0,
num_evaluated_steps: 0,
stack: Vec::new(),
can_access_statics,
check_alignment,
@ -569,13 +576,54 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
// The step limit has already been hit in a previous call to `increment_const_eval_counter`.
if ecx.machine.steps_remaining == 0 {
return Ok(());
}
ecx.machine.steps_remaining -= 1;
if ecx.machine.steps_remaining == 0 {
throw_exhaust!(StepLimitReached)
if let Some(new_steps) = ecx.machine.num_evaluated_steps.checked_add(1) {
let (limit, start) = if ecx.tcx.sess.opts.unstable_opts.tiny_const_eval_limit {
(TINY_LINT_TERMINATOR_LIMIT, TINY_LINT_TERMINATOR_LIMIT)
} else {
(LINT_TERMINATOR_LIMIT, PROGRESS_INDICATOR_START)
};
ecx.machine.num_evaluated_steps = new_steps;
// By default, we have a *deny* lint kicking in after some time
// to ensure `loop {}` doesn't just go forever.
// In case that lint got reduced, in particular for `--cap-lint` situations, we also
// have a hard warning shown every now and then for really long executions.
if new_steps == limit {
// By default, we stop after a million steps, but the user can disable this lint
// to be able to run until the heat death of the universe or power loss, whichever
// comes first.
let hir_id = ecx.best_lint_scope();
let is_error = ecx
.tcx
.lint_level_at_node(
rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
hir_id,
)
.0
.is_error();
let span = ecx.cur_span();
ecx.tcx.emit_spanned_lint(
rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
hir_id,
span,
LongRunning { item_span: ecx.tcx.span },
);
// If this was a hard error, don't bother continuing evaluation.
if is_error {
let guard = ecx
.tcx
.sess
.delay_span_bug(span, "The deny lint should have already errored");
throw_inval!(AlreadyReported(guard.into()));
}
} else if new_steps > start && new_steps.is_power_of_two() {
// Only report after a certain number of terminators have been evaluated and the
// current number of evaluated terminators is a power of 2. The latter gives us a cheap
// way to implement exponential backoff.
let span = ecx.cur_span();
ecx.tcx.sess.emit_warning(LongRunningWarn { span, item_span: ecx.tcx.span });
}
}
Ok(())

View File

@ -1,5 +1,5 @@
use rustc_hir::ConstContext;
use rustc_macros::Diagnostic;
use rustc_macros::{Diagnostic, LintDiagnostic};
use rustc_span::Span;
#[derive(Diagnostic)]
@ -194,3 +194,21 @@ pub(crate) struct InteriorMutabilityBorrow {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(const_eval_long_running)]
#[note]
pub struct LongRunning {
#[help]
pub item_span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_long_running)]
pub struct LongRunningWarn {
#[primary_span]
#[label]
pub span: Span,
#[help]
pub item_span: Span,
}

View File

@ -4,6 +4,7 @@ use std::mem;
use either::{Either, Left, Right};
use hir::CRATE_HIR_ID;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec;
use rustc_middle::mir;
@ -405,6 +406,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.stack().last().map_or(self.tcx.span, |f| f.current_span())
}
#[inline(always)]
/// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId
pub fn best_lint_scope(&self) -> hir::HirId {
self.stack()
.iter()
.find_map(|frame| frame.body.source.def_id().as_local())
.map_or(CRATE_HIR_ID, |def_id| self.tcx.hir().local_def_id_to_hir_id(def_id))
}
#[inline(always)]
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
M::stack(self)

View File

@ -1,6 +1,5 @@
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_session::Limit;
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
@ -45,11 +44,8 @@ fn might_permit_raw_init_strict<'tcx>(
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
) -> Result<bool, LayoutError<'tcx>> {
let machine = CompileTimeInterpreter::new(
Limit::new(0),
/*can_access_statics:*/ false,
CheckAlignment::Error,
);
let machine =
CompileTimeInterpreter::new(/*can_access_statics:*/ false, CheckAlignment::Error);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);

View File

@ -351,8 +351,6 @@ declare_features! (
(active, const_async_blocks, "1.53.0", Some(85368), None),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003), None),
/// Allows limiting the evaluation steps of const expressions
(active, const_eval_limit, "1.43.0", Some(67217), None),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(active, const_extern_fn, "1.40.0", Some(64926), None),
/// Allows basic arithmetic on floating point types in a `const fn`.

View File

@ -355,10 +355,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
gated!(
const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
const_eval_limit, experimental!(const_eval_limit)
),
gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit)

View File

@ -59,8 +59,10 @@ declare_features! (
/// Allows comparing raw pointers during const eval.
(removed, const_compare_raw_pointers, "1.46.0", Some(53020), None,
Some("cannot be allowed in const eval in any meaningful way")),
/// Allows limiting the evaluation steps of const expressions
(removed, const_eval_limit, "1.43.0", Some(67217), None, Some("removed the limit entirely")),
/// Allows non-trivial generic constants which have to be manually propagated upwards.
(removed, const_evaluatable_checked, "1.48.0", Some(76560), None, Some("renamed to `generic_const_exprs`")),
(removed, const_evaluatable_checked, "1.48.0", Some(76560), None, Some("renamed to `generic_const_exprs`")),
/// Allows the definition of `const` functions with some advanced features.
(removed, const_fn, "1.54.0", Some(57563), None,
Some("split into finer-grained feature gates")),

View File

@ -3357,6 +3357,7 @@ declare_lint_pass! {
LARGE_ASSIGNMENTS,
LATE_BOUND_LIFETIME_ARGUMENTS,
LEGACY_DERIVE_HELPERS,
LONG_RUNNING_CONST_EVAL,
LOSSY_PROVENANCE_CASTS,
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
MACRO_USE_EXTERN_CRATE,
@ -3426,6 +3427,43 @@ declare_lint_pass! {
]
}
declare_lint! {
/// The `long_running_const_eval` lint is emitted when const
/// eval is running for a long time to ensure rustc terminates
/// even if you accidentally wrote an infinite loop.
///
/// ### Example
///
/// ```rust,compile_fail
/// const FOO: () = loop {};
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Loops allow const evaluation to compute arbitrary code, but may also
/// cause infinite loops or just very long running computations.
/// Users can enable long running computations by allowing the lint
/// on individual constants or for entire crates.
///
/// ### Unconditional warnings
///
/// Note that regardless of whether the lint is allowed or set to warn,
/// the compiler will issue warnings if constant evaluation runs significantly
/// longer than this lint's limit. These warnings are also shown to downstream
/// users from crates.io or similar registries. If you are above the lint's limit,
/// both you and downstream users might be exposed to these warnings.
/// They might also appear on compiler updates, as the compiler makes minor changes
/// about how complexity is measured: staying below the limit ensures that there
/// is enough room, and given that the lint is disabled for people who use your
/// dependency it means you will be the only one to get the warning and can put
/// out an update in your own time.
pub LONG_RUNNING_CONST_EVAL,
Deny,
"detects long const eval operations"
}
declare_lint! {
/// The `unused_doc_comments` lint detects doc comments that aren't used
/// by `rustdoc`.

View File

@ -1,8 +1,7 @@
//! Registering limits:
//! * recursion_limit,
//! * move_size_limit,
//! * type_length_limit, and
//! * const_eval_limit
//! * move_size_limit, and
//! * type_length_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
@ -34,12 +33,6 @@ pub fn provide(providers: &mut Providers) {
sym::type_length_limit,
1048576,
),
const_eval_limit: get_limit(
tcx.hir().krate_attrs(),
tcx.sess,
sym::const_eval_limit,
2_000_000,
),
}
}

View File

@ -465,10 +465,6 @@ impl fmt::Display for UnsupportedOpInfo {
pub enum ResourceExhaustionInfo {
/// The stack grew too big.
StackFrameLimitReached,
/// The program ran for too long.
///
/// The exact limit is set by the `const_eval_limit` attribute.
StepLimitReached,
/// There is not enough memory (on the host) to perform an allocation.
MemoryExhausted,
/// The address space (of the target) is full.
@ -482,9 +478,6 @@ impl fmt::Display for ResourceExhaustionInfo {
StackFrameLimitReached => {
write!(f, "reached the configured maximum number of stack frames")
}
StepLimitReached => {
write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
}
MemoryExhausted => {
write!(f, "tried to allocate more memory than available to compiler")
}

View File

@ -82,8 +82,6 @@ use std::iter;
use std::mem;
use std::ops::{Bound, Deref};
const TINY_CONST_EVAL_LIMIT: Limit = Limit(20);
#[allow(rustc::usage_of_ty_tykind)]
impl<'tcx> Interner for TyCtxt<'tcx> {
type AdtDef = ty::AdtDef<'tcx>;
@ -1178,14 +1176,6 @@ impl<'tcx> TyCtxt<'tcx> {
self.limits(()).move_size_limit
}
pub fn const_eval_limit(self) -> Limit {
if self.sess.opts.unstable_opts.tiny_const_eval_limit {
TINY_CONST_EVAL_LIMIT
} else {
self.limits(()).const_eval_limit
}
}
pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
iter::once(LOCAL_CRATE)
.chain(self.crates(()).iter().copied())

View File

@ -128,8 +128,6 @@ pub struct Limits {
pub move_size_limit: Limit,
/// The maximum length of types during monomorphization.
pub type_length_limit: Limit,
/// The maximum blocks a const expression can evaluate.
pub const_eval_limit: Limit,
}
pub struct CompilerIO {

View File

@ -55,7 +55,7 @@ RUN ./build-clang.sh
ENV CC=clang CXX=clang++
# rustc-perf version from 2023-03-15
ENV PERF_COMMIT 9dfaa35193154b690922347ee1141a06ec87a199
ENV PERF_COMMIT 8b2ac3042e1ff2c0074455a0a3618adef97156b1
RUN curl -LS -o perf.zip https://github.com/rust-lang/rustc-perf/archive/$PERF_COMMIT.zip && \
unzip perf.zip && \
mv rustc-perf-$PERF_COMMIT rustc-perf && \

View File

@ -175,7 +175,7 @@ class WindowsPipeline(Pipeline):
def build_rustc_perf(self):
# rustc-perf version from 2023-03-15
perf_commit = "9dfaa35193154b690922347ee1141a06ec87a199"
perf_commit = "8b2ac3042e1ff2c0074455a0a3618adef97156b1"
rustc_perf_zip_path = self.opt_artifacts() / "perf.zip"
def download_rustc_perf():

View File

@ -1,7 +0,0 @@
# `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`.

View File

@ -195,10 +195,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
gated!(
const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
const_eval_limit, experimental!(const_eval_limit)
),
gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit)

View File

@ -4,8 +4,8 @@ fn main() {
let _ = [(); {
let mut n = 113383; // #20 in https://oeis.org/A006884
while n != 0 {
//~^ ERROR evaluation of constant value failed
n = if n % 2 == 0 { n/2 } else { 3*n + 1 };
//~^ ERROR is taking a long time
n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 };
}
n
}];

View File

@ -1,12 +1,27 @@
error[E0080]: evaluation of constant value failed
error: constant evaluation is taking a long time
--> $DIR/infinite_loop.rs:6:9
|
LL | / while n != 0 {
LL | |
LL | | n = if n % 2 == 0 { n/2 } else { 3*n + 1 };
LL | | n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 };
LL | | }
| |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`)
| |_________^
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/infinite_loop.rs:4:18
|
LL | let _ = [(); {
| __________________^
LL | | let mut n = 113383; // #20 in https://oeis.org/A006884
LL | | while n != 0 {
LL | |
... |
LL | | n
LL | | }];
| |_____^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -2,7 +2,8 @@ fn main() {
let _ = [(); {
let mut x = &0;
let mut n = 0;
while n < 5 { //~ ERROR evaluation of constant value failed [E0080]
while n < 5 {
//~^ ERROR: constant evaluation is taking a long time
n = (n + 1) % 5;
x = &0; // Materialize a new AllocId
}

View File

@ -1,12 +1,28 @@
error[E0080]: evaluation of constant value failed
error: constant evaluation is taking a long time
--> $DIR/issue-52475.rs:5:9
|
LL | / while n < 5 {
LL | |
LL | | n = (n + 1) % 5;
LL | | x = &0; // Materialize a new AllocId
LL | | }
| |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`)
| |_________^
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/issue-52475.rs:2:18
|
LL | let _ = [(); {
| __________________^
LL | | let mut x = &0;
LL | | let mut n = 0;
LL | | while n < 5 {
... |
LL | | 0
LL | | }];
| |_____^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -1,3 +1,3 @@
static _X: () = loop {}; //~ ERROR could not evaluate static initializer
static _X: () = loop {}; //~ ERROR taking a long time
fn main() {}

View File

@ -1,9 +1,17 @@
error[E0080]: could not evaluate static initializer
error: constant evaluation is taking a long time
--> $DIR/issue-70723.rs:1:17
|
LL | static _X: () = loop {};
| ^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
| ^^^^^^^
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/issue-70723.rs:1:1
|
LL | static _X: () = loop {};
| ^^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -25,7 +25,7 @@ const fn call_foo() -> u32 {
foo();
foo();
foo();
foo(); //~ ERROR evaluation of constant value failed [E0080]
foo(); //~ ERROR is taking a long time
0
}

View File

@ -1,20 +1,17 @@
error[E0080]: evaluation of constant value failed
--> $DIR/ctfe-fn-call.rs:28:5
|
LL | foo();
| ^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
|
note: inside `call_foo`
error: constant evaluation is taking a long time
--> $DIR/ctfe-fn-call.rs:28:5
|
LL | foo();
| ^^^^^
note: inside `X`
--> $DIR/ctfe-fn-call.rs:32:16
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/ctfe-fn-call.rs:32:1
|
LL | const X: u32 = call_foo();
| ^^^^^^^^^^
| ^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -3,9 +3,10 @@
const fn labelled_loop(n: u32) -> u32 {
let mut i = 0;
'mylabel: loop { //~ ERROR evaluation of constant value failed [E0080]
'mylabel: loop {
//~^ ERROR is taking a long time
if i > n {
break 'mylabel
break 'mylabel;
}
i += 1;
}

View File

@ -1,30 +1,23 @@
error[E0080]: evaluation of constant value failed
error: constant evaluation is taking a long time
--> $DIR/ctfe-labelled-loop.rs:6:5
|
LL | / 'mylabel: loop {
LL | |
LL | | if i > n {
LL | | break 'mylabel
LL | | }
LL | | i += 1;
LL | | }
| |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`)
|
note: inside `labelled_loop`
--> $DIR/ctfe-labelled-loop.rs:6:5
|
LL | / 'mylabel: loop {
LL | | if i > n {
LL | | break 'mylabel
LL | | break 'mylabel;
LL | | }
LL | | i += 1;
LL | | }
| |_____^
note: inside `X`
--> $DIR/ctfe-labelled-loop.rs:15:16
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/ctfe-labelled-loop.rs:16:1
|
LL | const X: u32 = labelled_loop(19);
| ^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -1,11 +1,12 @@
// check-fail
// compile-flags: -Z tiny-const-eval-limit
#[rustfmt::skip]
const fn recurse(n: u32) -> u32 {
if n == 0 {
n
} else {
recurse(n - 1) //~ ERROR evaluation of constant value failed [E0080]
recurse(n - 1) //~ ERROR is taking a long time
}
}

View File

@ -1,25 +1,17 @@
error[E0080]: evaluation of constant value failed
--> $DIR/ctfe-recursion.rs:8:9
|
LL | recurse(n - 1)
| ^^^^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
|
note: inside `recurse`
--> $DIR/ctfe-recursion.rs:8:9
error: constant evaluation is taking a long time
--> $DIR/ctfe-recursion.rs:9:9
|
LL | recurse(n - 1)
| ^^^^^^^^^^^^^^
note: [... 18 additional calls inside `recurse` ...]
--> $DIR/ctfe-recursion.rs:8:9
|
LL | recurse(n - 1)
| ^^^^^^^^^^^^^^
note: inside `X`
--> $DIR/ctfe-recursion.rs:12:16
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/ctfe-recursion.rs:13:1
|
LL | const X: u32 = recurse(19);
| ^^^^^^^^^^^
| ^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -0,0 +1,19 @@
warning: constant evaluation is taking a long time
--> $DIR/ctfe-simple-loop.rs:9:5
|
LL | / while index < n {
LL | |
LL | |
LL | |
LL | | index = index + 1;
LL | | }
| |_____^ the const evaluator is currently interpreting this expression
|
help: the constant being evaluated
--> $DIR/ctfe-simple-loop.rs:19:1
|
LL | const Y: u32 = simple_loop(35);
| ^^^^^^^^^^^^
warning: 1 warning emitted

View File

@ -1,14 +1,22 @@
// check-fail
// check-pass
// revisions: warn allow
#![cfg_attr(warn, warn(long_running_const_eval))]
#![cfg_attr(allow, allow(long_running_const_eval))]
// compile-flags: -Z tiny-const-eval-limit
const fn simple_loop(n: u32) -> u32 {
let mut index = 0;
while index < n { //~ ERROR evaluation of constant value failed [E0080]
while index < n {
//~^ WARN is taking a long time
//[warn]~| WARN is taking a long time
//[warn]~| WARN is taking a long time
index = index + 1;
}
0
}
const X: u32 = simple_loop(19);
const Y: u32 = simple_loop(35);
fn main() {
println!("{X}");

View File

@ -1,24 +0,0 @@
error[E0080]: evaluation of constant value failed
--> $DIR/ctfe-simple-loop.rs:5:5
|
LL | / while index < n {
LL | | index = index + 1;
LL | | }
| |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`)
|
note: inside `simple_loop`
--> $DIR/ctfe-simple-loop.rs:5:5
|
LL | / while index < n {
LL | | index = index + 1;
LL | | }
| |_____^
note: inside `X`
--> $DIR/ctfe-simple-loop.rs:11:16
|
LL | const X: u32 = simple_loop(19);
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -0,0 +1,62 @@
warning: constant evaluation is taking a long time
--> $DIR/ctfe-simple-loop.rs:9:5
|
LL | / while index < n {
LL | |
LL | |
LL | |
LL | | index = index + 1;
LL | | }
| |_____^
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/ctfe-simple-loop.rs:18:1
|
LL | const X: u32 = simple_loop(19);
| ^^^^^^^^^^^^
note: the lint level is defined here
--> $DIR/ctfe-simple-loop.rs:3:24
|
LL | #![cfg_attr(warn, warn(long_running_const_eval))]
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: constant evaluation is taking a long time
--> $DIR/ctfe-simple-loop.rs:9:5
|
LL | / while index < n {
LL | |
LL | |
LL | |
LL | | index = index + 1;
LL | | }
| |_____^
|
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint.
help: the constant being evaluated
--> $DIR/ctfe-simple-loop.rs:19:1
|
LL | const Y: u32 = simple_loop(35);
| ^^^^^^^^^^^^
warning: constant evaluation is taking a long time
--> $DIR/ctfe-simple-loop.rs:9:5
|
LL | / while index < n {
LL | |
LL | |
LL | |
LL | | index = index + 1;
LL | | }
| |_____^ the const evaluator is currently interpreting this expression
|
help: the constant being evaluated
--> $DIR/ctfe-simple-loop.rs:19:1
|
LL | const Y: u32 = simple_loop(35);
| ^^^^^^^^^^^^
warning: 3 warnings emitted

View File

@ -1,20 +0,0 @@
// check-pass
#![feature(const_eval_limit)]
// This needs to be higher than the number of loop iterations since each pass through the loop may
// hit more than one terminator.
#![const_eval_limit="4000"]
const X: usize = {
let mut x = 0;
while x != 1000 {
x += 1;
}
x
};
fn main() {
assert_eq!(X, 1000);
}

View File

@ -1,15 +0,0 @@
#![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
}

View File

@ -1,10 +0,0 @@
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

View File

@ -1,16 +0,0 @@
#![feature(const_eval_limit)]
#![const_eval_limit = "500"]
const X: usize = {
let mut x = 0;
while x != 1000 {
//~^ ERROR evaluation of constant value failed
x += 1;
}
x
};
fn main() {
assert_eq!(X, 1000);
}

View File

@ -1,12 +0,0 @@
error[E0080]: evaluation of constant value failed
--> $DIR/const_eval_limit_reached.rs:6:5
|
LL | / while x != 1000 {
LL | |
LL | | x += 1;
LL | | }
| |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`)
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -1,14 +0,0 @@
#![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
}

View File

@ -1,12 +0,0 @@
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`.