From 3ebe058fad91921a3b10e618d35f1d20e640834c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Oct 2025 13:37:44 +1100 Subject: [PATCH 01/11] Make `ConstAnalysis::ecx` a `RefCell`. Of the many dataflow analyses, `ConstAnalysis` is the only one that requires the analysis be mutabile when used with `ResultsVisitor`. It's needed because of the `ecx` field -- `ecx.intern_with_temp_alloc` is called during visiting and it takes `&mut self`. This commit changes `ConstAnalysis` to use interior mutability for the `ecx` field. This is a bit annoying for `ConstAnalysis`, but it will allow more immutability in `ResultsVisitor`, as seen in the next commit. --- .../src/dataflow_const_prop.rs | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 8bcda77f4bc32..a79b341ad1a7a 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -3,6 +3,7 @@ //! Currently, this pass only propagates scalar values. use std::assert_matches::assert_matches; +use std::cell::RefCell; use std::fmt::Formatter; use rustc_abi::{BackendRepr, FIRST_VARIANT, FieldIdx, Size, VariantIdx}; @@ -85,7 +86,7 @@ struct ConstAnalysis<'a, 'tcx> { map: Map<'tcx>, tcx: TyCtxt<'tcx>, local_decls: &'a LocalDecls<'tcx>, - ecx: InterpCx<'tcx, DummyMachine>, + ecx: RefCell>, typing_env: ty::TypingEnv<'tcx>, } @@ -153,7 +154,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { map, tcx, local_decls: &body.local_decls, - ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine), + ecx: RefCell::new(InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine)), typing_env, } } @@ -410,6 +411,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { match self.eval_operand(operand, state) { FlatSet::Elem(op) => self .ecx + .borrow() .int_to_int_or_float(&op, layout) .discard_err() .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), @@ -424,6 +426,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { match self.eval_operand(operand, state) { FlatSet::Elem(op) => self .ecx + .borrow() .float_to_float_or_int(&op, layout) .discard_err() .map_or(FlatSet::Top, |result| self.wrap_immediate(*result)), @@ -454,6 +457,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { match self.eval_operand(operand, state) { FlatSet::Elem(value) => self .ecx + .borrow() .unary_op(*op, &value) .discard_err() .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), @@ -468,6 +472,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { let val = match null_op { NullOp::OffsetOf(fields) => self .ecx + .borrow() .tcx .offset_of_subfield(self.typing_env, layout, fields.iter()) .bytes(), @@ -556,8 +561,11 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } Operand::Constant(box constant) => { - if let Some(constant) = - self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() + if let Some(constant) = self + .ecx + .borrow() + .eval_mir_constant(&constant.const_, constant.span, None) + .discard_err() { self.assign_constant(state, place, constant, &[]); } @@ -587,7 +595,9 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return; } } - operand = if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_err() { + operand = if let Some(operand) = + self.ecx.borrow().project(&operand, proj_elem).discard_err() + { operand } else { return; @@ -598,17 +608,22 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { place, operand, &mut |elem, op| match elem { - TrackElem::Field(idx) => self.ecx.project_field(op, idx).discard_err(), - TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), + TrackElem::Field(idx) => self.ecx.borrow().project_field(op, idx).discard_err(), + TrackElem::Variant(idx) => { + self.ecx.borrow().project_downcast(op, idx).discard_err() + } TrackElem::Discriminant => { - let variant = self.ecx.read_discriminant(op).discard_err()?; - let discr_value = - self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?; + let variant = self.ecx.borrow().read_discriminant(op).discard_err()?; + let discr_value = self + .ecx + .borrow() + .discriminant_for_variant(op.layout.ty, variant) + .discard_err()?; Some(discr_value.into()) } TrackElem::DerefLen => { - let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into(); - let len_usize = op.len(&self.ecx).discard_err()?; + let op: OpTy<'_> = self.ecx.borrow().deref_pointer(op).discard_err()?.into(); + let len_usize = op.len(&self.ecx.borrow()).discard_err()?; let layout = self .tcx .layout_of(self.typing_env.as_query_input(self.tcx.types.usize)) @@ -617,7 +632,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } }, &mut |place, op| { - if let Some(imm) = self.ecx.read_immediate_raw(op).discard_err() + if let Some(imm) = self.ecx.borrow().read_immediate_raw(op).discard_err() && let Some(imm) = imm.right() { let elem = self.wrap_immediate(*imm); @@ -641,7 +656,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom), // Both sides are known, do the actual computation. (FlatSet::Elem(left), FlatSet::Elem(right)) => { - match self.ecx.binary_op(op, &left, &right).discard_err() { + match self.ecx.borrow().binary_op(op, &left, &right).discard_err() { // Ideally this would return an Immediate, since it's sometimes // a pair and sometimes not. But as a hack we always return a pair // and just make the 2nd component `Bottom` when it does not exist. @@ -714,8 +729,11 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { return None; } let enum_ty_layout = self.tcx.layout_of(self.typing_env.as_query_input(enum_ty)).ok()?; - let discr_value = - self.ecx.discriminant_for_variant(enum_ty_layout.ty, variant_index).discard_err()?; + let discr_value = self + .ecx + .borrow() + .discriminant_for_variant(enum_ty_layout.ty, variant_index) + .discard_err()?; Some(discr_value.to_scalar()) } @@ -956,7 +974,7 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> OperandCollector { state, visitor: self, - ecx: &mut analysis.ecx, + ecx: &mut analysis.ecx.borrow_mut(), map: &analysis.map, } .visit_rvalue(rvalue, location); @@ -978,9 +996,12 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> // Don't overwrite the assignment if it already uses a constant (to keep the span). } StatementKind::Assign(box (place, _)) => { - if let Some(value) = - self.try_make_constant(&mut analysis.ecx, place, state, &analysis.map) - { + if let Some(value) = self.try_make_constant( + &mut analysis.ecx.borrow_mut(), + place, + state, + &analysis.map, + ) { self.patch.assignments.insert(location, value); } } @@ -995,8 +1016,13 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> terminator: &Terminator<'tcx>, location: Location, ) { - OperandCollector { state, visitor: self, ecx: &mut analysis.ecx, map: &analysis.map } - .visit_terminator(terminator, location); + OperandCollector { + state, + visitor: self, + ecx: &mut analysis.ecx.borrow_mut(), + map: &analysis.map, + } + .visit_terminator(terminator, location); } } From 958a2e4a3d57269f50f7973a4228c48c69419da3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Oct 2025 15:11:10 +1100 Subject: [PATCH 02/11] Make `Analysis` immutable in `ResultsVisitor::visit_*` methods. This makes sense -- you wouldn't expect that visiting the results of an analysis would change the analysis itself. --- compiler/rustc_borrowck/src/lib.rs | 6 +++--- compiler/rustc_mir_dataflow/src/framework/graphviz.rs | 8 ++++---- compiler/rustc_mir_dataflow/src/framework/visitor.rs | 8 ++++---- compiler/rustc_mir_transform/src/coroutine.rs | 4 ++-- compiler/rustc_mir_transform/src/dataflow_const_prop.rs | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a57689a45b67b..3fef76b1fc406 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -790,7 +790,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> { fn visit_after_early_statement_effect( &mut self, - _analysis: &mut Borrowck<'a, 'tcx>, + _analysis: &Borrowck<'a, 'tcx>, state: &BorrowckDomain, stmt: &Statement<'tcx>, location: Location, @@ -865,7 +865,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, fn visit_after_early_terminator_effect( &mut self, - _analysis: &mut Borrowck<'a, 'tcx>, + _analysis: &Borrowck<'a, 'tcx>, state: &BorrowckDomain, term: &Terminator<'tcx>, loc: Location, @@ -985,7 +985,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, fn visit_after_primary_terminator_effect( &mut self, - _analysis: &mut Borrowck<'a, 'tcx>, + _analysis: &Borrowck<'a, 'tcx>, state: &BorrowckDomain, term: &Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index b85b82b8f6d92..a55b7a8f67332 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -736,7 +736,7 @@ where fn visit_after_early_statement_effect( &mut self, - analysis: &mut A, + analysis: &A, state: &A::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -749,7 +749,7 @@ where fn visit_after_primary_statement_effect( &mut self, - analysis: &mut A, + analysis: &A, state: &A::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -760,7 +760,7 @@ where fn visit_after_early_terminator_effect( &mut self, - analysis: &mut A, + analysis: &A, state: &A::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -773,7 +773,7 @@ where fn visit_after_primary_terminator_effect( &mut self, - analysis: &mut A, + analysis: &A, state: &A::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index fbb9e4108726d..1bc9d2eff8eea 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -53,7 +53,7 @@ where /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( &mut self, - _analysis: &mut A, + _analysis: &A, _state: &A::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -63,7 +63,7 @@ where /// Called after the "primary" effect of the given statement is applied to `state`. fn visit_after_primary_statement_effect( &mut self, - _analysis: &mut A, + _analysis: &A, _state: &A::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -73,7 +73,7 @@ where /// Called after the "early" effect of the given terminator is applied to `state`. fn visit_after_early_terminator_effect( &mut self, - _analysis: &mut A, + _analysis: &A, _state: &A::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -85,7 +85,7 @@ where /// The `call_return_effect` (if one exists) will *not* be applied to `state`. fn visit_after_primary_terminator_effect( &mut self, - _analysis: &mut A, + _analysis: &A, _state: &A::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 2ccd8178e667b..fef93c5c5bdc6 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -942,7 +942,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>> { fn visit_after_early_statement_effect( &mut self, - _analysis: &mut MaybeRequiresStorage<'a, 'tcx>, + _analysis: &MaybeRequiresStorage<'a, 'tcx>, state: &DenseBitSet, _statement: &Statement<'tcx>, loc: Location, @@ -952,7 +952,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>> fn visit_after_early_terminator_effect( &mut self, - _analysis: &mut MaybeRequiresStorage<'a, 'tcx>, + _analysis: &MaybeRequiresStorage<'a, 'tcx>, state: &DenseBitSet, _terminator: &Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index a79b341ad1a7a..433100ef95fb1 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -964,7 +964,7 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> #[instrument(level = "trace", skip(self, analysis, statement))] fn visit_after_early_statement_effect( &mut self, - analysis: &mut ConstAnalysis<'_, 'tcx>, + analysis: &ConstAnalysis<'_, 'tcx>, state: &State>, statement: &Statement<'tcx>, location: Location, @@ -986,7 +986,7 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> #[instrument(level = "trace", skip(self, analysis, statement))] fn visit_after_primary_statement_effect( &mut self, - analysis: &mut ConstAnalysis<'_, 'tcx>, + analysis: &ConstAnalysis<'_, 'tcx>, state: &State>, statement: &Statement<'tcx>, location: Location, @@ -1011,7 +1011,7 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> fn visit_after_early_terminator_effect( &mut self, - analysis: &mut ConstAnalysis<'_, 'tcx>, + analysis: &ConstAnalysis<'_, 'tcx>, state: &State>, terminator: &Terminator<'tcx>, location: Location, From 517b767fa1072ff915e1b4ae02557de48e11d7b6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Oct 2025 16:50:21 +1100 Subject: [PATCH 03/11] Reorder args for `visit_results_in_block`. Put `analysis` first, to match `apply_effects_in_range`. --- compiler/rustc_mir_dataflow/src/framework/direction.rs | 6 +++--- compiler/rustc_mir_dataflow/src/framework/visitor.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 79c0db7d72831..1dd8fc40006dc 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -40,10 +40,10 @@ pub trait Direction { /// all locations in a basic block (starting from `entry_state` and to /// visit them with `vis`. fn visit_results_in_block<'mir, 'tcx, A>( + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - analysis: &mut A, vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>; @@ -206,10 +206,10 @@ impl Direction for Backward { } fn visit_results_in_block<'mir, 'tcx, A>( + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - analysis: &mut A, vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, @@ -386,10 +386,10 @@ impl Direction for Forward { } fn visit_results_in_block<'mir, 'tcx, A>( + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - analysis: &mut A, vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 1bc9d2eff8eea..9ec59dff0548a 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -24,7 +24,7 @@ pub fn visit_results<'mir, 'tcx, A>( let block_data = &body[block]; state.clone_from(&results[block]); - A::Direction::visit_results_in_block(&mut state, block, block_data, analysis, vis); + A::Direction::visit_results_in_block(analysis, &mut state, block, block_data, vis); } } From 8afbd9fe025d61848335fb268a6aad172ef41383 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Oct 2025 16:04:02 +1100 Subject: [PATCH 04/11] Use a `RefCell` in `MaybeRequiresStorage`. This will let us make `Analysis` arguments in many other places immutable, in the next commit. --- .../rustc_mir_dataflow/src/impls/storage_liveness.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 026826fc379c7..a01db72ff7f73 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::cell::RefCell; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -115,12 +116,12 @@ type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowed /// Dataflow analysis that determines whether each local requires storage at a /// given location; i.e. whether its storage can go away without being observed. pub struct MaybeRequiresStorage<'mir, 'tcx> { - borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>, + borrowed_locals: RefCell>, } impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self { - MaybeRequiresStorage { borrowed_locals } + MaybeRequiresStorage { borrowed_locals: RefCell::new(borrowed_locals) } } } @@ -294,9 +295,10 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. - fn check_for_move(&mut self, state: &mut >::Domain, loc: Location) { - let body = self.borrowed_locals.body(); - let mut visitor = MoveVisitor { state, borrowed_locals: &mut self.borrowed_locals }; + fn check_for_move(&self, state: &mut >::Domain, loc: Location) { + let mut borrowed_locals = self.borrowed_locals.borrow_mut(); + let body = borrowed_locals.body(); + let mut visitor = MoveVisitor { state, borrowed_locals: &mut borrowed_locals }; visitor.visit_location(body, loc); } } From a97cd3ba57b1f07503ca3f121ea8b425decf8fcc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 27 Oct 2025 16:06:39 +1100 Subject: [PATCH 05/11] Make `Analysis` immutable in many more places. The `state: A::Domain` value is the primary things that's modified when performing an analysis. The `Analysis` impl is immutable in every case but one (`MaybeRequiredStorage`) and it now uses interior mutability. As well as changing many `&mut A` arguments to `&A`, this also: - lets `CowMut` be replaced with the simpler `SimpleCow` in `cursor.rs`; - removes the need for the `RefCell` in `Formatter`; - removes the need for `MaybeBorrowedLocals` to impl `Clone`, because it's a unit type and it's now clear that its constructor can be used directly instead of being put into a local variable and cloned. --- compiler/rustc_borrowck/src/dataflow.rs | 18 +++---- compiler/rustc_borrowck/src/lib.rs | 6 +-- .../src/check_consts/resolver.rs | 6 +-- .../src/framework/cursor.rs | 47 +++++++------------ .../src/framework/direction.rs | 18 +++---- .../src/framework/graphviz.rs | 22 ++++----- .../rustc_mir_dataflow/src/framework/mod.rs | 20 ++++---- .../rustc_mir_dataflow/src/framework/tests.rs | 8 ++-- .../src/framework/visitor.rs | 4 +- .../src/impls/borrowed_locals.rs | 5 +- .../src/impls/initialized.rs | 26 +++++----- .../rustc_mir_dataflow/src/impls/liveness.rs | 12 ++--- .../src/impls/storage_liveness.rs | 14 +++--- compiler/rustc_mir_transform/src/coroutine.rs | 29 ++++-------- .../src/dataflow_const_prop.rs | 10 ++-- compiler/rustc_mir_transform/src/liveness.rs | 6 +-- 16 files changed, 111 insertions(+), 140 deletions(-) diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 84d5ffb947914..813ceaeb8da9f 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, loc: Location, @@ -55,7 +55,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, loc: Location, @@ -66,7 +66,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, term: &mir::Terminator<'tcx>, loc: Location, @@ -77,7 +77,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, term: &'mir mir::Terminator<'tcx>, loc: Location, @@ -92,7 +92,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, _state: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -533,7 +533,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -542,7 +542,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, location: Location, @@ -590,7 +590,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -599,7 +599,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, _location: Location, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 3fef76b1fc406..a33241ebe70ac 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -537,13 +537,13 @@ fn borrowck_check_region_constraints<'tcx>( mbcx.report_region_errors(nll_errors); } - let (mut flow_analysis, flow_entry_states) = + let (flow_analysis, flow_results) = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), - &mut flow_analysis, - &flow_entry_states, + &flow_analysis, + &flow_results, &mut mbcx, ); diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 664dda8701a63..e6e3948305af7 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -332,7 +332,7 @@ where } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -341,7 +341,7 @@ where } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -351,7 +351,7 @@ where } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 3f6e7a0661921..8799caa3e28f5 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,8 +1,7 @@ //! Random access inspection of the results of a dataflow analysis. -use std::borrow::Cow; use std::cmp::Ordering; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; #[cfg(debug_assertions)] use rustc_index::bit_set::DenseBitSet; @@ -10,30 +9,20 @@ use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; -/// Some `ResultsCursor`s want to own an `Analysis`, and some want to borrow an `Analysis`, either -/// mutable or immutably. This type allows all of the above. It's similar to `Cow`, but `Cow` -/// doesn't allow mutable borrowing. -enum CowMut<'a, T> { - BorrowedMut(&'a mut T), +/// This is like `Cow`, but it lacks the `T: ToOwned` bound and doesn't support +/// `to_owned`/`into_owned`. +enum SimpleCow<'a, T> { + Borrowed(&'a T), Owned(T), } -impl Deref for CowMut<'_, T> { +impl Deref for SimpleCow<'_, T> { type Target = T; fn deref(&self) -> &T { match self { - CowMut::BorrowedMut(borrowed) => borrowed, - CowMut::Owned(owned) => owned, - } - } -} - -impl DerefMut for CowMut<'_, T> { - fn deref_mut(&mut self) -> &mut T { - match self { - CowMut::BorrowedMut(borrowed) => borrowed, - CowMut::Owned(owned) => owned, + SimpleCow::Borrowed(borrowed) => borrowed, + SimpleCow::Owned(owned) => owned, } } } @@ -53,8 +42,8 @@ where A: Analysis<'tcx>, { body: &'mir mir::Body<'tcx>, - analysis: CowMut<'mir, A>, - results: Cow<'mir, Results>, + analysis: SimpleCow<'mir, A>, + results: SimpleCow<'mir, Results>, state: A::Domain, pos: CursorPosition, @@ -84,8 +73,8 @@ where fn new( body: &'mir mir::Body<'tcx>, - analysis: CowMut<'mir, A>, - results: Cow<'mir, Results>, + analysis: SimpleCow<'mir, A>, + results: SimpleCow<'mir, Results>, ) -> Self { let bottom_value = analysis.bottom_value(body); ResultsCursor { @@ -111,16 +100,16 @@ where analysis: A, results: Results, ) -> Self { - Self::new(body, CowMut::Owned(analysis), Cow::Owned(results)) + Self::new(body, SimpleCow::Owned(analysis), SimpleCow::Owned(results)) } /// Returns a new cursor that borrows and inspects analysis results. pub fn new_borrowing( body: &'mir mir::Body<'tcx>, - analysis: &'mir mut A, + analysis: &'mir A, results: &'mir Results, ) -> Self { - Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results)) + Self::new(body, SimpleCow::Borrowed(analysis), SimpleCow::Borrowed(results)) } /// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled. @@ -236,7 +225,7 @@ where let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( - &mut *self.analysis, + &*self.analysis, &mut self.state, target.block, block_data, @@ -251,8 +240,8 @@ where /// /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) { - f(&mut self.analysis, &mut self.state); + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { + f(&self.analysis, &mut self.state); self.state_needs_reset = true; } } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 1dd8fc40006dc..b15b5c07ce382 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -14,7 +14,7 @@ pub trait Direction { /// Called by `iterate_to_fixpoint` during initial analysis computation. fn apply_effects_in_block<'mir, 'tcx, A>( - analysis: &mut A, + analysis: &A, body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, @@ -28,7 +28,7 @@ pub trait Direction { /// /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range<'tcx, A>( - analysis: &mut A, + analysis: &A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -40,7 +40,7 @@ pub trait Direction { /// all locations in a basic block (starting from `entry_state` and to /// visit them with `vis`. fn visit_results_in_block<'mir, 'tcx, A>( - analysis: &mut A, + analysis: &A, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, @@ -56,7 +56,7 @@ impl Direction for Backward { const IS_FORWARD: bool = false; fn apply_effects_in_block<'mir, 'tcx, A>( - analysis: &mut A, + analysis: &A, body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, @@ -129,7 +129,7 @@ impl Direction for Backward { } fn apply_effects_in_range<'tcx, A>( - analysis: &mut A, + analysis: &A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -206,7 +206,7 @@ impl Direction for Backward { } fn visit_results_in_block<'mir, 'tcx, A>( - analysis: &mut A, + analysis: &A, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, @@ -242,7 +242,7 @@ impl Direction for Forward { const IS_FORWARD: bool = true; fn apply_effects_in_block<'mir, 'tcx, A>( - analysis: &mut A, + analysis: &A, body: &mir::Body<'tcx>, state: &mut A::Domain, block: BasicBlock, @@ -312,7 +312,7 @@ impl Direction for Forward { } fn apply_effects_in_range<'tcx, A>( - analysis: &mut A, + analysis: &A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -386,7 +386,7 @@ impl Direction for Forward { } fn visit_results_in_block<'mir, 'tcx, A>( - analysis: &mut A, + analysis: &A, state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index a55b7a8f67332..f9d9106ba7af3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -1,7 +1,6 @@ //! A helpful diagram for debugging dataflow problems. use std::borrow::Cow; -use std::cell::RefCell; use std::ffi::OsString; use std::path::PathBuf; use std::sync::OnceLock; @@ -33,7 +32,7 @@ use crate::errors::{ pub(super) fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - analysis: &mut A, + analysis: &A, results: &Results, pass_name: Option<&'static str>, ) -> std::io::Result<()> @@ -206,11 +205,7 @@ where A: Analysis<'tcx>, { body: &'mir Body<'tcx>, - // The `RefCell` is used because `::node_label` - // takes `&self`, but it needs to modify the analysis. This is also the - // reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has - // the operations that involve the mutation, i.e. within the `borrow_mut`. - analysis: RefCell<&'mir mut A>, + analysis: &'mir A, results: &'mir Results, style: OutputStyle, reachable: DenseBitSet, @@ -222,12 +217,12 @@ where { fn new( body: &'mir Body<'tcx>, - analysis: &'mir mut A, + analysis: &'mir A, results: &'mir Results, style: OutputStyle, ) -> Self { let reachable = traversal::reachable_as_bitset(body); - Formatter { body, analysis: analysis.into(), results, style, reachable } + Formatter { body, analysis, results, style, reachable } } } @@ -265,12 +260,11 @@ where } fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { - let analysis = &mut **self.analysis.borrow_mut(); - - let diffs = StateDiffCollector::run(self.body, *block, analysis, self.results, self.style); + let diffs = + StateDiffCollector::run(self.body, *block, self.analysis, self.results, self.style); let mut fmt = BlockFormatter { - cursor: ResultsCursor::new_borrowing(self.body, analysis, self.results), + cursor: ResultsCursor::new_borrowing(self.body, self.analysis, self.results), style: self.style, bg: Background::Light, }; @@ -698,7 +692,7 @@ impl StateDiffCollector { fn run<'tcx, A>( body: &Body<'tcx>, block: BasicBlock, - analysis: &mut A, + analysis: &A, results: &Results, style: OutputStyle, ) -> Self diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index b6a5603601959..9e1fc5a59fe34 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -136,7 +136,7 @@ pub trait Analysis<'tcx> { /// analyses should not implement this without also implementing /// `apply_primary_statement_effect`. fn apply_early_statement_effect( - &mut self, + &self, _state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -145,7 +145,7 @@ pub trait Analysis<'tcx> { /// Updates the current dataflow state with the effect of evaluating a statement. fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -159,7 +159,7 @@ pub trait Analysis<'tcx> { /// analyses should not implement this without also implementing /// `apply_primary_terminator_effect`. fn apply_early_terminator_effect( - &mut self, + &self, _state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -173,7 +173,7 @@ pub trait Analysis<'tcx> { /// `InitializedPlaces` analyses, the return place for a function call is not marked as /// initialized here. fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, _state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -189,7 +189,7 @@ pub trait Analysis<'tcx> { /// This is separate from `apply_primary_terminator_effect` to properly track state across /// unwind edges. fn apply_call_return_effect( - &mut self, + &self, _state: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -211,7 +211,7 @@ pub trait Analysis<'tcx> { /// engine doesn't need to clone the exit state for a block unless /// `get_switch_int_data` is actually called. fn get_switch_int_data( - &mut self, + &self, _block: mir::BasicBlock, _discr: &mir::Operand<'tcx>, ) -> Option { @@ -220,7 +220,7 @@ pub trait Analysis<'tcx> { /// See comments on `get_switch_int_data`. fn apply_switch_int_edge_effect( - &mut self, + &self, _data: &mut Self::SwitchIntData, _state: &mut Self::Domain, _value: SwitchTargetValue, @@ -245,7 +245,7 @@ pub trait Analysis<'tcx> { /// Without a `pass_name` to differentiates them, only the results for the latest run will be /// saved. fn iterate_to_fixpoint<'mir>( - mut self, + self, tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, pass_name: Option<&'static str>, @@ -285,7 +285,7 @@ pub trait Analysis<'tcx> { state.clone_from(&results[bb]); Self::Direction::apply_effects_in_block( - &mut self, + &self, body, &mut state, bb, @@ -300,7 +300,7 @@ pub trait Analysis<'tcx> { } if tcx.sess.opts.unstable_opts.dump_mir_dataflow { - let res = write_graphviz_results(tcx, body, &mut self, &results, pass_name); + let res = write_graphviz_results(tcx, body, &self, &results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 23e28a11a452b..36d0f3a947bc6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -175,7 +175,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -185,7 +185,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -195,7 +195,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -205,7 +205,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 9ec59dff0548a..28ae08d1e6b19 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -7,7 +7,7 @@ use super::{Analysis, Direction, Results}; pub fn visit_results<'mir, 'tcx, A>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - analysis: &mut A, + analysis: &A, results: &Results, vis: &mut impl ResultsVisitor<'tcx, A>, ) where @@ -31,7 +31,7 @@ pub fn visit_results<'mir, 'tcx, A>( /// Like `visit_results`, but only for reachable blocks. pub fn visit_reachable_results<'mir, 'tcx, A>( body: &'mir mir::Body<'tcx>, - analysis: &mut A, + analysis: &A, results: &Results, vis: &mut impl ResultsVisitor<'tcx, A>, ) where diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index a4e4e30a8bb6c..331e41bd126b7 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -11,7 +11,6 @@ use crate::{Analysis, GenKill}; /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for /// immovable coroutines. -#[derive(Clone)] pub struct MaybeBorrowedLocals; impl MaybeBorrowedLocals { @@ -34,7 +33,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &Statement<'tcx>, location: Location, @@ -43,7 +42,7 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, location: Location, diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 5937d68f389e4..9216106b6eddc 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -376,7 +376,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -400,7 +400,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -429,7 +429,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -448,7 +448,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn get_switch_int_data( - &mut self, + &self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, ) -> Option { @@ -460,7 +460,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn apply_switch_int_edge_effect( - &mut self, + &self, data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, @@ -513,7 +513,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -527,7 +527,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -545,7 +545,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -564,7 +564,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn get_switch_int_data( - &mut self, + &self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, ) -> Option { @@ -580,7 +580,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn apply_switch_int_edge_effect( - &mut self, + &self, data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, @@ -627,7 +627,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, state), level = "debug")] fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, location: Location, @@ -652,7 +652,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, state, terminator), level = "debug")] fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -674,7 +674,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 596da18e3d1b4..e439f2e052fe8 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -41,7 +41,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -50,7 +50,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -60,7 +60,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -278,7 +278,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -294,7 +294,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -304,7 +304,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index a01db72ff7f73..3702f7c55a431 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -54,7 +54,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &Statement<'tcx>, _: Location, @@ -98,7 +98,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &Statement<'tcx>, _: Location, @@ -144,7 +144,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &Statement<'tcx>, loc: Location, @@ -177,7 +177,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _: &Statement<'tcx>, loc: Location, @@ -188,7 +188,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, loc: Location, @@ -243,7 +243,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } fn apply_primary_terminator_effect<'t>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'t Terminator<'tcx>, loc: Location, @@ -284,7 +284,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index fef93c5c5bdc6..1fc109533ca23 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -720,27 +720,16 @@ fn locals_live_across_suspend_points<'tcx>( // Calculate the MIR locals that have been previously borrowed (even if they are still active). let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")); - let mut borrowed_locals_analysis1 = borrowed_locals.analysis; - let mut borrowed_locals_analysis2 = borrowed_locals_analysis1.clone(); // trivial - let borrowed_locals_cursor1 = ResultsCursor::new_borrowing( - body, - &mut borrowed_locals_analysis1, - &borrowed_locals.results, - ); - let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing( - body, - &mut borrowed_locals_analysis2, - &borrowed_locals.results, - ); + let borrowed_locals_cursor1 = + ResultsCursor::new_borrowing(body, &MaybeBorrowedLocals, &borrowed_locals.results); + let mut borrowed_locals_cursor2 = + ResultsCursor::new_borrowing(body, &MaybeBorrowedLocals, &borrowed_locals.results); // Calculate the MIR locals that we need to keep storage around for. - let mut requires_storage = + let requires_storage = MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None); - let mut requires_storage_cursor = ResultsCursor::new_borrowing( - body, - &mut requires_storage.analysis, - &requires_storage.results, - ); + let mut requires_storage_cursor = + ResultsCursor::new_borrowing(body, &requires_storage.analysis, &requires_storage.results); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = @@ -812,7 +801,7 @@ fn locals_live_across_suspend_points<'tcx>( body, &saved_locals, always_live_locals.clone(), - &mut requires_storage.analysis, + &requires_storage.analysis, &requires_storage.results, ); @@ -878,7 +867,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &'mir CoroutineSavedLocals, always_live_locals: DenseBitSet, - analysis: &mut MaybeRequiresStorage<'mir, 'tcx>, + analysis: &MaybeRequiresStorage<'mir, 'tcx>, results: &Results>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 433100ef95fb1..f849a788c361f 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -61,13 +61,13 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { let map = Map::new(tcx, body, place_limit); // Perform the actual dataflow analysis. - let mut const_ = debug_span!("analyze") + let const_ = debug_span!("analyze") .in_scope(|| ConstAnalysis::new(tcx, body, map).iterate_to_fixpoint(tcx, body, None)); // Collect results and patch the body afterwards. let mut visitor = Collector::new(tcx, &body.local_decls); debug_span!("collect").in_scope(|| { - visit_reachable_results(body, &mut const_.analysis, &const_.results, &mut visitor) + visit_reachable_results(body, &const_.analysis, &const_.results, &mut visitor) }); let mut patch = visitor.patch; debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body)); @@ -112,7 +112,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location, @@ -123,7 +123,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, _location: Location, @@ -136,7 +136,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 82f9dfe4745d2..840bb169a1fa3 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1160,7 +1160,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> { } fn apply_primary_statement_effect( - &mut self, + &self, trans: &mut Self::Domain, statement: &Statement<'tcx>, location: Location, @@ -1169,7 +1169,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> { } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, trans: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, location: Location, @@ -1179,7 +1179,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> { } fn apply_call_return_effect( - &mut self, + &self, _trans: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, From 87932397024a9053ca028adbba8315fc93cfa343 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 28 Oct 2025 09:41:58 +1100 Subject: [PATCH 06/11] Put `Analysis` back into `Results`. `Results` used to contain an `Analysis`, but it was removed in #140234. That change made sense because the analysis was mutable but the entry states were immutable and it was good to separate them so the mutability of the different pieces was clear. Now that analyses are immutable there is no need for the separation, lots of analysis+results pairs can be combined, and the names are going back to what they were before: - `Results` -> `EntryStates` - `AnalysisAndResults` -> `Results` --- compiler/rustc_borrowck/src/lib.rs | 18 +++++----- .../src/framework/cursor.rs | 36 ++++++------------- .../src/framework/graphviz.rs | 29 ++++++--------- .../rustc_mir_dataflow/src/framework/mod.rs | 25 +++++++------ .../src/framework/results.rs | 18 +++++----- .../rustc_mir_dataflow/src/framework/tests.rs | 10 +++--- .../src/framework/visitor.rs | 14 ++++---- compiler/rustc_mir_dataflow/src/lib.rs | 5 +-- compiler/rustc_mir_transform/src/coroutine.rs | 17 ++++----- .../src/dataflow_const_prop.rs | 4 +-- compiler/rustc_mir_transform/src/dest_prop.rs | 8 ++--- 11 files changed, 77 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a33241ebe70ac..d4d38301a1586 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -49,7 +49,7 @@ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex, }; use rustc_mir_dataflow::points::DenseLocationMap; -use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results}; +use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use smallvec::SmallVec; @@ -537,12 +537,10 @@ fn borrowck_check_region_constraints<'tcx>( mbcx.report_region_errors(nll_errors); } - let (flow_analysis, flow_results) = - get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); + let flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), - &flow_analysis, &flow_results, &mut mbcx, ); @@ -604,7 +602,7 @@ fn get_flow_results<'a, 'tcx>( move_data: &'a MoveData<'tcx>, borrow_set: &'a BorrowSet<'tcx>, regioncx: &RegionInferenceContext<'tcx>, -) -> (Borrowck<'a, 'tcx>, Results) { +) -> Results<'tcx, Borrowck<'a, 'tcx>> { // We compute these three analyses individually, but them combine them into // a single results so that `mbcx` can visit them all together. let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint( @@ -629,14 +627,14 @@ fn get_flow_results<'a, 'tcx>( ever_inits: ever_inits.analysis, }; - assert_eq!(borrows.results.len(), uninits.results.len()); - assert_eq!(borrows.results.len(), ever_inits.results.len()); - let results: Results<_> = - itertools::izip!(borrows.results, uninits.results, ever_inits.results) + assert_eq!(borrows.entry_states.len(), uninits.entry_states.len()); + assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len()); + let entry_states: EntryStates<_> = + itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states) .map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits }) .collect(); - (analysis, results) + Results { analysis, entry_states } } pub(crate) struct BorrowckInferCtxt<'tcx> { diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 8799caa3e28f5..3c56999fcbdc9 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -42,8 +42,7 @@ where A: Analysis<'tcx>, { body: &'mir mir::Body<'tcx>, - analysis: SimpleCow<'mir, A>, - results: SimpleCow<'mir, Results>, + results: SimpleCow<'mir, Results<'tcx, A>>, state: A::Domain, pos: CursorPosition, @@ -71,15 +70,10 @@ where self.body } - fn new( - body: &'mir mir::Body<'tcx>, - analysis: SimpleCow<'mir, A>, - results: SimpleCow<'mir, Results>, - ) -> Self { - let bottom_value = analysis.bottom_value(body); + fn new(body: &'mir mir::Body<'tcx>, results: SimpleCow<'mir, Results<'tcx, A>>) -> Self { + let bottom_value = results.analysis.bottom_value(body); ResultsCursor { body, - analysis, results, // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that @@ -95,21 +89,13 @@ where } /// Returns a new cursor that takes ownership of and inspects analysis results. - pub fn new_owning( - body: &'mir mir::Body<'tcx>, - analysis: A, - results: Results, - ) -> Self { - Self::new(body, SimpleCow::Owned(analysis), SimpleCow::Owned(results)) + pub fn new_owning(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self { + Self::new(body, SimpleCow::Owned(results)) } /// Returns a new cursor that borrows and inspects analysis results. - pub fn new_borrowing( - body: &'mir mir::Body<'tcx>, - analysis: &'mir A, - results: &'mir Results, - ) -> Self { - Self::new(body, SimpleCow::Borrowed(analysis), SimpleCow::Borrowed(results)) + pub fn new_borrowing(body: &'mir mir::Body<'tcx>, results: &'mir Results<'tcx, A>) -> Self { + Self::new(body, SimpleCow::Borrowed(results)) } /// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled. @@ -121,7 +107,7 @@ where /// Returns the `Analysis` used to generate the underlying `Results`. pub fn analysis(&self) -> &A { - &self.analysis + &self.results.analysis } /// Resets the cursor to hold the entry set for the given basic block. @@ -133,7 +119,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.clone_from(&self.results[block]); + self.state.clone_from(&self.results.entry_states[block]); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -225,7 +211,7 @@ where let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( - &*self.analysis, + &self.results.analysis, &mut self.state, target.block, block_data, @@ -241,7 +227,7 @@ where /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { - f(&self.analysis, &mut self.state); + f(&self.results.analysis, &mut self.state); self.state_needs_reset = true; } } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index f9d9106ba7af3..22bff3806b156 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -32,8 +32,7 @@ use crate::errors::{ pub(super) fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - analysis: &A, - results: &Results, + results: &Results<'tcx, A>, pass_name: Option<&'static str>, ) -> std::io::Result<()> where @@ -80,7 +79,7 @@ where let mut buf = Vec::new(); - let graphviz = Formatter::new(body, analysis, results, style); + let graphviz = Formatter::new(body, results, style); let mut render_opts = vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; if tcx.sess.opts.unstable_opts.graphviz_dark_mode { @@ -205,8 +204,7 @@ where A: Analysis<'tcx>, { body: &'mir Body<'tcx>, - analysis: &'mir A, - results: &'mir Results, + results: &'mir Results<'tcx, A>, style: OutputStyle, reachable: DenseBitSet, } @@ -215,14 +213,9 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { - fn new( - body: &'mir Body<'tcx>, - analysis: &'mir A, - results: &'mir Results, - style: OutputStyle, - ) -> Self { + fn new(body: &'mir Body<'tcx>, results: &'mir Results<'tcx, A>, style: OutputStyle) -> Self { let reachable = traversal::reachable_as_bitset(body); - Formatter { body, analysis, results, style, reachable } + Formatter { body, results, style, reachable } } } @@ -260,11 +253,10 @@ where } fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { - let diffs = - StateDiffCollector::run(self.body, *block, self.analysis, self.results, self.style); + let diffs = StateDiffCollector::run(self.body, *block, self.results, self.style); let mut fmt = BlockFormatter { - cursor: ResultsCursor::new_borrowing(self.body, self.analysis, self.results), + cursor: ResultsCursor::new_borrowing(self.body, self.results), style: self.style, bg: Background::Light, }; @@ -692,8 +684,7 @@ impl StateDiffCollector { fn run<'tcx, A>( body: &Body<'tcx>, block: BasicBlock, - analysis: &A, - results: &Results, + results: &Results<'tcx, A>, style: OutputStyle, ) -> Self where @@ -701,12 +692,12 @@ impl StateDiffCollector { D: DebugWithContext, { let mut collector = StateDiffCollector { - prev_state: analysis.bottom_value(body), + prev_state: results.analysis.bottom_value(body), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), }; - visit_results(body, std::iter::once(block), analysis, results, &mut collector); + visit_results(body, std::iter::once(block), results, &mut collector); collector } } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 9e1fc5a59fe34..60b3c15d80d79 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -58,8 +58,7 @@ mod visitor; pub use self::cursor::ResultsCursor; pub use self::direction::{Backward, Direction, Forward}; pub use self::lattice::{JoinSemiLattice, MaybeReachable}; -pub(crate) use self::results::AnalysisAndResults; -pub use self::results::Results; +pub use self::results::{EntryStates, Results}; pub use self::visitor::{ResultsVisitor, visit_reachable_results, visit_results}; /// Analysis domains are all bitsets of various kinds. This trait holds @@ -249,15 +248,17 @@ pub trait Analysis<'tcx> { tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, pass_name: Option<&'static str>, - ) -> AnalysisAndResults<'tcx, Self> + ) -> Results<'tcx, Self> where Self: Sized, Self::Domain: DebugWithContext, { - let mut results = IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len()); - self.initialize_start_block(body, &mut results[mir::START_BLOCK]); + let mut entry_states = + IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len()); + self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]); - if Self::Direction::IS_BACKWARD && results[mir::START_BLOCK] != self.bottom_value(body) { + if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body) + { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } @@ -281,8 +282,8 @@ pub trait Analysis<'tcx> { let mut state = self.bottom_value(body); while let Some(bb) = dirty_queue.pop() { // Set the state to the entry state of the block. This is equivalent to `state = - // results[bb].clone()`, but it saves an allocation, thus improving compile times. - state.clone_from(&results[bb]); + // entry_states[bb].clone()`, but it saves an allocation, thus improving compile times. + state.clone_from(&entry_states[bb]); Self::Direction::apply_effects_in_block( &self, @@ -291,7 +292,7 @@ pub trait Analysis<'tcx> { bb, &body[bb], |target: BasicBlock, state: &Self::Domain| { - let set_changed = results[target].join(state); + let set_changed = entry_states[target].join(state); if set_changed { dirty_queue.insert(target); } @@ -299,14 +300,16 @@ pub trait Analysis<'tcx> { ); } + let results = Results { analysis: self, entry_states }; + if tcx.sess.opts.unstable_opts.dump_mir_dataflow { - let res = write_graphviz_results(tcx, body, &self, &results, pass_name); + let res = write_graphviz_results(tcx, body, &results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } } - AnalysisAndResults { analysis: self, results } + results } } diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs index 7b7e981d3a554..76b6cc47dde4a 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -5,26 +5,26 @@ use rustc_middle::mir::{BasicBlock, Body}; use super::{Analysis, ResultsCursor}; -/// The results of a dataflow analysis that has converged to fixpoint. It only holds the domain -/// values at the entry of each basic block. Domain values in other parts of the block are -/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls). -pub type Results = IndexVec; +pub type EntryStates = IndexVec; -/// Utility type used in a few places where it's convenient to bundle an analysis with its results. -pub struct AnalysisAndResults<'tcx, A> +/// The results of a dataflow analysis that has converged to fixpoint. It holds the domain values +/// (states) at the entry of each basic block. Domain values in other parts of the block are +/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls). The +/// analysis is also present because it's often needed alongside the entry states. +pub struct Results<'tcx, A> where A: Analysis<'tcx>, { pub analysis: A, - pub results: Results, + pub entry_states: EntryStates, } -impl<'tcx, A> AnalysisAndResults<'tcx, A> +impl<'tcx, A> Results<'tcx, A> where A: Analysis<'tcx>, { /// Creates a `ResultsCursor` that takes ownership of `self`. pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> { - ResultsCursor::new_owning(body, self.analysis, self.results) + ResultsCursor::new_owning(body, self) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 36d0f3a947bc6..85f23b8332a1c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -71,15 +71,15 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { /// /// | Location | Before | After | /// |------------------------|-------------------|--------| -/// | (on_entry) | {102} || +/// | (on_entry) | {102} | | /// | statement 0 | +0 | +1 | /// | statement 1 | +2 | +3 | /// | `Call` terminator | +4 | +5 | -/// | (on unwind) | {102,0,1,2,3,4,5} || +/// | (on unwind) | {102,0,1,2,3,4,5} | | /// /// The `102` in the block's entry set is derived from the basic block index and ensures that the /// expected state is unique across all basic blocks. Remember, it is generated by -/// `mock_results`, not from actually running `MockAnalysis` to fixpoint. +/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint. struct MockAnalysis<'tcx, D> { body: &'tcx mir::Body<'tcx>, dir: PhantomData, @@ -96,7 +96,7 @@ impl MockAnalysis<'_, D> { ret } - fn mock_results(&self) -> IndexVec> { + fn mock_entry_states(&self) -> IndexVec> { let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks); @@ -255,7 +255,7 @@ fn test_cursor(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - AnalysisAndResults { results: analysis.mock_results(), analysis }.into_results_cursor(body); + Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body); cursor.allow_unreachable(); diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 28ae08d1e6b19..46940c6ab62fc 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -7,13 +7,12 @@ use super::{Analysis, Direction, Results}; pub fn visit_results<'mir, 'tcx, A>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - analysis: &A, - results: &Results, + results: &Results<'tcx, A>, vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { - let mut state = analysis.bottom_value(body); + let mut state = results.analysis.bottom_value(body); #[cfg(debug_assertions)] let reachable_blocks = mir::traversal::reachable_as_bitset(body); @@ -23,22 +22,21 @@ pub fn visit_results<'mir, 'tcx, A>( assert!(reachable_blocks.contains(block)); let block_data = &body[block]; - state.clone_from(&results[block]); - A::Direction::visit_results_in_block(analysis, &mut state, block, block_data, vis); + state.clone_from(&results.entry_states[block]); + A::Direction::visit_results_in_block(&results.analysis, &mut state, block, block_data, vis); } } /// Like `visit_results`, but only for reachable blocks. pub fn visit_reachable_results<'mir, 'tcx, A>( body: &'mir mir::Body<'tcx>, - analysis: &A, - results: &Results, + results: &Results<'tcx, A>, vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { let blocks = traversal::reachable(body).map(|(bb, _)| bb); - visit_results(body, blocks, analysis, results, vis) + visit_results(body, blocks, results, vis) } /// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 2e8c916544160..485925e7b50cd 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -17,8 +17,9 @@ pub use self::drop_flag_effects::{ move_path_children_matching, on_all_children_bits, on_lookup_result_bits, }; pub use self::framework::{ - Analysis, Backward, Direction, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results, - ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results, visit_results, + Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable, + Results, ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results, + visit_results, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 1fc109533ca23..069fcfefac21e 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -720,16 +720,13 @@ fn locals_live_across_suspend_points<'tcx>( // Calculate the MIR locals that have been previously borrowed (even if they are still active). let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")); - let borrowed_locals_cursor1 = - ResultsCursor::new_borrowing(body, &MaybeBorrowedLocals, &borrowed_locals.results); - let mut borrowed_locals_cursor2 = - ResultsCursor::new_borrowing(body, &MaybeBorrowedLocals, &borrowed_locals.results); + let borrowed_locals_cursor1 = ResultsCursor::new_borrowing(body, &borrowed_locals); + let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing(body, &borrowed_locals); // Calculate the MIR locals that we need to keep storage around for. let requires_storage = MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None); - let mut requires_storage_cursor = - ResultsCursor::new_borrowing(body, &requires_storage.analysis, &requires_storage.results); + let mut requires_storage_cursor = ResultsCursor::new_borrowing(body, &requires_storage); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = @@ -801,8 +798,7 @@ fn locals_live_across_suspend_points<'tcx>( body, &saved_locals, always_live_locals.clone(), - &requires_storage.analysis, - &requires_storage.results, + &requires_storage, ); LivenessInfo { @@ -867,8 +863,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &'mir CoroutineSavedLocals, always_live_locals: DenseBitSet, - analysis: &MaybeRequiresStorage<'mir, 'tcx>, - results: &Results>, + results: &Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -888,7 +883,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()), }; - visit_reachable_results(body, analysis, results, &mut visitor); + visit_reachable_results(body, results, &mut visitor); let local_conflicts = visitor.local_conflicts; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index f849a788c361f..8532e1e9d7cc9 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -66,9 +66,7 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { // Collect results and patch the body afterwards. let mut visitor = Collector::new(tcx, &body.local_decls); - debug_span!("collect").in_scope(|| { - visit_reachable_results(body, &const_.analysis, &const_.results, &mut visitor) - }); + debug_span!("collect").in_scope(|| visit_reachable_results(body, &const_, &mut visitor)); let mut patch = visitor.patch; debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body)); } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index a10e0b82467cc..8c2149ef96fb0 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -146,7 +146,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; use rustc_mir_dataflow::points::DenseLocationMap; -use rustc_mir_dataflow::{Analysis, Results}; +use rustc_mir_dataflow::{Analysis, EntryStates}; use tracing::{debug, trace}; pub(super) struct DestinationPropagation; @@ -173,7 +173,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { let points = DenseLocationMap::new(body); let mut relevant = RelevantLocals::compute(&candidates, body.local_decls.len()); - let mut live = save_as_intervals(&points, body, &relevant, live.results); + let mut live = save_as_intervals(&points, body, &relevant, live.entry_states); dest_prop_mir_dump(tcx, body, &points, &live, &relevant); @@ -506,7 +506,7 @@ fn save_as_intervals<'tcx>( elements: &DenseLocationMap, body: &Body<'tcx>, relevant: &RelevantLocals, - results: Results>, + entry_states: EntryStates>, ) -> SparseIntervalMatrix { let mut values = SparseIntervalMatrix::new(2 * elements.num_points()); let mut state = MaybeLiveLocals.bottom_value(body); @@ -529,7 +529,7 @@ fn save_as_intervals<'tcx>( continue; } - state.clone_from(&results[block]); + state.clone_from(&entry_states[block]); let block_data = &body.basic_blocks[block]; let loc = Location { block, statement_index: block_data.statements.len() }; From 5538a068bd4220c3f69edc9204d6b9bf58f75613 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Oct 2025 09:33:53 +0000 Subject: [PATCH 07/11] Show that a test still fails with the feature gate enabled --- tests/ui/traits/const-traits/issue-103677.cnst.stderr | 9 +++++++++ tests/ui/traits/const-traits/issue-103677.rs | 9 +++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/const-traits/issue-103677.cnst.stderr diff --git a/tests/ui/traits/const-traits/issue-103677.cnst.stderr b/tests/ui/traits/const-traits/issue-103677.cnst.stderr new file mode 100644 index 0000000000000..845bdb326db11 --- /dev/null +++ b/tests/ui/traits/const-traits/issue-103677.cnst.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `String: const Deref` is not satisfied + --> $DIR/issue-103677.rs:6:5 + | +LL | &*s as &str; + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/issue-103677.rs b/tests/ui/traits/const-traits/issue-103677.rs index c032cc7a68803..09f45f5ee3771 100644 --- a/tests/ui/traits/const-traits/issue-103677.rs +++ b/tests/ui/traits/const-traits/issue-103677.rs @@ -1,5 +1,10 @@ -//@ check-pass +//@[stock] check-pass +//@ revisions: stock cnst +#![cfg_attr(cnst, feature(const_trait_impl))] -const _: fn(&String) = |s| { &*s as &str; }; +const _: fn(&String) = |s| { + &*s as &str; + //[cnst]~^ ERROR: the trait bound `String: const Deref` is not satisfied +}; fn main() {} From 49205a1dc09c729ddcb887fa631dd0d0c63b70db Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Oct 2025 13:36:14 +1100 Subject: [PATCH 08/11] Extract parts of `bootstrap::core::builder` into a `cli_paths` module --- src/bootstrap/src/core/builder/cli_paths.rs | 229 ++++++++++++++++++++ src/bootstrap/src/core/builder/mod.rs | 227 +------------------ src/bootstrap/src/core/builder/tests.rs | 1 + 3 files changed, 234 insertions(+), 223 deletions(-) create mode 100644 src/bootstrap/src/core/builder/cli_paths.rs diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs new file mode 100644 index 0000000000000..de50ecee48ad1 --- /dev/null +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -0,0 +1,229 @@ +//! Various pieces of code for dealing with "paths" passed to bootstrap on the +//! command-line, extracted from `core/builder/mod.rs` because that file is +//! large and hard to navigate. + +use std::fmt::{self, Debug}; +use std::path::PathBuf; + +use crate::core::builder::{Builder, Kind, ShouldRun, StepDescription}; + +pub(crate) const PATH_REMAP: &[(&str, &[&str])] = &[ + // bootstrap.toml uses `rust-analyzer-proc-macro-srv`, but the + // actual path is `proc-macro-srv-cli` + ("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]), + // Make `x test tests` function the same as `x t tests/*` + ( + "tests", + &[ + // tidy-alphabetical-start + "tests/assembly-llvm", + "tests/codegen-llvm", + "tests/codegen-units", + "tests/coverage", + "tests/coverage-run-rustdoc", + "tests/crashes", + "tests/debuginfo", + "tests/incremental", + "tests/mir-opt", + "tests/pretty", + "tests/run-make", + "tests/run-make-cargo", + "tests/rustdoc", + "tests/rustdoc-gui", + "tests/rustdoc-js", + "tests/rustdoc-js-std", + "tests/rustdoc-json", + "tests/rustdoc-ui", + "tests/ui", + "tests/ui-fulldeps", + // tidy-alphabetical-end + ], + ), +]; + +pub(crate) fn remap_paths(paths: &mut Vec) { + let mut remove = vec![]; + let mut add = vec![]; + for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s))) + { + for &(search, replace) in PATH_REMAP { + // Remove leading and trailing slashes so `tests/` and `tests` are equivalent + if path.trim_matches(std::path::is_separator) == search { + remove.push(i); + add.extend(replace.iter().map(PathBuf::from)); + break; + } + } + } + remove.sort(); + remove.dedup(); + for idx in remove.into_iter().rev() { + paths.remove(idx); + } + paths.append(&mut add); +} + +#[derive(Clone, PartialEq)] +pub(crate) struct CLIStepPath { + pub(crate) path: PathBuf, + pub(crate) will_be_executed: bool, +} + +#[cfg(test)] +impl CLIStepPath { + pub(crate) fn will_be_executed(mut self, will_be_executed: bool) -> Self { + self.will_be_executed = will_be_executed; + self + } +} + +impl Debug for CLIStepPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.path.display()) + } +} + +impl From for CLIStepPath { + fn from(path: PathBuf) -> Self { + Self { path, will_be_executed: false } + } +} + +pub(crate) fn match_paths_to_steps_and_run( + builder: &Builder<'_>, + v: &[StepDescription], + paths: &[PathBuf], +) { + let should_runs = v + .iter() + .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind))) + .collect::>(); + + // FIXME(Zalathar): This particular check isn't related to path-to-step + // matching, and should probably be hoisted to somewhere much earlier. + if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install) { + eprintln!( + "ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.", + builder.kind.as_str() + ); + crate::exit!(1); + } + + // sanity checks on rules + for (desc, should_run) in v.iter().zip(&should_runs) { + assert!(!should_run.paths.is_empty(), "{:?} should have at least one pathset", desc.name); + } + + if paths.is_empty() || builder.config.include_default_paths { + for (desc, should_run) in v.iter().zip(&should_runs) { + if desc.default && should_run.is_really_default() { + desc.maybe_run(builder, should_run.paths.iter().cloned().collect()); + } + } + } + + // Attempt to resolve paths to be relative to the builder source directory. + let mut paths: Vec = paths + .iter() + .map(|original_path| { + let mut path = original_path.clone(); + + // Someone could run `x ` from a different repository than the source + // directory. + // In that case, we should not try to resolve the paths relative to the working + // directory, but rather relative to the source directory. + // So we forcefully "relocate" the path to the source directory here. + if !path.is_absolute() { + path = builder.src.join(path); + } + + // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy` + if !path.exists() { + // Use the original path here + return original_path.clone(); + } + + // Make the path absolute, strip the prefix, and convert to a PathBuf. + match std::path::absolute(&path) { + Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), + Err(e) => { + eprintln!("ERROR: {e:?}"); + panic!("Due to the above error, failed to resolve path: {path:?}"); + } + } + }) + .collect(); + + remap_paths(&mut paths); + + // Handle all test suite paths. + // (This is separate from the loop below to avoid having to handle multiple paths in `is_suite_path` somehow.) + paths.retain(|path| { + for (desc, should_run) in v.iter().zip(&should_runs) { + if let Some(suite) = should_run.is_suite_path(path) { + desc.maybe_run(builder, vec![suite.clone()]); + return false; + } + } + true + }); + + if paths.is_empty() { + return; + } + + let mut paths: Vec = paths.into_iter().map(|p| p.into()).collect(); + let mut path_lookup: Vec<(CLIStepPath, bool)> = + paths.clone().into_iter().map(|p| (p, false)).collect(); + + // List of `(usize, &StepDescription, Vec)` where `usize` is the closest index of a path + // compared to the given CLI paths. So we can respect to the CLI order by using this value to sort + // the steps. + let mut steps_to_run = vec![]; + + for (desc, should_run) in v.iter().zip(&should_runs) { + let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind); + + // This value is used for sorting the step execution order. + // By default, `usize::MAX` is used as the index for steps to assign them the lowest priority. + // + // If we resolve the step's path from the given CLI input, this value will be updated with + // the step's actual index. + let mut closest_index = usize::MAX; + + // Find the closest index from the original list of paths given by the CLI input. + for (index, (path, is_used)) in path_lookup.iter_mut().enumerate() { + if !*is_used && !paths.contains(path) { + closest_index = index; + *is_used = true; + break; + } + } + + steps_to_run.push((closest_index, desc, pathsets)); + } + + // Sort the steps before running them to respect the CLI order. + steps_to_run.sort_by_key(|(index, _, _)| *index); + + // Handle all PathSets. + for (_index, desc, pathsets) in steps_to_run { + if !pathsets.is_empty() { + desc.maybe_run(builder, pathsets); + } + } + + paths.retain(|p| !p.will_be_executed); + + if !paths.is_empty() { + eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths); + eprintln!( + "HELP: run `x.py {} --help --verbose` to show a list of available paths", + builder.kind.as_str() + ); + eprintln!( + "NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`" + ); + crate::exit!(1); + } +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index b5d0f11236143..c7490c7072bda 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1,7 +1,7 @@ use std::any::{Any, type_name}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::fmt::{self, Debug, Write}; +use std::fmt::{Debug, Write}; use std::hash::Hash; use std::ops::Deref; use std::path::{Path, PathBuf}; @@ -20,6 +20,7 @@ use crate::core::build_steps::tool::RustcPrivateCompilers; use crate::core::build_steps::{ check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, }; +use crate::core::builder::cli_paths::CLIStepPath; use crate::core::config::flags::Subcommand; use crate::core::config::{DryRun, TargetSelection}; use crate::utils::build_stamp::BuildStamp; @@ -29,7 +30,7 @@ use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linke use crate::{Build, Crate, trace}; mod cargo; - +mod cli_paths; #[cfg(test)] mod tests; @@ -424,88 +425,6 @@ impl PathSet { } } -const PATH_REMAP: &[(&str, &[&str])] = &[ - // bootstrap.toml uses `rust-analyzer-proc-macro-srv`, but the - // actual path is `proc-macro-srv-cli` - ("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]), - // Make `x test tests` function the same as `x t tests/*` - ( - "tests", - &[ - // tidy-alphabetical-start - "tests/assembly-llvm", - "tests/codegen-llvm", - "tests/codegen-units", - "tests/coverage", - "tests/coverage-run-rustdoc", - "tests/crashes", - "tests/debuginfo", - "tests/incremental", - "tests/mir-opt", - "tests/pretty", - "tests/run-make", - "tests/run-make-cargo", - "tests/rustdoc", - "tests/rustdoc-gui", - "tests/rustdoc-js", - "tests/rustdoc-js-std", - "tests/rustdoc-json", - "tests/rustdoc-ui", - "tests/ui", - "tests/ui-fulldeps", - // tidy-alphabetical-end - ], - ), -]; - -fn remap_paths(paths: &mut Vec) { - let mut remove = vec![]; - let mut add = vec![]; - for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s))) - { - for &(search, replace) in PATH_REMAP { - // Remove leading and trailing slashes so `tests/` and `tests` are equivalent - if path.trim_matches(std::path::is_separator) == search { - remove.push(i); - add.extend(replace.iter().map(PathBuf::from)); - break; - } - } - } - remove.sort(); - remove.dedup(); - for idx in remove.into_iter().rev() { - paths.remove(idx); - } - paths.append(&mut add); -} - -#[derive(Clone, PartialEq)] -struct CLIStepPath { - path: PathBuf, - will_be_executed: bool, -} - -#[cfg(test)] -impl CLIStepPath { - fn will_be_executed(mut self, will_be_executed: bool) -> Self { - self.will_be_executed = will_be_executed; - self - } -} - -impl Debug for CLIStepPath { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.path.display()) - } -} - -impl From for CLIStepPath { - fn from(path: PathBuf) -> Self { - Self { path, will_be_executed: false } - } -} - impl StepDescription { fn from(kind: Kind) -> StepDescription { StepDescription { @@ -554,144 +473,6 @@ impl StepDescription { } false } - - fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) { - let should_runs = v - .iter() - .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind))) - .collect::>(); - - if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install) - { - eprintln!( - "ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.", - builder.kind.as_str() - ); - crate::exit!(1); - } - - // sanity checks on rules - for (desc, should_run) in v.iter().zip(&should_runs) { - assert!( - !should_run.paths.is_empty(), - "{:?} should have at least one pathset", - desc.name - ); - } - - if paths.is_empty() || builder.config.include_default_paths { - for (desc, should_run) in v.iter().zip(&should_runs) { - if desc.default && should_run.is_really_default() { - desc.maybe_run(builder, should_run.paths.iter().cloned().collect()); - } - } - } - - // Attempt to resolve paths to be relative to the builder source directory. - let mut paths: Vec = paths - .iter() - .map(|original_path| { - let mut path = original_path.clone(); - - // Someone could run `x ` from a different repository than the source - // directory. - // In that case, we should not try to resolve the paths relative to the working - // directory, but rather relative to the source directory. - // So we forcefully "relocate" the path to the source directory here. - if !path.is_absolute() { - path = builder.src.join(path); - } - - // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy` - if !path.exists() { - // Use the original path here - return original_path.clone(); - } - - // Make the path absolute, strip the prefix, and convert to a PathBuf. - match std::path::absolute(&path) { - Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), - Err(e) => { - eprintln!("ERROR: {e:?}"); - panic!("Due to the above error, failed to resolve path: {path:?}"); - } - } - }) - .collect(); - - remap_paths(&mut paths); - - // Handle all test suite paths. - // (This is separate from the loop below to avoid having to handle multiple paths in `is_suite_path` somehow.) - paths.retain(|path| { - for (desc, should_run) in v.iter().zip(&should_runs) { - if let Some(suite) = should_run.is_suite_path(path) { - desc.maybe_run(builder, vec![suite.clone()]); - return false; - } - } - true - }); - - if paths.is_empty() { - return; - } - - let mut paths: Vec = paths.into_iter().map(|p| p.into()).collect(); - let mut path_lookup: Vec<(CLIStepPath, bool)> = - paths.clone().into_iter().map(|p| (p, false)).collect(); - - // List of `(usize, &StepDescription, Vec)` where `usize` is the closest index of a path - // compared to the given CLI paths. So we can respect to the CLI order by using this value to sort - // the steps. - let mut steps_to_run = vec![]; - - for (desc, should_run) in v.iter().zip(&should_runs) { - let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind); - - // This value is used for sorting the step execution order. - // By default, `usize::MAX` is used as the index for steps to assign them the lowest priority. - // - // If we resolve the step's path from the given CLI input, this value will be updated with - // the step's actual index. - let mut closest_index = usize::MAX; - - // Find the closest index from the original list of paths given by the CLI input. - for (index, (path, is_used)) in path_lookup.iter_mut().enumerate() { - if !*is_used && !paths.contains(path) { - closest_index = index; - *is_used = true; - break; - } - } - - steps_to_run.push((closest_index, desc, pathsets)); - } - - // Sort the steps before running them to respect the CLI order. - steps_to_run.sort_by_key(|(index, _, _)| *index); - - // Handle all PathSets. - for (_index, desc, pathsets) in steps_to_run { - if !pathsets.is_empty() { - desc.maybe_run(builder, pathsets); - } - } - - paths.retain(|p| !p.will_be_executed); - - if !paths.is_empty() { - eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths); - eprintln!( - "HELP: run `x.py {} --help --verbose` to show a list of available paths", - builder.kind.as_str() - ); - eprintln!( - "NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`" - ); - crate::exit!(1); - } - } } enum ReallyDefault<'a> { @@ -1349,7 +1130,7 @@ impl<'a> Builder<'a> { } fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) { - StepDescription::run(v, self, paths); + cli_paths::match_paths_to_steps_and_run(self, v, paths); } /// Returns if `std` should be statically linked into `rustc_driver`. diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index e0eb38d04aad0..b8ba1b4c2c340 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -7,6 +7,7 @@ use llvm::prebuilt_llvm_config; use super::*; use crate::Flags; use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::builder::cli_paths::PATH_REMAP; use crate::core::config::Config; use crate::utils::cache::ExecutedStep; use crate::utils::helpers::get_host_target; From 70a9883135d8d8799561f4a6b4fce1ba324e22a5 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Oct 2025 14:03:08 +1100 Subject: [PATCH 09/11] Replace repeated zips with a dedicated `StepExtra` struct --- src/bootstrap/src/core/builder/cli_paths.rs | 25 +++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index de50ecee48ad1..294318f1d494a 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -89,14 +89,25 @@ impl From for CLIStepPath { } } +/// Combines a `StepDescription` with its corresponding `ShouldRun`. +struct StepExtra<'a> { + desc: &'a StepDescription, + should_run: ShouldRun<'a>, +} + pub(crate) fn match_paths_to_steps_and_run( builder: &Builder<'_>, - v: &[StepDescription], + step_descs: &[StepDescription], paths: &[PathBuf], ) { - let should_runs = v + // Obtain `ShouldRun` information for each step, so that we know which + // paths to match it against. + let steps = step_descs .iter() - .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind))) + .map(|desc| StepExtra { + desc, + should_run: (desc.should_run)(ShouldRun::new(builder, desc.kind)), + }) .collect::>(); // FIXME(Zalathar): This particular check isn't related to path-to-step @@ -110,12 +121,12 @@ pub(crate) fn match_paths_to_steps_and_run( } // sanity checks on rules - for (desc, should_run) in v.iter().zip(&should_runs) { + for StepExtra { desc, should_run } in &steps { assert!(!should_run.paths.is_empty(), "{:?} should have at least one pathset", desc.name); } if paths.is_empty() || builder.config.include_default_paths { - for (desc, should_run) in v.iter().zip(&should_runs) { + for StepExtra { desc, should_run } in &steps { if desc.default && should_run.is_really_default() { desc.maybe_run(builder, should_run.paths.iter().cloned().collect()); } @@ -159,7 +170,7 @@ pub(crate) fn match_paths_to_steps_and_run( // Handle all test suite paths. // (This is separate from the loop below to avoid having to handle multiple paths in `is_suite_path` somehow.) paths.retain(|path| { - for (desc, should_run) in v.iter().zip(&should_runs) { + for StepExtra { desc, should_run } in &steps { if let Some(suite) = should_run.is_suite_path(path) { desc.maybe_run(builder, vec![suite.clone()]); return false; @@ -181,7 +192,7 @@ pub(crate) fn match_paths_to_steps_and_run( // the steps. let mut steps_to_run = vec![]; - for (desc, should_run) in v.iter().zip(&should_runs) { + for StepExtra { desc, should_run } in &steps { let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind); // This value is used for sorting the step execution order. From 250ee47708e3612d89389861103d4dbb2644cb88 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Oct 2025 14:26:31 +1100 Subject: [PATCH 10/11] Replace bare tuple with a `StepToRun` struct --- src/bootstrap/src/core/builder/cli_paths.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index 294318f1d494a..aa81c4684eab3 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Debug}; use std::path::PathBuf; -use crate::core::builder::{Builder, Kind, ShouldRun, StepDescription}; +use crate::core::builder::{Builder, Kind, PathSet, ShouldRun, StepDescription}; pub(crate) const PATH_REMAP: &[(&str, &[&str])] = &[ // bootstrap.toml uses `rust-analyzer-proc-macro-srv`, but the @@ -95,6 +95,12 @@ struct StepExtra<'a> { should_run: ShouldRun<'a>, } +struct StepToRun<'a> { + sort_index: usize, + desc: &'a StepDescription, + pathsets: Vec, +} + pub(crate) fn match_paths_to_steps_and_run( builder: &Builder<'_>, step_descs: &[StepDescription], @@ -187,9 +193,8 @@ pub(crate) fn match_paths_to_steps_and_run( let mut path_lookup: Vec<(CLIStepPath, bool)> = paths.clone().into_iter().map(|p| (p, false)).collect(); - // List of `(usize, &StepDescription, Vec)` where `usize` is the closest index of a path - // compared to the given CLI paths. So we can respect to the CLI order by using this value to sort - // the steps. + // Before actually running (non-suite) steps, collect them into a list of structs + // so that we can then sort the list to preserve CLI order as much as possible. let mut steps_to_run = vec![]; for StepExtra { desc, should_run } in &steps { @@ -211,14 +216,14 @@ pub(crate) fn match_paths_to_steps_and_run( } } - steps_to_run.push((closest_index, desc, pathsets)); + steps_to_run.push(StepToRun { sort_index: closest_index, desc, pathsets }); } // Sort the steps before running them to respect the CLI order. - steps_to_run.sort_by_key(|(index, _, _)| *index); + steps_to_run.sort_by_key(|step| step.sort_index); // Handle all PathSets. - for (_index, desc, pathsets) in steps_to_run { + for StepToRun { sort_index: _, desc, pathsets } in steps_to_run { if !pathsets.is_empty() { desc.maybe_run(builder, pathsets); } From 8762219b8fc5d3ab8d48345df6be45d6e63a20e6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Oct 2025 09:51:50 +0000 Subject: [PATCH 11/11] Fix deferred cast checks using the wrong body for determining constness --- compiler/rustc_hir_typeck/src/cast.rs | 6 ++++-- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 11 ++++------- tests/ui/traits/const-traits/issue-103677.cnst.stderr | 9 --------- tests/ui/traits/const-traits/issue-103677.rs | 3 +-- 4 files changed, 9 insertions(+), 20 deletions(-) delete mode 100644 tests/ui/traits/const-traits/issue-103677.cnst.stderr diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 40b21c45bc564..3f13a102684e0 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,7 +32,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -63,6 +63,7 @@ pub(crate) struct CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + pub body_id: LocalDefId, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -244,7 +245,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { span: Span, ) -> Result, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; + let check = + CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, body_id: fcx.body_id }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 4d1c7be391977..7b75d23ed9580 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,5 +1,5 @@ use std::ops::Deref; -use std::{fmt, iter, mem}; +use std::{fmt, iter}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; @@ -72,16 +72,13 @@ pub(crate) enum DivergingBlockBehavior { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { - // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors - // when writing to `self.param_env`. - let mut deferred_cast_checks = mem::take(&mut *self.deferred_cast_checks.borrow_mut()); - + let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut(); debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len()); for cast in deferred_cast_checks.drain(..) { + let body_id = std::mem::replace(&mut self.body_id, cast.body_id); cast.check(self); + self.body_id = body_id; } - - *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks; } pub(in super::super) fn check_asms(&self) { diff --git a/tests/ui/traits/const-traits/issue-103677.cnst.stderr b/tests/ui/traits/const-traits/issue-103677.cnst.stderr deleted file mode 100644 index 845bdb326db11..0000000000000 --- a/tests/ui/traits/const-traits/issue-103677.cnst.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0277]: the trait bound `String: const Deref` is not satisfied - --> $DIR/issue-103677.rs:6:5 - | -LL | &*s as &str; - | ^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/issue-103677.rs b/tests/ui/traits/const-traits/issue-103677.rs index 09f45f5ee3771..8117e393753fd 100644 --- a/tests/ui/traits/const-traits/issue-103677.rs +++ b/tests/ui/traits/const-traits/issue-103677.rs @@ -1,10 +1,9 @@ -//@[stock] check-pass +//@ check-pass //@ revisions: stock cnst #![cfg_attr(cnst, feature(const_trait_impl))] const _: fn(&String) = |s| { &*s as &str; - //[cnst]~^ ERROR: the trait bound `String: const Deref` is not satisfied }; fn main() {}