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 8bcda77f4bc3..a79b341ad1a7 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 @@ fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self { 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 @@ fn handle_rvalue( 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 @@ fn handle_rvalue( 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 @@ fn handle_rvalue( 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 @@ fn handle_rvalue( 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 @@ fn assign_operand( } } 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 @@ fn assign_constant( 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 @@ fn assign_constant( 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 @@ fn assign_constant( } }, &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 @@ fn binary_op( (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 @@ fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Opt 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 @@ fn visit_after_early_statement_effect( 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 @@ fn visit_after_primary_statement_effect( // 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 @@ fn visit_after_early_terminator_effect( 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 a57689a45b67..3fef76b1fc40 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 @@ fn visit_after_early_statement_effect( 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 @@ fn visit_after_early_terminator_effect( 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 b85b82b8f6d9..a55b7a8f6733 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -736,7 +736,7 @@ fn visit_block_end(&mut self, state: &A::Domain) { 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 @@ fn visit_after_early_statement_effect( 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 @@ fn visit_after_primary_statement_effect( 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 @@ fn visit_after_early_terminator_effect( 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 fbb9e4108726..1bc9d2eff8ee 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -53,7 +53,7 @@ fn visit_block_start(&mut self, _state: &A::Domain) {} /// 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 @@ fn visit_after_early_statement_effect( /// 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 @@ fn visit_after_primary_statement_effect( /// 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 @@ fn visit_after_early_terminator_effect( /// 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 2ccd8178e667..fef93c5c5bdc 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 @@ fn visit_after_early_statement_effect( 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 a79b341ad1a7..433100ef95fb 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 @@ fn visit_after_early_statement_effect( #[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 @@ fn visit_after_primary_statement_effect( 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 79c0db7d7283..1dd8fc40006d 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -40,10 +40,10 @@ fn apply_effects_in_range<'tcx, A>( /// 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 @@ fn apply_effects_in_range<'tcx, A>( } 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 @@ fn apply_effects_in_range<'tcx, A>( } 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 1bc9d2eff8ee..9ec59dff0548 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 026826fc379c..a01db72ff7f7 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 @@ fn apply_primary_statement_effect( /// 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 @@ fn apply_call_return_effect( 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 84d5ffb94791..813ceaeb8da9 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -44,7 +44,7 @@ fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Dom } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, loc: Location, @@ -55,7 +55,7 @@ fn apply_early_statement_effect( } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, loc: Location, @@ -66,7 +66,7 @@ fn apply_primary_statement_effect( } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, term: &mir::Terminator<'tcx>, loc: Location, @@ -77,7 +77,7 @@ fn apply_early_terminator_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, term: &'mir mir::Terminator<'tcx>, loc: Location, @@ -92,7 +92,7 @@ fn apply_primary_terminator_effect<'mir>( } fn apply_call_return_effect( - &mut self, + &self, _state: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -533,7 +533,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -542,7 +542,7 @@ fn apply_early_statement_effect( } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &mir::Statement<'tcx>, location: Location, @@ -590,7 +590,7 @@ fn apply_primary_statement_effect( } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -599,7 +599,7 @@ fn apply_early_terminator_effect( } 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 3fef76b1fc40..a33241ebe70a 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -537,13 +537,13 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { 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 664dda8701a6..e6e3948305af 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 @@ fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Doma } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -341,7 +341,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -351,7 +351,7 @@ fn apply_primary_terminator_effect<'mir>( } 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 3f6e7a066192..8799caa3e28f 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 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 @@ pub struct ResultsCursor<'mir, 'tcx, A> 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 @@ pub fn body(&self) -> &'mir mir::Body<'tcx> { 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 @@ pub fn new_owning( 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 @@ fn seek_after(&mut self, target: Location, effect: Effect) { 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 @@ fn seek_after(&mut self, target: Location, effect: Effect) { /// /// 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 1dd8fc40006d..b15b5c07ce38 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 @@ fn apply_effects_in_block<'mir, 'tcx, A>( /// /// `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 @@ fn apply_effects_in_range<'tcx, A>( /// 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 @@ fn apply_effects_in_block<'mir, 'tcx, A>( } 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 @@ fn apply_effects_in_range<'tcx, A>( } 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 @@ fn apply_effects_in_block<'mir, 'tcx, A>( } 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 @@ fn apply_effects_in_range<'tcx, A>( } 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 a55b7a8f6733..f9d9106ba7af 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 @@ 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 @@ struct Formatter<'mir, 'tcx, A> 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 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> { 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 @@ fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { } 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 b6a560360195..9e1fc5a59fe3 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 @@ fn apply_early_statement_effect( /// 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 @@ fn apply_primary_statement_effect( /// 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 @@ fn apply_early_terminator_effect( /// `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 @@ fn apply_primary_terminator_effect<'mir>( /// 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 @@ fn apply_call_return_effect( /// 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 @@ fn get_switch_int_data( /// 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 @@ fn apply_switch_int_edge_effect( /// 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 @@ fn iterate_to_fixpoint<'mir>( state.clone_from(&results[bb]); Self::Direction::apply_effects_in_block( - &mut self, + &self, body, &mut state, bb, @@ -300,7 +300,7 @@ fn iterate_to_fixpoint<'mir>( } 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 23e28a11a452..36d0f3a947bc 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -175,7 +175,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -185,7 +185,7 @@ fn apply_early_statement_effect( } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -195,7 +195,7 @@ fn apply_primary_statement_effect( } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -205,7 +205,7 @@ fn apply_early_terminator_effect( } 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 9ec59dff0548..28ae08d1e6b1 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -7,7 +7,7 @@ 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 a4e4e30a8bb6..331e41bd126b 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 @@ /// 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 @@ fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &Statement<'tcx>, location: Location, @@ -43,7 +42,7 @@ fn apply_primary_statement_effect( } 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 5937d68f389e..9216106b6edd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -376,7 +376,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -400,7 +400,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -429,7 +429,7 @@ fn apply_primary_terminator_effect<'mir>( } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -448,7 +448,7 @@ fn apply_call_return_effect( } fn get_switch_int_data( - &mut self, + &self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, ) -> Option { @@ -460,7 +460,7 @@ fn get_switch_int_data( } fn apply_switch_int_edge_effect( - &mut self, + &self, data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, @@ -513,7 +513,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -527,7 +527,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -545,7 +545,7 @@ fn apply_primary_terminator_effect<'mir>( } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -564,7 +564,7 @@ fn apply_call_return_effect( } fn get_switch_int_data( - &mut self, + &self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, ) -> Option { @@ -580,7 +580,7 @@ fn get_switch_int_data( } fn apply_switch_int_edge_effect( - &mut self, + &self, data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, @@ -627,7 +627,7 @@ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domai #[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 @@ fn apply_primary_statement_effect( #[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 @@ fn apply_primary_terminator_effect<'mir>( } 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 596da18e3d1b..e439f2e052fe 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -41,7 +41,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -50,7 +50,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -60,7 +60,7 @@ fn apply_primary_terminator_effect<'mir>( } fn apply_call_return_effect( - &mut self, + &self, state: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -278,7 +278,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -294,7 +294,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, @@ -304,7 +304,7 @@ fn apply_primary_terminator_effect<'mir>( } 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 a01db72ff7f7..3702f7c55a43 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 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &Statement<'tcx>, _: Location, @@ -98,7 +98,7 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &Statement<'tcx>, _: Location, @@ -144,7 +144,7 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { } fn apply_early_statement_effect( - &mut self, + &self, state: &mut Self::Domain, stmt: &Statement<'tcx>, loc: Location, @@ -177,7 +177,7 @@ fn apply_early_statement_effect( } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, _: &Statement<'tcx>, loc: Location, @@ -188,7 +188,7 @@ fn apply_primary_statement_effect( } fn apply_early_terminator_effect( - &mut self, + &self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, loc: Location, @@ -243,7 +243,7 @@ fn apply_early_terminator_effect( } fn apply_primary_terminator_effect<'t>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'t Terminator<'tcx>, loc: Location, @@ -284,7 +284,7 @@ fn apply_primary_terminator_effect<'t>( } 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 fef93c5c5bdc..1fc109533ca2 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 433100ef95fb..f849a788c361 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 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { 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 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location, @@ -123,7 +123,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, state: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, _location: Location, @@ -136,7 +136,7 @@ fn apply_primary_terminator_effect<'mir>( } 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 82f9dfe4745d..840bb169a1fa 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1160,7 +1160,7 @@ fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) { } fn apply_primary_statement_effect( - &mut self, + &self, trans: &mut Self::Domain, statement: &Statement<'tcx>, location: Location, @@ -1169,7 +1169,7 @@ fn apply_primary_statement_effect( } fn apply_primary_terminator_effect<'mir>( - &mut self, + &self, trans: &mut Self::Domain, terminator: &'mir Terminator<'tcx>, location: Location, @@ -1179,7 +1179,7 @@ fn apply_primary_terminator_effect<'mir>( } 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 a33241ebe70a..d4d38301a158 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -49,7 +49,7 @@ 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 visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { 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 8799caa3e28f..3c56999fcbdc 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -42,8 +42,7 @@ pub struct ResultsCursor<'mir, 'tcx, A> 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 @@ pub fn body(&self) -> &'mir mir::Body<'tcx> { 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 @@ fn new( } /// 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 @@ pub(crate) fn allow_unreachable(&mut self) { /// 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 @@ pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) { #[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 @@ fn seek_after(&mut self, target: Location, effect: Effect) { 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 @@ fn seek_after(&mut self, target: Location, effect: Effect) { /// 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 f9d9106ba7af..22bff3806b15 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -32,8 +32,7 @@ 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 @@ pub(super) fn write_graphviz_results<'tcx, A>( 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 @@ struct Formatter<'mir, 'tcx, A> 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 @@ fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { } 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 @@ fn run<'tcx, A>( 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 9e1fc5a59fe3..60b3c15d80d7 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -58,8 +58,7 @@ 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 @@ fn iterate_to_fixpoint<'mir>( 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 @@ fn iterate_to_fixpoint<'mir>( 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 @@ fn iterate_to_fixpoint<'mir>( 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 @@ fn iterate_to_fixpoint<'mir>( ); } + 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 7b7e981d3a55..76b6cc47dde4 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -5,26 +5,26 @@ 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 36d0f3a947bc..85f23b8332a1 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 @@ fn mock_entry_set(&self, bb: BasicBlock) -> DenseBitSet { 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 28ae08d1e6b1..46940c6ab62f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -7,13 +7,12 @@ 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 2e8c91654416..485925e7b50c 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -17,8 +17,9 @@ 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 1fc109533ca2..069fcfefac21 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 f849a788c361..8532e1e9d7cc 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 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // 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 a10e0b82467c..8c2149ef96fb 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::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 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { 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 000000000000..845bdb326db1 --- /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 c032cc7a6880..09f45f5ee377 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 000000000000..de50ecee48ad --- /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 b5d0f1123614..c7490c7072bd 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::{ 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::{Build, Crate, trace}; mod cargo; - +mod cli_paths; #[cfg(test)] mod tests; @@ -424,88 +425,6 @@ pub fn assert_single_path(&self) -> &TaskPath { } } -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 @@ fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool { } 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 @@ pub fn doc_rust_lang_org_channel(&self) -> String { } 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 e0eb38d04aad..b8ba1b4c2c34 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -7,6 +7,7 @@ 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 de50ecee48ad..294318f1d494 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -89,14 +89,25 @@ fn from(path: PathBuf) -> Self { } } +/// 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 294318f1d494..aa81c4684eab 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 40b21c45bc56..3f13a102684e 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,7 +32,7 @@ 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 @@ pub(crate) fn new( 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 4d1c7be39197..7b75d23ed958 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 845bdb326db1..000000000000 --- 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 09f45f5ee377..8117e393753f 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() {}