diff options
| author | bors <bors@rust-lang.org> | 2023-01-29 04:11:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-01-29 04:11:27 +0000 |
| commit | 3cdd0197e78e2ca3403157617709bbfe18a3f0b5 (patch) | |
| tree | f1408419d2d0288c966e40cbb9e455cb5da51603 | |
| parent | bcb064a7f4aee705c97ce1b74beb9af0f9b5adb2 (diff) | |
| parent | bdb815a22ab00450dcc010a99309c24c475432a6 (diff) | |
| download | rust-3cdd0197e78e2ca3403157617709bbfe18a3f0b5.tar.gz rust-3cdd0197e78e2ca3403157617709bbfe18a3f0b5.zip | |
Auto merge of #106227 - bryangarza:ctfe-limit, r=oli-obk
Use stable metric for const eval limit instead of current terminator-based logic This patch adds a `MirPass` that inserts a new MIR instruction `ConstEvalCounter` to any loops and function calls in the CFG. This instruction is used during Const Eval to count against the `const_eval_limit`, and emit the `StepLimitReached` error, replacing the current logic which uses Terminators only. The new method of counting loops and function calls should be more stable across compiler versions (i.e., not cause crates that compiled successfully before, to no longer compile when changes to the MIR generation/optimization are made). Also see: #103877
50 files changed, 400 insertions, 20 deletions
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 8c4885770ad..2821677c537 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -393,6 +393,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { | mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Coverage(..) | mir::StatementKind::Intrinsic(..) + | mir::StatementKind::ConstEvalCounter | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 6fd9290058c..6217676d5c1 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -91,7 +91,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { LocalMutationIsAllowed::Yes, ); } - StatementKind::Nop + StatementKind::ConstEvalCounter + | StatementKind::Nop | StatementKind::Retag { .. } | StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 73ea7314b75..bc81abe4005 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -609,7 +609,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx StatementKind::AscribeUserType(..) // Doesn't have any language semantics | StatementKind::Coverage(..) - // Does not actually affect borrowck + // These do not actually affect borrowck + | StatementKind::ConstEvalCounter | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 81bd4c2a783..06087b0c579 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1258,6 +1258,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | StatementKind::StorageDead(..) | StatementKind::Retag { .. } | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index d3a8c10657e..dffb2ed8f4f 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -794,6 +794,7 @@ fn codegen_stmt<'tcx>( StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Deinit(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::FakeRead(..) | StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 51450897bfc..49c4f1aaaef 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -530,6 +530,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 19452c8cdc8..60fbceb344d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -91,6 +91,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) + | mir::StatementKind::ConstEvalCounter | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4709514c82e..a5bc121485d 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -561,8 +561,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); } - fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - // The step limit has already been hit in a previous call to `before_terminator`. + 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(()); } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 248953de867..76ed7b80f8d 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -244,12 +244,18 @@ pub trait Machine<'mir, 'tcx>: Sized { } /// Called before a basic block terminator is executed. - /// You can use this to detect endlessly running programs. #[inline] fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { Ok(()) } + /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction. + /// You can use this to detect long or endlessly running programs. + #[inline] + fn increment_const_eval_counter(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + /// Called before a global allocation is accessed. /// `def_id` is `Some` if this is the "lazy" allocation of a static. #[inline] diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index fad4cb06cd6..d101937fd74 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -129,6 +129,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // FIXME(#73156): Handle source code coverage in const eval Coverage(..) => {} + ConstEvalCounter => { + M::increment_const_eval_counter(self)?; + } + // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index cc40c2566d2..f47e3e86ebe 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -693,6 +693,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index fab92f6f6f3..a2f2457487a 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -761,6 +761,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 6398a501983..0a21a4249c8 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -135,7 +135,47 @@ pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> { // This loop computes the semi[w] for w. semi[w] = w; for v in graph.predecessors(pre_order_to_real[w]) { - // Reachable vertices may have unreachable predecessors, so ignore any of them + // TL;DR: Reachable vertices may have unreachable predecessors, so ignore any of them. + // + // Ignore blocks which are not connected to the entry block. + // + // The algorithm that was used to traverse the graph and build the + // `pre_order_to_real` and `real_to_pre_order` vectors does so by + // starting from the entry block and following the successors. + // Therefore, any blocks not reachable from the entry block will be + // set to `None` in the `pre_order_to_real` vector. + // + // For example, in this graph, A and B should be skipped: + // + // ┌─────┐ + // │ │ + // └──┬──┘ + // │ + // ┌──▼──┐ ┌─────┐ + // │ │ │ A │ + // └──┬──┘ └──┬──┘ + // │ │ + // ┌───────┴───────┐ │ + // │ │ │ + // ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ + // │ │ │ │ │ B │ + // └──┬──┘ └──┬──┘ └──┬──┘ + // │ └──────┬─────┘ + // ┌──▼──┐ │ + // │ │ │ + // └──┬──┘ ┌──▼──┐ + // │ │ │ + // │ └─────┘ + // ┌──▼──┐ + // │ │ + // └──┬──┘ + // │ + // ┌──▼──┐ + // │ │ + // └─────┘ + // + // ...this may be the case if a MirPass modifies the CFG to remove + // or rearrange certain blocks/edges. let Some(v) = real_to_pre_order[v] else { continue }; @@ -264,13 +304,18 @@ fn compress( } } +/// Tracks the list of dominators for each node. #[derive(Clone, Debug)] pub struct Dominators<N: Idx> { post_order_rank: IndexVec<N, usize>, + // Even though we track only the immediate dominator of each node, it's + // possible to get its full list of dominators by looking up the dominator + // of each dominator. (See the `impl Iterator for Iter` definition). immediate_dominators: IndexVec<N, Option<N>>, } impl<Node: Idx> Dominators<Node> { + /// Whether the given Node has an immediate dominator. pub fn is_reachable(&self, node: Node) -> bool { self.immediate_dominators[node].is_some() } @@ -280,6 +325,8 @@ impl<Node: Idx> Dominators<Node> { self.immediate_dominators[node].unwrap() } + /// Provides an iterator over each dominator up the CFG, for the given Node. + /// See the `impl Iterator for Iter` definition to understand how this works. pub fn dominators(&self, node: Node) -> Iter<'_, Node> { assert!(self.is_reachable(node), "node {node:?} is not reachable"); Iter { dominators: self, node: Some(node) } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index f94bc4d4c66..52a4e0e7418 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -802,6 +802,7 @@ fn test_unstable_options_tracking_hash() { tracked!(teach, true); tracked!(thinlto, Some(true)); tracked!(thir_unsafeck, true); + tracked!(tiny_const_eval_limit, true); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(trait_solver, TraitSolver::Chalk); tracked!(translate_remapped_path_to_local_path, false); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index bc3c38fdb1c..552af589bec 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1463,6 +1463,7 @@ impl Debug for Statement<'_> { } Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind), Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"), + ConstEvalCounter => write!(fmt, "ConstEvalCounter"), Nop => write!(fmt, "nop"), } } diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index fb1e3d233a2..1610ae1ce14 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -250,6 +250,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { AscribeUserType(..) => "AscribeUserType", Coverage(..) => "Coverage", Intrinsic(..) => "Intrinsic", + ConstEvalCounter => "ConstEvalCounter", Nop => "Nop", } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 52c2b10cbbe..549bc65d6d7 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -355,6 +355,12 @@ pub enum StatementKind<'tcx> { /// This avoids adding a new block and a terminator for simple intrinsics. Intrinsic(Box<NonDivergingIntrinsic<'tcx>>), + /// Instructs the const eval interpreter to increment a counter; this counter is used to track + /// how many steps the interpreter has taken. It is used to prevent the user from writing const + /// code that runs for too long or infinitely. Other than in the const eval interpreter, this + /// is a no-op. + ConstEvalCounter, + /// No-op. Useful for deleting instructions without affecting statement indices. Nop, } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 1a264d2d5af..3ddac5e11fb 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -427,6 +427,7 @@ macro_rules! make_mir_visitor { } } } + StatementKind::ConstEvalCounter => {} StatementKind::Nop => {} } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 526df090a3c..b63b9e754cf 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -77,6 +77,8 @@ use std::iter; use std::mem; use std::ops::{Bound, Deref}; +const TINY_CONST_EVAL_LIMIT: Limit = Limit(20); + pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self @@ -1104,7 +1106,11 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn const_eval_limit(self) -> Limit { - self.limits(()).const_eval_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 { diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 923dc16c11b..2890fa32cc9 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -271,6 +271,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => None, }; if let Some(destination) = destination { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 8d379b90a86..fcf0ce9d821 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -141,6 +141,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::Retag(..) | StatementKind::Intrinsic(..) diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index f46fd118bde..0195693a7cb 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -331,6 +331,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 0522c657939..6bdbda909d7 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -84,7 +84,8 @@ pub trait ValueAnalysis<'tcx> { StatementKind::Retag(..) => { // We don't track references. } - StatementKind::Nop + StatementKind::ConstEvalCounter + | StatementKind::Nop | StatementKind::FakeRead(..) | StatementKind::Coverage(..) | StatementKind::AscribeUserType(..) => (), diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 9f006a76162..8afa53313fc 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -104,6 +104,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => { // safe (at least as emitted during MIR construction) } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 31d5541a31b..f973c1ed28f 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -802,6 +802,8 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> | StatementKind::StorageDead(_) // Coverage should not be encountered, but don't inject coverage coverage | StatementKind::Coverage(_) + // Ignore `ConstEvalCounter`s + | StatementKind::ConstEvalCounter // Ignore `Nop`s | StatementKind::Nop => None, diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs new file mode 100644 index 00000000000..7d127032179 --- /dev/null +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -0,0 +1,59 @@ +//! A pass that inserts the `ConstEvalCounter` instruction into any blocks that have a back edge +//! (thus indicating there is a loop in the CFG), or whose terminator is a function call. +use crate::MirPass; + +use rustc_data_structures::graph::dominators::Dominators; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; + +pub struct CtfeLimit; + +impl<'tcx> MirPass<'tcx> for CtfeLimit { + #[instrument(skip(self, _tcx, body))] + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let doms = body.basic_blocks.dominators(); + let indices: Vec<BasicBlock> = body + .basic_blocks + .iter_enumerated() + .filter_map(|(node, node_data)| { + if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) + // Back edges in a CFG indicate loops + || has_back_edge(&doms, node, &node_data) + { + Some(node) + } else { + None + } + }) + .collect(); + for index in indices { + insert_counter( + body.basic_blocks_mut() + .get_mut(index) + .expect("basic_blocks index {index} should exist"), + ); + } + } +} + +fn has_back_edge( + doms: &Dominators<BasicBlock>, + node: BasicBlock, + node_data: &BasicBlockData<'_>, +) -> bool { + if !doms.is_reachable(node) { + return false; + } + // Check if any of the dominators of the node are also the node's successor. + doms.dominators(node) + .any(|dom| node_data.terminator().successors().into_iter().any(|succ| succ == dom)) +} + +fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { + basic_block_data.statements.push(Statement { + source_info: basic_block_data.terminator().source_info, + kind: StatementKind::ConstEvalCounter, + }); +} diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 09546330cec..9dbfb089dc6 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -53,6 +53,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS | StatementKind::StorageDead(_) | StatementKind::Coverage(_) | StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => (), StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 08e296a8371..20ffb0ab334 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -577,6 +577,7 @@ impl WriteInfo { self.add_place(**place); } StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop | StatementKind::Coverage(_) | StatementKind::StorageLive(_) diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index a9fd95f4541..5624e312da1 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1657,6 +1657,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::Intrinsic(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index fcb09fa02dd..6858fa8e524 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -55,6 +55,7 @@ mod const_goto; mod const_prop; mod const_prop_lint; mod coverage; +mod ctfe_limit; mod dataflow_const_prop; mod dead_store_elimination; mod deaggregator; @@ -410,6 +411,8 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) - } } + pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None); + debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE"); body diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index f1bbf2ea7e8..e3a03aa08af 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -35,6 +35,7 @@ impl RemoveNoopLandingPads { | StatementKind::StorageDead(_) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => { // These are all noops in a landing pad } diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index 2f116aaa958..a24d2d34d79 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -250,6 +250,7 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData< | StatementKind::Coverage(_) | StatementKind::StorageDead(_) | StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} } } @@ -318,6 +319,7 @@ fn find_determining_place<'tcx>( | StatementKind::AscribeUserType(_, _) | StatementKind::Coverage(_) | StatementKind::Intrinsic(_) + | StatementKind::ConstEvalCounter | StatementKind::Nop => {} // If the discriminant is set, it is always set diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8f6abe7a912..7b6fa2baf2f 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -517,7 +517,7 @@ impl<'tcx> Visitor<'tcx> for UsedLocals { self.super_statement(statement, location); } - StatementKind::Nop => {} + StatementKind::ConstEvalCounter | StatementKind::Nop => {} StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 66b100c103e..0db4d85ff4b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1618,6 +1618,8 @@ options! { "measure time of each LLVM pass (default: no)"), time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + tiny_const_eval_limit: bool = (false, parse_bool, [TRACKED], + "sets a tiny, non-configurable limit for const eval; useful for compiler tests"), #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), diff --git a/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md b/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md new file mode 100644 index 00000000000..51c5fd69c63 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/tiny-const-eval-limit.md @@ -0,0 +1,6 @@ +# `tiny-const-eval-limit` + +-------------------- + +The `-Ztiny-const-eval-limit` compiler flag sets a tiny, non-configurable limit for const eval. +This flag should only be used by const eval tests in the rustc test suite. diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 13de780b710..72705878075 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -240,6 +240,7 @@ fn check_statement<'tcx>( | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter | StatementKind::Nop => Ok(()), } } diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 546df947c0f..4f07fca82d1 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -173,6 +173,7 @@ -Z threads=val -- use a thread pool with N threads -Z time-llvm-passes=val -- measure time of each LLVM pass (default: no) -Z time-passes=val -- measure time of each rustc pass (default: no) + -Z tiny-const-eval-limit=val -- sets a tiny, non-configurable limit for const eval; useful for compiler tests -Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) -Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no) -Z track-diagnostics=val -- tracks where in rustc a diagnostic was emitted diff --git a/tests/ui/consts/const-eval/infinite_loop.stderr b/tests/ui/consts/const-eval/infinite_loop.stderr index 8b58cb279f3..f30bfaf3f95 100644 --- a/tests/ui/consts/const-eval/infinite_loop.stderr +++ b/tests/ui/consts/const-eval/infinite_loop.stderr @@ -1,8 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/infinite_loop.rs:6:15 + --> $DIR/infinite_loop.rs:6:9 | -LL | while n != 0 { - | ^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | / while n != 0 { +LL | | +LL | | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; +LL | | } + | |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to previous error diff --git a/tests/ui/consts/const-eval/issue-52475.rs b/tests/ui/consts/const-eval/issue-52475.rs index ce65407bbab..307c1a66834 100644 --- a/tests/ui/consts/const-eval/issue-52475.rs +++ b/tests/ui/consts/const-eval/issue-52475.rs @@ -2,8 +2,8 @@ fn main() { let _ = [(); { let mut x = &0; let mut n = 0; - while n < 5 { - n = (n + 1) % 5; //~ ERROR evaluation of constant value failed + while n < 5 { //~ ERROR evaluation of constant value failed [E0080] + n = (n + 1) % 5; x = &0; // Materialize a new AllocId } 0 diff --git a/tests/ui/consts/const-eval/issue-52475.stderr b/tests/ui/consts/const-eval/issue-52475.stderr index 8536ff02c6d..3aa6bd277dd 100644 --- a/tests/ui/consts/const-eval/issue-52475.stderr +++ b/tests/ui/consts/const-eval/issue-52475.stderr @@ -1,8 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-52475.rs:6:17 + --> $DIR/issue-52475.rs:5:9 | -LL | n = (n + 1) % 5; - | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | / while n < 5 { +LL | | n = (n + 1) % 5; +LL | | x = &0; // Materialize a new AllocId +LL | | } + | |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to previous error diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs new file mode 100644 index 00000000000..c59596238e1 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.rs @@ -0,0 +1,36 @@ +// check-fail +// compile-flags: -Z tiny-const-eval-limit + +const fn foo() {} + +const fn call_foo() -> u32 { + foo(); + foo(); + foo(); + foo(); + foo(); + + foo(); + foo(); + foo(); + foo(); + foo(); + + foo(); + foo(); + foo(); + foo(); + foo(); + + foo(); + foo(); + foo(); + foo(); //~ ERROR evaluation of constant value failed [E0080] + 0 +} + +const X: u32 = call_foo(); + +fn main() { + println!("{X}"); +} diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr new file mode 100644 index 00000000000..ed70975af34 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-fn-call.stderr @@ -0,0 +1,20 @@ +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` + --> $DIR/ctfe-fn-call.rs:28:5 + | +LL | foo(); + | ^^^^^ +note: inside `X` + --> $DIR/ctfe-fn-call.rs:32:16 + | +LL | const X: u32 = call_foo(); + | ^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs new file mode 100644 index 00000000000..c10b8d83791 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.rs @@ -0,0 +1,19 @@ +// check-fail +// compile-flags: -Z tiny-const-eval-limit + +const fn labelled_loop(n: u32) -> u32 { + let mut i = 0; + 'mylabel: loop { //~ ERROR evaluation of constant value failed [E0080] + if i > n { + break 'mylabel + } + i += 1; + } + 0 +} + +const X: u32 = labelled_loop(19); + +fn main() { + println!("{X}"); +} diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr new file mode 100644 index 00000000000..d9404edd5b1 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-labelled-loop.stderr @@ -0,0 +1,30 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-labelled-loop.rs:6:5 + | +LL | / 'mylabel: loop { +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 | | } +LL | | i += 1; +LL | | } + | |_____^ +note: inside `X` + --> $DIR/ctfe-labelled-loop.rs:15:16 + | +LL | const X: u32 = labelled_loop(19); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.rs new file mode 100644 index 00000000000..80ff835f3e8 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.rs @@ -0,0 +1,16 @@ +// check-fail +// compile-flags: -Z tiny-const-eval-limit + +const fn recurse(n: u32) -> u32 { + if n == 0 { + n + } else { + recurse(n - 1) //~ ERROR evaluation of constant value failed [E0080] + } +} + +const X: u32 = recurse(19); + +fn main() { + println!("{X}"); +} diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr new file mode 100644 index 00000000000..ed9a3111942 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-recursion.stderr @@ -0,0 +1,25 @@ +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 + | +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 + | +LL | const X: u32 = recurse(19); + | ^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs b/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs new file mode 100644 index 00000000000..ca0eec93c5d --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.rs @@ -0,0 +1,15 @@ +// check-fail +// 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] + index = index + 1; + } + 0 +} + +const X: u32 = simple_loop(19); + +fn main() { + println!("{X}"); +} diff --git a/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr b/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr new file mode 100644 index 00000000000..83ff275de70 --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/ctfe-simple-loop.stderr @@ -0,0 +1,24 @@ +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`. diff --git a/tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs b/tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs new file mode 100644 index 00000000000..0b0f361809f --- /dev/null +++ b/tests/ui/consts/const-eval/stable-metric/dominators-edge-case.rs @@ -0,0 +1,19 @@ +// check-pass +// +// Exercising an edge case which was found during Stage 2 compilation. +// Compilation would fail for this code when running the `CtfeLimit` +// MirPass (specifically when looking up the dominators). +#![crate_type="lib"] + +const DUMMY: Expr = Expr::Path(ExprPath { + attrs: Vec::new(), + path: Vec::new(), +}); + +pub enum Expr { + Path(ExprPath), +} +pub struct ExprPath { + pub attrs: Vec<()>, + pub path: Vec<()>, +} diff --git a/tests/ui/consts/const_limit/const_eval_limit_reached.stderr b/tests/ui/consts/const_limit/const_eval_limit_reached.stderr index 850aebdfb2a..a8e8ae9bb08 100644 --- a/tests/ui/consts/const_limit/const_eval_limit_reached.stderr +++ b/tests/ui/consts/const_limit/const_eval_limit_reached.stderr @@ -1,8 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const_eval_limit_reached.rs:6:11 + --> $DIR/const_eval_limit_reached.rs:6:5 | -LL | while x != 1000 { - | ^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | / while x != 1000 { +LL | | +LL | | x += 1; +LL | | } + | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to previous error |
