1
Fork 0
rust/compiler/rustc_borrowck/src/used_muts.rs
Nicholas Nethercote 3fe7dd6893 Remove unnecessary lifetimes in dataflow structs.
There are four related dataflow structs: `MaybeInitializedPlaces`,
`MaybeUninitializedPlaces`, and `EverInitializedPlaces`,
`DefinitelyInitializedPlaces`. They all have a `&Body` and a
`&MoveData<'tcx>` field. The first three use different lifetimes for the
two fields, but the last one uses the same lifetime for both.

This commit changes the first three to use the same lifetime, removing
the need for one of the lifetimes. Other structs that also lose a
lifetime as a result of this are `LivenessContext`, `LivenessResults`,
`InitializationData`.

It then does similar things in various other structs.
2024-09-09 16:14:18 +10:00

109 lines
4.9 KiB
Rust

use rustc_data_structures::fx::FxIndexSet;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{
Local, Location, Place, Statement, StatementKind, Terminator, TerminatorKind,
};
use tracing::debug;
use crate::MirBorrowckCtxt;
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
/// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
/// of the `unused_mut` lint.
///
/// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
/// used from borrow checking. This function looks for assignments into these locals from
/// user-declared locals and adds those user-defined locals to the `used_mut` set. This can
/// occur due to a rare case involving upvars in closures.
///
/// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
/// (not arguments) that have not already been marked as being used.
/// This function then looks for assignments from statements or the terminator into the locals
/// from this set and removes them from the set. This leaves only those locals that have not
/// been assigned to - this set is used as a proxy for locals that were not initialized due to
/// unreachable code. These locals are then considered "used" to silence the lint for them.
/// See #55344 for context.
pub(crate) fn gather_used_muts(
&mut self,
temporary_used_locals: FxIndexSet<Local>,
mut never_initialized_mut_locals: FxIndexSet<Local>,
) {
{
let mut visitor = GatherUsedMutsVisitor {
temporary_used_locals,
never_initialized_mut_locals: &mut never_initialized_mut_locals,
mbcx: self,
};
visitor.visit_body(visitor.mbcx.body);
}
// Take the union of the existed `used_mut` set with those variables we've found were
// never initialized.
debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
}
}
/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'a, 'b, 'infcx, 'tcx> {
temporary_used_locals: FxIndexSet<Local>,
never_initialized_mut_locals: &'a mut FxIndexSet<Local>,
mbcx: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
}
impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) {
// Remove any locals that we found were initialized from the
// `never_initialized_mut_locals` set. At the end, the only remaining locals will
// be those that were never initialized - we will consider those as being used as
// they will either have been removed by unreachable code optimizations; or linted
// as unused variables.
// FIXME(#120456) - is `swap_remove` correct?
self.never_initialized_mut_locals.swap_remove(&into.local);
}
}
impl<'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'_, '_, '_, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
debug!("visit_terminator: terminator={:?}", terminator);
match &terminator.kind {
TerminatorKind::Call { destination, .. } => {
self.remove_never_initialized_mut_locals(*destination);
}
_ => {}
}
self.super_terminator(terminator, location);
}
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
if let StatementKind::Assign(box (into, _)) = &statement.kind {
debug!(
"visit_statement: statement={:?} local={:?} \
never_initialized_mut_locals={:?}",
statement, into.local, self.never_initialized_mut_locals
);
self.remove_never_initialized_mut_locals(*into);
}
self.super_statement(statement, location);
}
fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
if place_context.is_place_assignment() && self.temporary_used_locals.contains(&local) {
// Propagate the Local assigned at this Location as a used mutable local variable
for moi in &self.mbcx.move_data.loc_map[location] {
let mpi = &self.mbcx.move_data.moves[*moi].path;
let path = &self.mbcx.move_data.move_paths[*mpi];
debug!(
"assignment of {:?} to {:?}, adding {:?} to used mutable set",
path.place, local, path.place
);
if let Some(user_local) = path.place.as_local() {
self.mbcx.used_mut.insert(user_local);
}
}
}
}
}