diff options
Diffstat (limited to 'compiler/rustc_mir_dataflow')
18 files changed, 601 insertions, 464 deletions
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index d615c83d6..0540a5e94 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -409,14 +409,21 @@ where self.drop_ladder(fields, succ, unwind).0 } + /// Drops the T contained in a `Box<T>` if it has not been moved out of #[instrument(level = "debug", ret)] - fn open_drop_for_box(&mut self, adt: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> BasicBlock { + fn open_drop_for_box_contents( + &mut self, + adt: ty::AdtDef<'tcx>, + substs: SubstsRef<'tcx>, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { // drop glue is sent straight to codegen // box cannot be directly dereferenced let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), substs); let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant(); let nonnull_ty = unique_variant.fields[FieldIdx::from_u32(0)].ty(self.tcx(), substs); - let ptr_ty = self.tcx().mk_imm_ptr(substs[0].expect_ty()); + let ptr_ty = Ty::new_imm_ptr(self.tcx(), substs[0].expect_ty()); let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::new(0), unique_ty); let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::new(0), nonnull_ty); @@ -425,11 +432,7 @@ where let interior_path = self.elaborator.deref_subpath(self.path); - let succ = self.box_free_block(adt, substs, self.succ, self.unwind); - let unwind_succ = - self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup)); - - self.drop_subpath(interior, interior_path, succ, unwind_succ) + self.drop_subpath(interior, interior_path, succ, unwind) } #[instrument(level = "debug", ret)] @@ -453,7 +456,15 @@ where self.open_drop_for_adt_contents(adt, substs) }; - if adt.has_dtor(self.tcx()) { + if adt.is_box() { + // we need to drop the inside of the box before running the destructor + let succ = self.destructor_call_block(contents_drop); + let unwind = contents_drop + .1 + .map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup))); + + self.open_drop_for_box_contents(adt, substs, succ, unwind) + } else if adt.has_dtor(self.tcx()) { self.destructor_call_block(contents_drop) } else { contents_drop.0 @@ -617,17 +628,20 @@ where let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; let ty = self.place_ty(self.place); - let ref_ty = - tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }); + let ref_ty = Ty::new_ref( + tcx, + tcx.lifetimes.re_erased, + ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }, + ); let ref_place = self.new_temp(ref_ty); - let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); + let unit_temp = Place::from(self.new_temp(Ty::new_unit(tcx))); let result = BasicBlockData { statements: vec![self.assign( Place::from(ref_place), Rvalue::Ref( tcx.lifetimes.re_erased, - BorrowKind::Mut { allow_two_phase_borrow: false }, + BorrowKind::Mut { kind: MutBorrowKind::Default }, self.place, ), )], @@ -643,14 +657,20 @@ where destination: unit_temp, target: Some(succ), unwind: unwind.into_action(), - from_hir_call: true, + call_source: CallSource::Misc, fn_span: self.source_info.span, }, source_info: self.source_info, }), is_cleanup: unwind.is_cleanup(), }; - self.elaborator.patch().new_block(result) + + let destructor_block = self.elaborator.patch().new_block(result); + + let block_start = Location { block: destructor_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + + self.drop_flag_test_block(destructor_block, succ, unwind) } /// Create a loop that drops an array: @@ -676,7 +696,7 @@ where let move_ = |place: Place<'tcx>| Operand::Move(place); let tcx = self.tcx(); - let ptr_ty = tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); + let ptr_ty = Ty::new_ptr(tcx, ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); let ptr = Place::from(self.new_temp(ptr_ty)); let can_go = Place::from(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); @@ -851,13 +871,7 @@ where self.open_drop_for_tuple(&tys) } ty::Tuple(fields) => self.open_drop_for_tuple(fields), - ty::Adt(def, substs) => { - if def.is_box() { - self.open_drop_for_box(*def, substs) - } else { - self.open_drop_for_adt(*def, substs) - } - } + ty::Adt(def, substs) => self.open_drop_for_adt(*def, substs), ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), ty::Array(ety, size) => { let size = size.try_eval_target_usize(self.tcx(), self.elaborator.param_env()); @@ -905,65 +919,6 @@ where blk } - /// Creates a block that frees the backing memory of a `Box` if its drop is required (either - /// statically or by checking its drop flag). - /// - /// The contained value will not be dropped. - fn box_free_block( - &mut self, - adt: ty::AdtDef<'tcx>, - substs: SubstsRef<'tcx>, - target: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - let block = self.unelaborated_free_block(adt, substs, target, unwind); - self.drop_flag_test_block(block, target, unwind) - } - - /// Creates a block that frees the backing memory of a `Box` (without dropping the contained - /// value). - fn unelaborated_free_block( - &mut self, - adt: ty::AdtDef<'tcx>, - substs: SubstsRef<'tcx>, - target: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - let tcx = self.tcx(); - let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); - let free_func = tcx.require_lang_item(LangItem::BoxFree, Some(self.source_info.span)); - let args = adt - .variant(FIRST_VARIANT) - .fields - .iter() - .enumerate() - .map(|(i, f)| { - let field = FieldIdx::new(i); - let field_ty = f.ty(tcx, substs); - Operand::Move(tcx.mk_place_field(self.place, field, field_ty)) - }) - .collect(); - - let call = TerminatorKind::Call { - func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), - args, - destination: unit_temp, - target: Some(target), - unwind: if unwind.is_cleanup() { - UnwindAction::Terminate - } else { - UnwindAction::Continue - }, - from_hir_call: false, - fn_span: self.source_info.span, - }; // FIXME(#43234) - let free_block = self.new_block(unwind, call); - - let block_start = Location { block: free_block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); - free_block - } - fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = TerminatorKind::Drop { place: self.place, diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index f3b5544aa..c978bddfe 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,18 +1,60 @@ //! Random access inspection of the results of a dataflow analysis. -use crate::framework::BitSetExt; +use crate::{framework::BitSetExt, CloneAnalysis}; -use std::borrow::Borrow; +use std::borrow::{Borrow, BorrowMut}; use std::cmp::Ordering; #[cfg(debug_assertions)] use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Effect, EffectIndex, Results}; +use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned}; + +// `AnalysisResults` is needed as an impl such as the following has an unconstrained type +// parameter: +// ``` +// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R> +// where +// A: Analysis<'tcx>, +// E: Borrow<EntrySets<'tcx, A>>, +// R: Results<'tcx, A, E>, +// {} +// ``` + +/// A type representing the analysis results consumed by a `ResultsCursor`. +pub trait AnalysisResults<'tcx, A>: BorrowMut<Results<'tcx, A, Self::EntrySets>> +where + A: Analysis<'tcx>, +{ + /// The type containing the entry sets for this `Results` type. + /// + /// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`. + type EntrySets: Borrow<EntrySets<'tcx, A>>; +} +impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E> +where + A: Analysis<'tcx>, + E: Borrow<EntrySets<'tcx, A>>, +{ + type EntrySets = E; +} +impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E> +where + A: Analysis<'tcx>, + E: Borrow<EntrySets<'tcx, A>>, +{ + type EntrySets = E; +} /// A `ResultsCursor` that borrows the underlying `Results`. -pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; +pub type ResultsRefCursor<'res, 'mir, 'tcx, A> = + ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>; + +/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This +/// allows multiple cursors over the same `Results`. +pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> = + ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>; /// Allows random access inspection of the results of a dataflow analysis. /// @@ -45,7 +87,38 @@ where impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> where A: Analysis<'tcx>, - R: Borrow<Results<'tcx, A>>, +{ + /// Returns the dataflow state at the current location. + pub fn get(&self) -> &A::Domain { + &self.state + } + + /// Returns the body this analysis was run on. + pub fn body(&self) -> &'mir mir::Body<'tcx> { + self.body + } + + /// Unwraps this cursor, returning the underlying `Results`. + pub fn into_results(self) -> R { + self.results + } +} + +impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new cursor over the same `Results`. Note that the cursor's position is *not* + /// copied. + pub fn new_cursor(&self) -> Self { + Self::new(self.body, self.results.reclone_analysis()) + } +} + +impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx>, + R: AnalysisResults<'tcx, A>, { /// Returns a new cursor that can inspect `results`. pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { @@ -74,8 +147,13 @@ where } /// Returns the underlying `Results`. - pub fn results(&self) -> &Results<'tcx, A> { - &self.results.borrow() + pub fn results(&mut self) -> &Results<'tcx, A, R::EntrySets> { + self.results.borrow() + } + + /// Returns the underlying `Results`. + pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> { + self.results.borrow_mut() } /// Returns the `Analysis` used to generate the underlying `Results`. @@ -83,9 +161,14 @@ where &self.results.borrow().analysis } - /// Returns the dataflow state at the current location. - pub fn get(&self) -> &A::Domain { - &self.state + /// Returns the `Analysis` used to generate the underlying `Results`. + pub fn mut_analysis(&mut self) -> &mut A { + &mut self.results.borrow_mut().analysis + } + + /// Returns both the dataflow state at the current location and the `Analysis`. + pub fn get_with_analysis(&mut self) -> (&A::Domain, &mut A) { + (&self.state, &mut self.results.borrow_mut().analysis) } /// Resets the cursor to hold the entry set for the given basic block. @@ -97,7 +180,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.clone_from(&self.results.borrow().entry_set_for_block(block)); + self.state.clone_from(self.results.borrow().entry_set_for_block(block)); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -186,7 +269,7 @@ where ) }; - let analysis = &self.results.borrow().analysis; + let analysis = &mut self.results.borrow_mut().analysis; let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( @@ -205,8 +288,8 @@ where /// /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { - f(&self.results.borrow().analysis, &mut self.state); + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) { + f(&mut self.results.borrow_mut().analysis, &mut self.state); self.state_needs_reset = true; } } @@ -215,7 +298,6 @@ impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> where A: crate::GenKillAnalysis<'tcx>, A::Domain: BitSetExt<A::Idx>, - R: Borrow<Results<'tcx, A>>, { pub fn contains(&self, elem: A::Idx) -> bool { self.get().contains(elem) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index ba328e780..804b44a6b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -16,7 +16,7 @@ pub trait Direction { /// /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -25,7 +25,7 @@ pub trait Direction { A: Analysis<'tcx>; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -33,7 +33,7 @@ pub trait Direction { A: Analysis<'tcx>; fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet<A::Idx>, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -44,13 +44,13 @@ pub trait Direction { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>; fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -67,7 +67,7 @@ impl Direction for Backward { const IS_FORWARD: bool = false; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -87,7 +87,7 @@ impl Direction for Backward { } fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet<A::Idx>, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -107,7 +107,7 @@ impl Direction for Backward { } fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -187,36 +187,36 @@ impl Direction for Backward { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>, { results.reset_to_block_entry(state, block); - vis.visit_block_end(&state, block_data, block); + vis.visit_block_end(results, &state, block_data, block); // Terminator let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.reconstruct_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(results, state, term, loc); results.reconstruct_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(results, state, term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(results, state, stmt, loc); results.reconstruct_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(results, state, stmt, loc); } - vis.visit_block_start(state, block_data, block); + vis.visit_block_start(results, state, block_data, block); } fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, _tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -319,7 +319,7 @@ impl Direction for Forward { const IS_FORWARD: bool = true; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -339,7 +339,7 @@ impl Direction for Forward { } fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet<A::Idx>, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -359,7 +359,7 @@ impl Direction for Forward { } fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -435,35 +435,35 @@ impl Direction for Forward { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>, { results.reset_to_block_entry(state, block); - vis.visit_block_start(state, block_data, block); + vis.visit_block_start(results, state, block_data, block); for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(results, state, stmt, loc); results.reconstruct_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(results, state, stmt, loc); } let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.reconstruct_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(results, state, term, loc); results.reconstruct_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(results, state, term, loc); - vis.visit_block_end(state, block_data, block); + vis.visit_block_end(results, state, block_data, block); } fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, _tcx: TyCtxt<'tcx>, _body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -502,15 +502,7 @@ impl Direction for Forward { propagate(target, exit_state); } - Call { - unwind, - destination, - target, - func: _, - args: _, - from_hir_call: _, - fn_span: _, - } => { + Call { unwind, destination, target, func: _, args: _, call_source: _, fn_span: _ } => { if let UnwindAction::Cleanup(unwind) = unwind { propagate(unwind, exit_state); } diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 3e8f792e6..c755d7588 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -5,7 +5,9 @@ use crate::errors::{ }; use crate::framework::BitSetExt; +use std::borrow::Borrow; use std::ffi::OsString; +use std::marker::PhantomData; use std::path::PathBuf; use rustc_ast as ast; @@ -22,54 +24,108 @@ use rustc_span::symbol::{sym, Symbol}; use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, - ResultsCursor, ResultsVisitor, + visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis, + GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, + ResultsVisitor, }; +pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>; + /// A dataflow analysis that has converged to fixpoint. -pub struct Results<'tcx, A> +pub struct Results<'tcx, A, E = EntrySets<'tcx, A>> where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>, + pub(super) entry_sets: E, + pub(super) _marker: PhantomData<&'tcx ()>, } -impl<'tcx, A> Results<'tcx, A> +/// `Results` type with a cloned `Analysis` and borrowed entry sets. +pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>; + +impl<'tcx, A, E> Results<'tcx, A, E> where A: Analysis<'tcx>, + E: Borrow<EntrySets<'tcx, A>>, { /// Creates a `ResultsCursor` that can inspect these `Results`. pub fn into_results_cursor<'mir>( self, body: &'mir mir::Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A> { + ) -> ResultsCursor<'mir, 'tcx, A, Self> { ResultsCursor::new(body, self) } /// Gets the dataflow state for the given block. pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { - &self.entry_sets[block] + &self.entry_sets.borrow()[block] } pub fn visit_with<'mir>( - &self, + &mut self, body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator<Item = BasicBlock>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>, ) { visit_results(body, blocks, self, vis) } pub fn visit_reachable_with<'mir>( - &self, + &mut self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>, ) { let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } } +impl<'tcx, A> Results<'tcx, A> +where + A: Analysis<'tcx>, +{ + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn as_results_cursor<'a, 'mir>( + &'a mut self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsRefCursor<'a, 'mir, 'tcx, A> { + ResultsCursor::new(body, self) + } +} +impl<'tcx, A> Results<'tcx, A> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. + pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> { + Results { + analysis: self.analysis.clone_analysis(), + entry_sets: &self.entry_sets, + _marker: PhantomData, + } + } + + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn cloned_results_cursor<'mir>( + &self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> { + self.clone_analysis().into_results_cursor(body) + } +} +impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. + pub fn reclone_analysis(&self) -> Self { + Results { + analysis: self.analysis.clone_analysis(), + entry_sets: self.entry_sets, + _marker: PhantomData, + } + } +} /// A solver for dataflow problems. pub struct Engine<'a, 'tcx, A> @@ -98,7 +154,7 @@ where T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. - pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self { + pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, mut analysis: A) -> Self { // If there are no back-edges in the control-flow graph, we only ever need to apply the // transfer function for each block exactly once (assuming that we process blocks in RPO). // @@ -114,7 +170,7 @@ where for (block, block_data) in body.basic_blocks.iter_enumerated() { let trans = &mut trans_for_block[block]; - A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); + A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data); } let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| { @@ -171,7 +227,13 @@ where A::Domain: DebugWithContext<A>, { let Engine { - analysis, body, mut entry_sets, tcx, apply_trans_for_block, pass_name, .. + mut analysis, + body, + mut entry_sets, + tcx, + apply_trans_for_block, + pass_name, + .. } = self; let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len()); @@ -203,11 +265,13 @@ where // Apply the block transfer function, using the cached one if it exists. match &apply_trans_for_block { Some(apply) => apply(bb, &mut state), - None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), + None => { + A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data) + } } A::Direction::join_state_into_successors_of( - &analysis, + &mut analysis, tcx, body, &mut state, @@ -221,11 +285,13 @@ where ); } - let results = Results { analysis, entry_sets }; + let mut results = Results { analysis, entry_sets, _marker: PhantomData }; - let res = write_graphviz_results(tcx, &body, &results, pass_name); - if let Err(e) = res { - error!("Failed to write graphviz dataflow results: {}", e); + if tcx.sess.opts.unstable_opts.dump_mir_dataflow { + let res = write_graphviz_results(tcx, &body, &mut results, pass_name); + if let Err(e) = res { + error!("Failed to write graphviz dataflow results: {}", e); + } } results @@ -235,11 +301,11 @@ where // Graphviz /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via -/// `rustc_mir` attributes. +/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, pass_name: Option<&'static str>, ) -> std::io::Result<()> where @@ -264,9 +330,7 @@ where io::BufWriter::new(fs::File::create(&path)?) } - None if tcx.sess.opts.unstable_opts.dump_mir_dataflow - && dump_enabled(tcx, A::NAME, def_id) => - { + None if dump_enabled(tcx, A::NAME, def_id) => { create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 707729f8f..e331533c3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -1,6 +1,7 @@ //! A helpful diagram for debugging dataflow problems. use std::borrow::Cow; +use std::cell::RefCell; use std::sync::OnceLock; use std::{io, ops, str}; @@ -28,23 +29,27 @@ impl OutputStyle { } } -pub struct Formatter<'a, 'tcx, A> +pub struct Formatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - body: &'a Body<'tcx>, - results: &'a Results<'tcx, A>, + body: &'mir Body<'tcx>, + results: RefCell<&'res mut Results<'tcx, A>>, style: OutputStyle, reachable: BitSet<BasicBlock>, } -impl<'a, 'tcx, A> Formatter<'a, 'tcx, A> +impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self { + pub fn new( + body: &'mir Body<'tcx>, + results: &'res mut Results<'tcx, A>, + style: OutputStyle, + ) -> Self { let reachable = mir::traversal::reachable_as_bitset(body); - Formatter { body, results, style, reachable } + Formatter { body, results: results.into(), style, reachable } } } @@ -64,7 +69,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> { .collect() } -impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A> +impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, @@ -83,13 +88,14 @@ where fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { let mut label = Vec::new(); + let mut results = self.results.borrow_mut(); let mut fmt = BlockFormatter { - results: ResultsRefCursor::new(self.body, self.results), + results: results.as_results_cursor(self.body), style: self.style, bg: Background::Light, }; - fmt.write_node_label(&mut label, self.body, *block).unwrap(); + fmt.write_node_label(&mut label, *block).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -103,7 +109,7 @@ where } } -impl<'a, 'tcx, A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> +impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -137,16 +143,16 @@ where } } -struct BlockFormatter<'a, 'tcx, A> +struct BlockFormatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - results: ResultsRefCursor<'a, 'a, 'tcx, A>, + results: ResultsRefCursor<'res, 'mir, 'tcx, A>, bg: Background, style: OutputStyle, } -impl<'a, 'tcx, A> BlockFormatter<'a, 'tcx, A> +impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, @@ -159,12 +165,7 @@ where bg } - fn write_node_label( - &mut self, - w: &mut impl io::Write, - body: &'a Body<'tcx>, - block: BasicBlock, - ) -> io::Result<()> { + fn write_node_label(&mut self, w: &mut impl io::Write, block: BasicBlock) -> io::Result<()> { // Sample output: // +-+-----------------------------------------------+ // A | bb4 | @@ -215,11 +216,11 @@ where self.write_row_with_full_state(w, "", "(on start)")?; // D + E: Statement and terminator transfer functions - self.write_statements_and_terminator(w, body, block)?; + self.write_statements_and_terminator(w, block)?; // F: State at end of block - let terminator = body[block].terminator(); + let terminator = self.results.body()[block].terminator(); // Write the full dataflow state immediately after the terminator if it differs from the // state at block entry. @@ -389,10 +390,14 @@ where fn write_statements_and_terminator( &mut self, w: &mut impl io::Write, - body: &'a Body<'tcx>, block: BasicBlock, ) -> io::Result<()> { - let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style); + let diffs = StateDiffCollector::run( + self.results.body(), + block, + self.results.mut_results(), + self.style, + ); let mut diffs_before = diffs.before.map(|v| v.into_iter()); let mut diffs_after = diffs.after.into_iter(); @@ -401,7 +406,7 @@ where if A::Direction::IS_FORWARD { it.next().unwrap() } else { it.next_back().unwrap() } }; - for (i, statement) in body[block].statements.iter().enumerate() { + for (i, statement) in self.results.body()[block].statements.iter().enumerate() { let statement_str = format!("{statement:?}"); let index_str = format!("{i}"); @@ -423,7 +428,7 @@ where assert!(diffs_after.is_empty()); assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty)); - let terminator = body[block].terminator(); + let terminator = self.results.body()[block].terminator(); let mut terminator_str = String::new(); terminator.kind.fmt_head(&mut terminator_str).unwrap(); @@ -492,29 +497,24 @@ where } } -struct StateDiffCollector<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - analysis: &'a A, - prev_state: A::Domain, +struct StateDiffCollector<D> { + prev_state: D, before: Option<Vec<String>>, after: Vec<String>, } -impl<'a, 'tcx, A> StateDiffCollector<'a, 'tcx, A> -where - A: Analysis<'tcx>, - A::Domain: DebugWithContext<A>, -{ - fn run( - body: &'a mir::Body<'tcx>, +impl<D> StateDiffCollector<D> { + fn run<'tcx, A>( + body: &mir::Body<'tcx>, block: BasicBlock, - results: &'a Results<'tcx, A>, + results: &mut Results<'tcx, A>, style: OutputStyle, - ) -> Self { + ) -> Self + where + A: Analysis<'tcx, Domain = D>, + D: DebugWithContext<A>, + { let mut collector = StateDiffCollector { - analysis: &results.analysis, prev_state: results.analysis.bottom_value(body), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), @@ -525,7 +525,7 @@ where } } -impl<'a, 'tcx, A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A> +impl<'tcx, A> ResultsVisitor<'_, 'tcx, Results<'tcx, A>> for StateDiffCollector<A::Domain> where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, @@ -534,6 +534,7 @@ where fn visit_block_start( &mut self, + _results: &Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -545,6 +546,7 @@ where fn visit_block_end( &mut self, + _results: &Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -556,45 +558,49 @@ where fn visit_statement_before_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, self.analysis)); + before.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } fn visit_statement_after_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } fn visit_terminator_before_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, self.analysis)); + before.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } fn visit_terminator_after_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index f2263007f..58df9b9a7 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -45,9 +45,9 @@ pub mod graphviz; pub mod lattice; mod visitor; -pub use self::cursor::{ResultsCursor, ResultsRefCursor}; +pub use self::cursor::{AnalysisResults, ResultsClonedCursor, ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; -pub use self::engine::{Engine, Results}; +pub use self::engine::{Engine, EntrySets, Results, ResultsCloned}; pub use self::lattice::{JoinSemiLattice, MeetSemiLattice}; pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor}; @@ -146,7 +146,7 @@ pub trait AnalysisDomain<'tcx> { pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// Updates the current dataflow state with the effect of evaluating a statement. fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -159,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, /// analyses should not implement this without also implementing `apply_statement_effect`. fn apply_before_statement_effect( - &self, + &mut self, _state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -173,7 +173,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// `InitializedPlaces` analyses, the return place for a function call is not marked as /// initialized here. fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -186,7 +186,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, /// analyses should not implement this without also implementing `apply_terminator_effect`. fn apply_before_terminator_effect( - &self, + &mut self, _state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -201,7 +201,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// This is separate from `apply_terminator_effect` to properly track state across unwind /// edges. fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -214,7 +214,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// /// By default, no effects happen. fn apply_yield_resume_effect( - &self, + &mut self, _state: &mut Self::Domain, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, @@ -235,7 +235,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// engine doesn't need to clone the exit state for a block unless /// `SwitchIntEdgeEffects::apply` is actually called. fn apply_switch_int_edge_effects( - &self, + &mut self, _block: BasicBlock, _discr: &mir::Operand<'tcx>, _apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, @@ -269,6 +269,21 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { } } +/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or +/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new +/// `ResultsCursor` or `ResultsVisitor` +pub trait CloneAnalysis { + fn clone_analysis(&self) -> Self; +} +impl<'tcx, A> CloneAnalysis for A +where + A: Analysis<'tcx> + Copy, +{ + fn clone_analysis(&self) -> Self { + *self + } +} + /// A gen/kill dataflow problem. /// /// Each method in this trait has a corresponding one in `Analysis`. However, these methods only @@ -282,7 +297,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_statement_effect`. fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, statement: &mir::Statement<'tcx>, location: Location, @@ -290,7 +305,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_before_statement_effect`. fn before_statement_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _statement: &mir::Statement<'tcx>, _location: Location, @@ -299,7 +314,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_terminator_effect`. fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, location: Location, @@ -307,7 +322,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_before_terminator_effect`. fn before_terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -318,7 +333,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_call_return_effect`. fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -326,7 +341,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_yield_resume_effect`. fn yield_resume_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, @@ -335,7 +350,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_switch_int_edge_effects`. fn switch_int_edge_effects<G: GenKill<Self::Idx>>( - &self, + &mut self, _block: BasicBlock, _discr: &mir::Operand<'tcx>, _edge_effects: &mut impl SwitchIntEdgeEffects<G>, @@ -349,7 +364,7 @@ where A::Domain: GenKill<A::Idx> + BitSetExt<A::Idx>, { fn apply_statement_effect( - &self, + &mut self, state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -358,7 +373,7 @@ where } fn apply_before_statement_effect( - &self, + &mut self, state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -367,7 +382,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -376,7 +391,7 @@ where } fn apply_before_terminator_effect( - &self, + &mut self, state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -387,7 +402,7 @@ where /* Edge-specific effects */ fn apply_call_return_effect( - &self, + &mut self, state: &mut A::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -396,7 +411,7 @@ where } fn apply_yield_resume_effect( - &self, + &mut self, state: &mut A::Domain, resume_block: BasicBlock, resume_place: mir::Place<'tcx>, @@ -405,7 +420,7 @@ where } fn apply_switch_int_edge_effects( - &self, + &mut self, block: BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>, diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 0fed305b9..cb0ec144e 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -40,7 +40,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { destination: dummy_place.clone(), target: Some(mir::START_BLOCK), unwind: mir::UnwindAction::Continue, - from_hir_call: false, + call_source: mir::CallSource::Misc, fn_span: DUMMY_SP, }, ); @@ -54,7 +54,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { destination: dummy_place.clone(), target: Some(mir::START_BLOCK), unwind: mir::UnwindAction::Continue, - from_hir_call: false, + call_source: mir::CallSource::Misc, fn_span: DUMMY_SP, }, ); @@ -179,7 +179,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -189,7 +189,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_before_statement_effect( - &self, + &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -199,7 +199,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -209,7 +209,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_before_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -219,7 +219,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_call_return_effect( - &self, + &mut self, _state: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -266,7 +266,8 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData } + .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 75b4e150a..76a729827 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -1,16 +1,18 @@ +use std::borrow::Borrow; + use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Results}; +use super::{Analysis, Direction, EntrySets, Results}; /// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the /// dataflow state at that location. -pub fn visit_results<'mir, 'tcx, F, V>( +pub fn visit_results<'mir, 'tcx, F, R>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator<Item = BasicBlock>, - results: &V, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where - V: ResultsVisitable<'tcx, FlowState = F>, + R: ResultsVisitable<'tcx, FlowState = F>, { let mut state = results.new_flow_state(body); @@ -22,15 +24,18 @@ pub fn visit_results<'mir, 'tcx, F, V>( assert!(reachable_blocks.contains(block)); let block_data = &body[block]; - V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); + R::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); } } -pub trait ResultsVisitor<'mir, 'tcx> { +/// A visitor over the results of an `Analysis`. The type parameter `R` is the results type being +/// visited. +pub trait ResultsVisitor<'mir, 'tcx, R> { type FlowState; fn visit_block_start( &mut self, + _results: &R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -41,6 +46,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// its `statement_effect`. fn visit_statement_before_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -51,6 +57,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// statement applied to `state`. fn visit_statement_after_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -61,6 +68,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// its `terminator_effect`. fn visit_terminator_before_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -73,6 +81,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// The `call_return_effect` (if one exists) will *not* be applied to `state`. fn visit_terminator_after_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -81,6 +90,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { fn visit_block_end( &mut self, + _results: &R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -105,37 +115,38 @@ pub trait ResultsVisitable<'tcx> { fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock); fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, statement: &mir::Statement<'tcx>, location: Location, ); fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, statement: &mir::Statement<'tcx>, location: Location, ); fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, terminator: &mir::Terminator<'tcx>, location: Location, ); fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, terminator: &mir::Terminator<'tcx>, location: Location, ); } -impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> +impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E> where A: Analysis<'tcx>, + E: Borrow<EntrySets<'tcx, A>>, { type FlowState = A::Domain; @@ -146,11 +157,11 @@ where } fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.clone_from(&self.entry_set_for_block(block)); + state.clone_from(self.entry_set_for_block(block)); } fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -159,7 +170,7 @@ where } fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -168,7 +179,7 @@ where } fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -177,7 +188,7 @@ where } fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 92d30f254..b88ed32b6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -10,6 +10,7 @@ use rustc_middle::mir::*; /// 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 generators. +#[derive(Clone, Copy)] pub struct MaybeBorrowedLocals; impl MaybeBorrowedLocals { @@ -36,7 +37,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, statement: &mir::Statement<'tcx>, location: Location, @@ -45,7 +46,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, location: Location, @@ -54,7 +55,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _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 6ae6bdc17..9662c1977 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -21,6 +21,7 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi /// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals /// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +#[derive(Clone, Copy)] pub struct MaybeLiveLocals; impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { @@ -43,7 +44,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, statement: &mir::Statement<'tcx>, location: Location, @@ -52,7 +53,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, location: Location, @@ -61,7 +62,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -74,7 +75,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn yield_resume_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _resume_block: mir::BasicBlock, resume_place: mir::Place<'tcx>, @@ -215,6 +216,7 @@ impl DefUse { /// This is basically written for dead store elimination and nothing else. /// /// All of the caveats of `MaybeLiveLocals` apply. +#[derive(Clone, Copy)] pub struct MaybeTransitiveLiveLocals<'a> { always_live: &'a BitSet<Local>, } @@ -247,7 +249,7 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> { impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { fn apply_statement_effect( - &self, + &mut self, trans: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -282,7 +284,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_terminator_effect( - &self, + &mut self, trans: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -291,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_call_return_effect( - &self, + &mut self, trans: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -304,7 +306,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_yield_resume_effect( - &self, + &mut self, trans: &mut Self::Domain, _resume_block: mir::BasicBlock, resume_place: mir::Place<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 171db6965..98cec1c67 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -306,7 +306,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, statement: &mir::Statement<'tcx>, location: Location, @@ -329,7 +329,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, location: Location, @@ -351,7 +351,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -372,7 +372,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn switch_int_edge_effects<G: GenKill<Self::Idx>>( - &self, + &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects<G>, @@ -442,7 +442,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _statement: &mir::Statement<'tcx>, location: Location, @@ -456,7 +456,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -467,7 +467,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -488,7 +488,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn switch_int_edge_effects<G: GenKill<Self::Idx>>( - &self, + &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects<G>, @@ -562,7 +562,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _statement: &mir::Statement<'tcx>, location: Location, @@ -573,7 +573,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -584,7 +584,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -627,7 +627,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, trans), level = "debug")] fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, location: Location, @@ -651,7 +651,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, trans, _terminator), level = "debug")] fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -672,7 +672,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, 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 463ce083a..666c8d50a 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,10 +1,9 @@ pub use super::*; -use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor}; +use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor}; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use std::borrow::Cow; -use std::cell::RefCell; #[derive(Clone)] pub struct MaybeStorageLive<'a> { @@ -17,6 +16,12 @@ impl<'a> MaybeStorageLive<'a> { } } +impl crate::CloneAnalysis for MaybeStorageLive<'_> { + fn clone_analysis(&self) -> Self { + self.clone() + } +} + impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> { type Domain = BitSet<Local>; @@ -43,7 +48,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: Location, @@ -56,7 +61,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { } fn terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _: &mir::Terminator<'tcx>, _: Location, @@ -65,7 +70,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -110,7 +115,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: Location, @@ -123,7 +128,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } fn terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _: &mir::Terminator<'tcx>, _: Location, @@ -132,7 +137,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill<Self::Idx>, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -141,28 +146,28 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } } -type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; +type BorrowedLocalsResults<'res, 'mir, 'tcx> = + ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>; /// 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> { - body: &'mir Body<'tcx>, - borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>, +pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> { + borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>, } -impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { - pub fn new( - body: &'mir Body<'tcx>, - borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, - ) -> Self { - MaybeRequiresStorage { - body, - borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)), - } +impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> { + pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self { + MaybeRequiresStorage { borrowed_locals } + } +} + +impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> { + fn clone_analysis(&self) -> Self { + Self { borrowed_locals: self.borrowed_locals.new_cursor() } } } -impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { type Domain = BitSet<Local>; const NAME: &'static str = "requires_storage"; @@ -181,17 +186,17 @@ impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx } } -impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { type Idx = Local; fn before_statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, loc: Location, ) { // If a place is borrowed in a statement, it needs storage for that statement. - self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc); + self.borrowed_locals.mut_analysis().statement_effect(trans, stmt, loc); match &stmt.kind { StatementKind::StorageDead(l) => trans.kill(*l), @@ -218,7 +223,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _: &mir::Statement<'tcx>, loc: Location, @@ -229,13 +234,13 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn before_terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, loc: Location, ) { // If a place is borrowed in a terminator, it needs storage for that terminator. - self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc); + self.borrowed_locals.mut_analysis().terminator_effect(trans, terminator, loc); match &terminator.kind { TerminatorKind::Call { destination, .. } => { @@ -282,7 +287,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, loc: Location, @@ -321,7 +326,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -330,7 +335,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn yield_resume_effect( - &self, + &mut self, trans: &mut impl GenKill<Self::Idx>, _resume_block: BasicBlock, resume_place: mir::Place<'tcx>, @@ -339,28 +344,28 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } } -impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. - fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) { - let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals }; - visitor.visit_location(&self.body, loc); + fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) { + let body = self.borrowed_locals.body(); + let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals }; + visitor.visit_location(body, loc); } } -struct MoveVisitor<'a, 'mir, 'tcx, T> { - borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>, +struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> { + borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>, trans: &'a mut T, } -impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T> +impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T> where T: GenKill<Local>, { fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) { if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { - let mut borrowed_locals = self.borrowed_locals.borrow_mut(); - borrowed_locals.seek_before_primary_effect(loc); - if !borrowed_locals.contains(local) { + self.borrowed_locals.seek_before_primary_effect(loc); + if !self.borrowed_locals.contains(local) { self.trans.kill(local); } } diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index fc4efb943..d43446bc5 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -27,9 +27,10 @@ pub use self::drop_flag_effects::{ on_lookup_result_bits, }; pub use self::framework::{ - fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces, - Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, + fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward, + CallReturnPlaces, CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis, + JoinSemiLattice, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, + ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, }; use self::move_paths::MoveData; @@ -42,7 +43,6 @@ pub mod impls; pub mod move_paths; pub mod rustc_peek; pub mod storage; -pub mod un_derefer; pub mod value_analysis; fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 096bc0acf..dc7e9ab3c 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -1,11 +1,10 @@ -use crate::move_paths::FxHashMap; -use crate::un_derefer::UnDerefer; use rustc_index::IndexVec; use rustc_middle::mir::tcx::RvalueInitializationState; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use smallvec::{smallvec, SmallVec}; +use std::iter; use std::mem; use super::abs_domain::Lift; @@ -21,7 +20,6 @@ struct MoveDataBuilder<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, - un_derefer: UnDerefer<'tcx>, } impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { @@ -35,25 +33,29 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { tcx, param_env, errors: Vec::new(), - un_derefer: UnDerefer { tcx: tcx, derefer_sidetable: Default::default() }, data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(body), rev_lookup: MovePathLookup { locals: body .local_decls - .indices() - .map(|i| { - Self::new_move_path( - &mut move_paths, - &mut path_map, - &mut init_path_map, - None, - Place::from(i), + .iter_enumerated() + .filter(|(_, l)| !l.is_deref_temp()) + .map(|(i, _)| { + ( + i, + Self::new_move_path( + &mut move_paths, + &mut path_map, + &mut init_path_map, + None, + Place::from(i), + ), ) }) .collect(), projections: Default::default(), + derefer_sidetable: Default::default(), }, move_paths, path_map, @@ -98,13 +100,11 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { /// /// Maybe we should have separate "borrowck" and "moveck" modes. fn move_path_for(&mut self, place: Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> { - if let Some(new_place) = self.builder.un_derefer.derefer(place.as_ref(), self.builder.body) - { - return self.move_path_for(new_place); - } + let deref_chain = self.builder.data.rev_lookup.deref_chain(place.as_ref()); debug!("lookup({:?})", place); - let mut base = self.builder.data.rev_lookup.locals[place.local]; + let mut base = + self.builder.data.rev_lookup.find_local(deref_chain.first().unwrap_or(&place).local); // The move path index of the first union that we find. Once this is // some we stop creating child move paths, since moves from unions @@ -113,60 +113,55 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // from `*(u.f: &_)` isn't allowed. let mut union_path = None; - for (i, elem) in place.projection.iter().enumerate() { - let proj_base = &place.projection[..i]; - let body = self.builder.body; - let tcx = self.builder.tcx; - let place_ty = Place::ty_from(place.local, proj_base, body, tcx).ty; - match place_ty.kind() { - ty::Ref(..) | ty::RawPtr(..) => { - let proj = &place.projection[..i + 1]; - return Err(MoveError::cannot_move_out_of( - self.loc, - BorrowedContent { - target_place: Place { - local: place.local, - projection: tcx.mk_place_elems(proj), + for place in deref_chain.into_iter().chain(iter::once(place)) { + for (place_ref, elem) in place.as_ref().iter_projections() { + let body = self.builder.body; + let tcx = self.builder.tcx; + let place_ty = place_ref.ty(body, tcx).ty; + match place_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + return Err(MoveError::cannot_move_out_of( + self.loc, + BorrowedContent { + target_place: place_ref.project_deeper(&[elem], tcx), }, - }, - )); - } - ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfTypeWithDestructor { container_ty: place_ty }, - )); - } - ty::Adt(adt, _) if adt.is_union() => { - union_path.get_or_insert(base); - } - ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: matches!(elem, ProjectionElem::Index(..)), - }, - )); - } - - ty::Array(..) => { - if let ProjectionElem::Index(..) = elem { + )); + } + ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => { return Err(MoveError::cannot_move_out_of( self.loc, - InteriorOfSliceOrArray { ty: place_ty, is_index: true }, + InteriorOfTypeWithDestructor { container_ty: place_ty }, + )); + } + ty::Adt(adt, _) if adt.is_union() => { + union_path.get_or_insert(base); + } + ty::Slice(_) => { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfSliceOrArray { + ty: place_ty, + is_index: matches!(elem, ProjectionElem::Index(..)), + }, )); } - } - _ => {} - }; + ty::Array(..) => { + if let ProjectionElem::Index(..) = elem { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfSliceOrArray { ty: place_ty, is_index: true }, + )); + } + } - if union_path.is_none() { - base = self.add_move_path(base, elem, |tcx| Place { - local: place.local, - projection: tcx.mk_place_elems(&place.projection[..i + 1]), - }); + _ => {} + }; + + if union_path.is_none() { + base = self + .add_move_path(base, elem, |tcx| place_ref.project_deeper(&[elem], tcx)); + } } } @@ -207,10 +202,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } -pub type MoveDat<'tcx> = Result< - (FxHashMap<Local, Place<'tcx>>, MoveData<'tcx>), - (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>), ->; +pub type MoveDat<'tcx> = + Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)>; impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { fn finalize(self) -> MoveDat<'tcx> { @@ -226,11 +219,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { "done dumping moves" }); - if self.errors.is_empty() { - Ok((self.un_derefer.derefer_sidetable, self.data)) - } else { - Err((self.data, self.errors)) - } + if self.errors.is_empty() { Ok(self.data) } else { Err((self.data, self.errors)) } } } @@ -259,7 +248,7 @@ pub(super) fn gather_moves<'tcx>( impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { fn gather_args(&mut self) { for arg in self.body.args_iter() { - let path = self.data.rev_lookup.locals[arg]; + let path = self.data.rev_lookup.find_local(arg); let init = self.data.inits.push(Init { path, @@ -295,7 +284,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => { assert!(place.projection.is_empty()); if self.builder.body.local_decls[place.local].is_deref_temp() { - self.builder.un_derefer.derefer_sidetable.insert(place.local, *reffed); + self.builder.data.rev_lookup.derefer_sidetable.insert(place.local, *reffed); } } StatementKind::Assign(box (place, rval)) => { @@ -317,7 +306,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { StatementKind::StorageLive(_) => {} StatementKind::StorageDead(local) => { // DerefTemp locals (results of CopyForDeref) don't actually move anything. - if !self.builder.un_derefer.derefer_sidetable.contains_key(&local) { + if !self.builder.data.rev_lookup.derefer_sidetable.contains_key(&local) { self.gather_move(Place::from(*local)); } } @@ -399,7 +388,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { destination, target, unwind: _, - from_hir_call: _, + call_source: _, fn_span: _, } => { self.gather_operand(func); @@ -459,12 +448,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_move(&mut self, place: Place<'tcx>) { debug!("gather_move({:?}, {:?})", self.loc, place); - if let Some(new_place) = self.builder.un_derefer.derefer(place.as_ref(), self.builder.body) - { - self.gather_move(new_place); - return; - } - if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] = **place.projection { @@ -521,11 +504,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) { debug!("gather_init({:?}, {:?})", self.loc, place); - if let Some(new_place) = self.builder.un_derefer.derefer(place, self.builder.body) { - self.gather_init(new_place.as_ref(), kind); - return; - } - let mut place = place; // Check if we are assigning into a field of a union, if so, lookup the place diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index ab1a67153..aa901f66d 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -1,5 +1,5 @@ use crate::move_paths::builder::MoveDat; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; @@ -175,7 +175,7 @@ pub struct MoveData<'tcx> { /// particular path being moved.) pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>, pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>, - pub rev_lookup: MovePathLookup, + pub rev_lookup: MovePathLookup<'tcx>, pub inits: IndexVec<InitIndex, Init>, /// Each Location `l` is mapped to the Inits that are effects /// of executing the code at `l`. @@ -289,8 +289,8 @@ impl Init { /// Tables mapping from a place to its MovePathIndex. #[derive(Debug)] -pub struct MovePathLookup { - locals: IndexVec<Local, MovePathIndex>, +pub struct MovePathLookup<'tcx> { + locals: FxIndexMap<Local, MovePathIndex>, /// projections are made from a base-place and a projection /// elem. The base-place will have a unique MovePathIndex; we use @@ -299,6 +299,9 @@ pub struct MovePathLookup { /// base-place). For the remaining lookup, we map the projection /// elem to the associated MovePathIndex. projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>, + + /// Maps `DerefTemp` locals to the `Place`s assigned to them. + derefer_sidetable: FxHashMap<Local, Place<'tcx>>, } mod builder; @@ -309,27 +312,59 @@ pub enum LookupResult { Parent(Option<MovePathIndex>), } -impl MovePathLookup { +impl<'tcx> MovePathLookup<'tcx> { // Unlike the builder `fn move_path_for` below, this lookup // alternative will *not* create a MovePath on the fly for an // unknown place, but will rather return the nearest available // parent. pub fn find(&self, place: PlaceRef<'_>) -> LookupResult { - let mut result = self.locals[place.local]; + let deref_chain = self.deref_chain(place); - for elem in place.projection.iter() { - if let Some(&subpath) = self.projections.get(&(result, elem.lift())) { - result = subpath; - } else { + let local = match deref_chain.first() { + Some(place) => place.local, + None => place.local, + }; + + let mut result = *self.locals.get(&local).unwrap_or_else(|| { + bug!("base local ({local:?}) of deref_chain should not be a deref temp") + }); + + // this needs to be a closure because `place` has a different lifetime than `prefix`'s places + let mut subpaths_for_place = |place: PlaceRef<'_>| { + for elem in place.projection.iter() { + if let Some(&subpath) = self.projections.get(&(result, elem.lift())) { + result = subpath; + } else { + return Some(result); + } + } + None + }; + + for place in deref_chain { + if let Some(result) = subpaths_for_place(place.as_ref()) { return LookupResult::Parent(Some(result)); } } + if let Some(result) = subpaths_for_place(place) { + return LookupResult::Parent(Some(result)); + } + LookupResult::Exact(result) } pub fn find_local(&self, local: Local) -> MovePathIndex { - self.locals[local] + let deref_chain = self.deref_chain(Place::from(local).as_ref()); + + let local = match deref_chain.last() { + Some(place) => place.local, + None => local, + }; + + *self.locals.get(&local).unwrap_or_else(|| { + bug!("base local ({local:?}) of deref_chain should not be a deref temp") + }) } /// An enumerated iterator of `local`s and their associated @@ -337,7 +372,22 @@ impl MovePathLookup { pub fn iter_locals_enumerated( &self, ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ { - self.locals.iter_enumerated().map(|(l, &idx)| (l, idx)) + self.locals.iter().map(|(&l, &idx)| (l, idx)) + } + + /// Returns the chain of places behind `DerefTemp` locals in `place` + pub fn deref_chain(&self, place: PlaceRef<'_>) -> Vec<Place<'tcx>> { + let mut prefix = Vec::new(); + let mut local = place.local; + + while let Some(&reffed) = self.derefer_sidetable.get(&local) { + prefix.insert(0, reffed); + local = reffed.local; + } + + debug!("deref_chain({place:?}) = {prefix:?}"); + + prefix } } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 7cae68efb..156231c3a 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -17,7 +17,7 @@ use crate::impls::{ use crate::move_paths::{HasMoveData, MoveData}; use crate::move_paths::{LookupResult, MovePathIndex}; use crate::MoveDataParamEnv; -use crate::{Analysis, JoinSemiLattice, Results, ResultsCursor}; +use crate::{Analysis, JoinSemiLattice, ResultsCursor}; pub struct SanityCheck; @@ -34,7 +34,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { } let param_env = tcx.param_env(def_id); - let (_, move_data) = MoveData::gather_moves(body, tcx, param_env).unwrap(); + let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data, param_env }; if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { @@ -42,7 +42,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_inits); + sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { @@ -50,7 +50,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_uninits); + sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { @@ -58,13 +58,13 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_def_inits); + sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_liveness); + sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { @@ -91,17 +91,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { /// errors are not intended to be used for unit tests.) pub fn sanity_check_via_rustc_peek<'tcx, A>( tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - results: &Results<'tcx, A>, + mut cursor: ResultsCursor<'_, 'tcx, A>, ) where A: RustcPeekAt<'tcx>, { - let def_id = body.source.def_id(); + let def_id = cursor.body().source.def_id(); debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id); - let mut cursor = ResultsCursor::new(body, results); - - let peek_calls = body.basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| { + let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| { PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call)) }); @@ -132,8 +129,8 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>( ) => { let loc = Location { block: bb, statement_index }; cursor.seek_before_primary_effect(loc); - let state = cursor.get(); - results.analysis.peek_at(tcx, *place, state, call); + let (state, analysis) = cursor.get_with_analysis(); + analysis.peek_at(tcx, *place, state, call); } _ => { diff --git a/compiler/rustc_mir_dataflow/src/un_derefer.rs b/compiler/rustc_mir_dataflow/src/un_derefer.rs deleted file mode 100644 index 7e6e25cc6..000000000 --- a/compiler/rustc_mir_dataflow/src/un_derefer.rs +++ /dev/null @@ -1,22 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; - -/// Used for reverting changes made by `DerefSeparator` -pub struct UnDerefer<'tcx> { - pub tcx: TyCtxt<'tcx>, - pub derefer_sidetable: FxHashMap<Local, Place<'tcx>>, -} - -impl<'tcx> UnDerefer<'tcx> { - #[inline] - pub fn derefer(&self, place: PlaceRef<'tcx>, body: &Body<'tcx>) -> Option<Place<'tcx>> { - let reffed = self.derefer_sidetable.get(&place.local)?; - - let new_place = reffed.project_deeper(place.projection, self.tcx); - if body.local_decls[new_place.local].is_deref_temp() { - return self.derefer(new_place.as_ref(), body); - } - Some(new_place) - } -} diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index b74d06e5a..5693e5a4a 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -343,7 +343,7 @@ where T: ValueAnalysis<'tcx>, { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location, @@ -354,7 +354,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location, @@ -365,7 +365,7 @@ where } fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, _block: BasicBlock, return_places: crate::CallReturnPlaces<'_, 'tcx>, @@ -376,7 +376,7 @@ where } fn apply_switch_int_edge_effects( - &self, + &mut self, _block: BasicBlock, discr: &Operand<'tcx>, apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, |