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 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. 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_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
const_eval_mut_deref = const_eval_mut_deref =

View File

@ -103,7 +103,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
tcx, tcx,
root_span, root_span,
param_env, 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 // 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. // they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new( CompileTimeInterpreter::new(
tcx.const_eval_limit(),
/*can_access_statics:*/ is_static, /*can_access_statics:*/ is_static,
if tcx.sess.opts.unstable_opts.extra_const_ub_checks { if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
CheckAlignment::Error CheckAlignment::Error

View File

@ -16,11 +16,11 @@ use std::fmt;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_middle::mir::AssertMessage; use rustc_middle::mir::AssertMessage;
use rustc_session::Limit;
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
use rustc_target::spec::abi::Abi as CallAbi; use rustc_target::spec::abi::Abi as CallAbi;
use crate::errors::{LongRunning, LongRunningWarn};
use crate::interpret::{ use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx, self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
InterpResult, OpTy, PlaceTy, Pointer, Scalar, InterpResult, OpTy, PlaceTy, Pointer, Scalar,
@ -28,13 +28,24 @@ use crate::interpret::{
use super::error::*; 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 /// Extra machine state for CTFE, and the Machine instance
pub struct CompileTimeInterpreter<'mir, 'tcx> { pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// For now, the number of terminators that can be evaluated before we throw a resource /// The number of terminators that have been evaluated.
/// exhaustion error.
/// ///
/// Setting this to `0` disables the limit and allows the interpreter to run forever. /// This is used to produce lints informing the user that the compiler is not stuck.
pub(super) steps_remaining: usize, /// Set to `usize::MAX` to never report anything.
pub(super) num_evaluated_steps: usize,
/// The virtual call stack. /// The virtual call stack.
pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>, pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>,
@ -72,13 +83,9 @@ impl CheckAlignment {
} }
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new( pub(crate) fn new(can_access_statics: bool, check_alignment: CheckAlignment) -> Self {
const_eval_limit: Limit,
can_access_statics: bool,
check_alignment: CheckAlignment,
) -> Self {
CompileTimeInterpreter { CompileTimeInterpreter {
steps_remaining: const_eval_limit.0, num_evaluated_steps: 0,
stack: Vec::new(), stack: Vec::new(),
can_access_statics, can_access_statics,
check_alignment, 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> { 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`. // 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 let Some(new_steps) = ecx.machine.num_evaluated_steps.checked_add(1) {
if ecx.machine.steps_remaining == 0 { let (limit, start) = if ecx.tcx.sess.opts.unstable_opts.tiny_const_eval_limit {
throw_exhaust!(StepLimitReached) (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(()) Ok(())

View File

@ -1,5 +1,5 @@
use rustc_hir::ConstContext; use rustc_hir::ConstContext;
use rustc_macros::Diagnostic; use rustc_macros::{Diagnostic, LintDiagnostic};
use rustc_span::Span; use rustc_span::Span;
#[derive(Diagnostic)] #[derive(Diagnostic)]
@ -194,3 +194,21 @@ pub(crate) struct InteriorMutabilityBorrow {
#[primary_span] #[primary_span]
pub span: 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 either::{Either, Left, Right};
use hir::CRATE_HIR_ID;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::mir; 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()) 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)] #[inline(always)]
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] { pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
M::stack(self) M::stack(self)

View File

@ -1,6 +1,5 @@
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_session::Limit;
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
use crate::const_eval::{CheckAlignment, CompileTimeInterpreter}; use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
@ -45,11 +44,8 @@ fn might_permit_raw_init_strict<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
kind: ValidityRequirement, kind: ValidityRequirement,
) -> Result<bool, LayoutError<'tcx>> { ) -> Result<bool, LayoutError<'tcx>> {
let machine = CompileTimeInterpreter::new( let machine =
Limit::new(0), CompileTimeInterpreter::new(/*can_access_statics:*/ false, CheckAlignment::Error);
/*can_access_statics:*/ false,
CheckAlignment::Error,
);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); 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), (active, const_async_blocks, "1.53.0", Some(85368), None),
/// Allows `const || {}` closures in const contexts. /// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003), None), (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`. /// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(active, const_extern_fn, "1.40.0", Some(64926), None), (active, const_extern_fn, "1.40.0", Some(64926), None),
/// Allows basic arithmetic on floating point types in a `const fn`. /// Allows basic arithmetic on floating point types in a `const fn`.

View File

@ -355,10 +355,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Limits: // Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_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!( gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit) large_assignments, experimental!(move_size_limit)

View File

@ -59,8 +59,10 @@ declare_features! (
/// Allows comparing raw pointers during const eval. /// Allows comparing raw pointers during const eval.
(removed, const_compare_raw_pointers, "1.46.0", Some(53020), None, (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None,
Some("cannot be allowed in const eval in any meaningful way")), 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. /// 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. /// Allows the definition of `const` functions with some advanced features.
(removed, const_fn, "1.54.0", Some(57563), None, (removed, const_fn, "1.54.0", Some(57563), None,
Some("split into finer-grained feature gates")), Some("split into finer-grained feature gates")),

View File

@ -3357,6 +3357,7 @@ declare_lint_pass! {
LARGE_ASSIGNMENTS, LARGE_ASSIGNMENTS,
LATE_BOUND_LIFETIME_ARGUMENTS, LATE_BOUND_LIFETIME_ARGUMENTS,
LEGACY_DERIVE_HELPERS, LEGACY_DERIVE_HELPERS,
LONG_RUNNING_CONST_EVAL,
LOSSY_PROVENANCE_CASTS, LOSSY_PROVENANCE_CASTS,
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
MACRO_USE_EXTERN_CRATE, 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! { declare_lint! {
/// The `unused_doc_comments` lint detects doc comments that aren't used /// The `unused_doc_comments` lint detects doc comments that aren't used
/// by `rustdoc`. /// by `rustdoc`.

View File

@ -1,8 +1,7 @@
//! Registering limits: //! Registering limits:
//! * recursion_limit, //! * recursion_limit,
//! * move_size_limit, //! * move_size_limit, and
//! * type_length_limit, and //! * type_length_limit
//! * const_eval_limit
//! //!
//! There are various parts of the compiler that must impose arbitrary limits //! There are various parts of the compiler that must impose arbitrary limits
//! on how deeply they recurse to prevent stack overflow. Users can override //! 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, sym::type_length_limit,
1048576, 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 { pub enum ResourceExhaustionInfo {
/// The stack grew too big. /// The stack grew too big.
StackFrameLimitReached, 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. /// There is not enough memory (on the host) to perform an allocation.
MemoryExhausted, MemoryExhausted,
/// The address space (of the target) is full. /// The address space (of the target) is full.
@ -482,9 +478,6 @@ impl fmt::Display for ResourceExhaustionInfo {
StackFrameLimitReached => { StackFrameLimitReached => {
write!(f, "reached the configured maximum number of stack frames") write!(f, "reached the configured maximum number of stack frames")
} }
StepLimitReached => {
write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
}
MemoryExhausted => { MemoryExhausted => {
write!(f, "tried to allocate more memory than available to compiler") 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::mem;
use std::ops::{Bound, Deref}; use std::ops::{Bound, Deref};
const TINY_CONST_EVAL_LIMIT: Limit = Limit(20);
#[allow(rustc::usage_of_ty_tykind)] #[allow(rustc::usage_of_ty_tykind)]
impl<'tcx> Interner for TyCtxt<'tcx> { impl<'tcx> Interner for TyCtxt<'tcx> {
type AdtDef = ty::AdtDef<'tcx>; type AdtDef = ty::AdtDef<'tcx>;
@ -1178,14 +1176,6 @@ impl<'tcx> TyCtxt<'tcx> {
self.limits(()).move_size_limit 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 { pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
iter::once(LOCAL_CRATE) iter::once(LOCAL_CRATE)
.chain(self.crates(()).iter().copied()) .chain(self.crates(()).iter().copied())

View File

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

View File

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

View File

@ -175,7 +175,7 @@ class WindowsPipeline(Pipeline):
def build_rustc_perf(self): def build_rustc_perf(self):
# rustc-perf version from 2023-03-15 # rustc-perf version from 2023-03-15
perf_commit = "9dfaa35193154b690922347ee1141a06ec87a199" perf_commit = "8b2ac3042e1ff2c0074455a0a3618adef97156b1"
rustc_perf_zip_path = self.opt_artifacts() / "perf.zip" rustc_perf_zip_path = self.opt_artifacts() / "perf.zip"
def download_rustc_perf(): 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: // Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_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!( gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit) large_assignments, experimental!(move_size_limit)

View File

@ -4,8 +4,8 @@ fn main() {
let _ = [(); { let _ = [(); {
let mut n = 113383; // #20 in https://oeis.org/A006884 let mut n = 113383; // #20 in https://oeis.org/A006884
while n != 0 { while n != 0 {
//~^ ERROR evaluation of constant value failed //~^ ERROR is taking a long time
n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 };
} }
n 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 --> $DIR/infinite_loop.rs:6:9
| |
LL | / while n != 0 { LL | / while n != 0 {
LL | | 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 | | } 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 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 _ = [(); {
let mut x = &0; let mut x = &0;
let mut n = 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; n = (n + 1) % 5;
x = &0; // Materialize a new AllocId 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 --> $DIR/issue-52475.rs:5:9
| |
LL | / while n < 5 { LL | / while n < 5 {
LL | |
LL | | n = (n + 1) % 5; LL | | n = (n + 1) % 5;
LL | | x = &0; // Materialize a new AllocId LL | | x = &0; // Materialize a new AllocId
LL | | } 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 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() {} 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 --> $DIR/issue-70723.rs:1:17
| |
LL | static _X: () = loop {}; 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 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();
foo(); foo();
foo(); //~ ERROR evaluation of constant value failed [E0080] foo(); //~ ERROR is taking a long time
0 0
} }

View File

@ -1,20 +1,17 @@
error[E0080]: evaluation of constant value failed error: constant evaluation is taking a long time
--> $DIR/ctfe-fn-call.rs:28:5
|
LL | foo();
| ^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
|
note: inside `call_foo`
--> $DIR/ctfe-fn-call.rs:28:5 --> $DIR/ctfe-fn-call.rs:28:5
| |
LL | foo(); 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(); LL | const X: u32 = call_foo();
| ^^^^^^^^^^ | ^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error 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 { const fn labelled_loop(n: u32) -> u32 {
let mut i = 0; let mut i = 0;
'mylabel: loop { //~ ERROR evaluation of constant value failed [E0080] 'mylabel: loop {
//~^ ERROR is taking a long time
if i > n { if i > n {
break 'mylabel break 'mylabel;
} }
i += 1; 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 --> $DIR/ctfe-labelled-loop.rs:6:5
| |
LL | / 'mylabel: loop { LL | / 'mylabel: loop {
LL | |
LL | | if i > n { LL | | if i > n {
LL | | break 'mylabel 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 | | } LL | | }
LL | | i += 1; LL | | i += 1;
LL | | } 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); LL | const X: u32 = labelled_loop(19);
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -1,11 +1,12 @@
// check-fail // check-fail
// compile-flags: -Z tiny-const-eval-limit // compile-flags: -Z tiny-const-eval-limit
#[rustfmt::skip]
const fn recurse(n: u32) -> u32 { const fn recurse(n: u32) -> u32 {
if n == 0 { if n == 0 {
n n
} else { } 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 error: constant evaluation is taking a long time
--> $DIR/ctfe-recursion.rs:8:9 --> $DIR/ctfe-recursion.rs:9:9
|
LL | recurse(n - 1)
| ^^^^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
|
note: inside `recurse`
--> $DIR/ctfe-recursion.rs:8:9
| |
LL | recurse(n - 1) LL | recurse(n - 1)
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
note: [... 18 additional calls inside `recurse` ...]
--> $DIR/ctfe-recursion.rs:8:9
| |
LL | recurse(n - 1) = 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.
note: inside `X` help: the constant being evaluated
--> $DIR/ctfe-recursion.rs:12:16 --> $DIR/ctfe-recursion.rs:13:1
| |
LL | const X: u32 = recurse(19); LL | const X: u32 = recurse(19);
| ^^^^^^^^^^^ | ^^^^^^^^^^^^
= note: `#[deny(long_running_const_eval)]` on by default
error: aborting due to previous error 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 // compile-flags: -Z tiny-const-eval-limit
const fn simple_loop(n: u32) -> u32 { const fn simple_loop(n: u32) -> u32 {
let mut index = 0; 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; index = index + 1;
} }
0 0
} }
const X: u32 = simple_loop(19); const X: u32 = simple_loop(19);
const Y: u32 = simple_loop(35);
fn main() { fn main() {
println!("{X}"); 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`.