//! A less precise version of `MaybeInitializedPlaces` whose domain is entire locals. //! //! A local will be maybe initialized if *any* projections of that local might be initialized. use crate::{CallReturnPlaces, GenKill}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; pub struct MaybeInitializedLocals; impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeInitializedLocals { type Domain = BitSet; const NAME: &'static str = "maybe_init_locals"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = uninit BitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) { // Function arguments are initialized to begin with. for arg in body.args_iter() { entry_set.insert(arg); } } } impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeInitializedLocals { type Idx = Local; fn statement_effect( &self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, loc: Location, ) { TransferFunction { trans }.visit_statement(statement, loc) } fn terminator_effect( &self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, loc: Location, ) { TransferFunction { trans }.visit_terminator(terminator, loc) } fn call_return_effect( &self, trans: &mut impl GenKill, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { return_places.for_each(|place| trans.gen(place.local)); } /// See `Analysis::apply_yield_resume_effect`. fn yield_resume_effect( &self, trans: &mut impl GenKill, _resume_block: BasicBlock, resume_place: mir::Place<'tcx>, ) { trans.gen(resume_place.local) } } struct TransferFunction<'a, T> { trans: &'a mut T, } impl Visitor<'_> for TransferFunction<'_, T> where T: GenKill, { // FIXME: Using `visit_local` here is a bug. For example, on `move _5.field` we mark `_5` as // deinitialized, although clearly it is only partially deinitialized. This analysis is not // actually used anywhere at the moment, so this is not critical, but this does need to be fixed // before it starts being used again. fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext}; match context { // These are handled specially in `call_return_effect` and `yield_resume_effect`. PlaceContext::MutatingUse( MutatingUseContext::Call | MutatingUseContext::AsmOutput | MutatingUseContext::Yield, ) => {} // If it's deinitialized, it's no longer init PlaceContext::MutatingUse(MutatingUseContext::Deinit) => self.trans.kill(local), // Otherwise, when a place is mutated, we must consider it possibly initialized. PlaceContext::MutatingUse(_) => self.trans.gen(local), // If the local is moved out of, or if it gets marked `StorageDead`, consider it no // longer initialized. PlaceContext::NonUse(NonUseContext::StorageDead) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => self.trans.kill(local), // All other uses do not affect this analysis. PlaceContext::NonUse( NonUseContext::StorageLive | NonUseContext::AscribeUserTy | NonUseContext::VarDebugInfo, ) | PlaceContext::NonMutatingUse( NonMutatingUseContext::Inspect | NonMutatingUseContext::Copy | NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::ShallowBorrow | NonMutatingUseContext::UniqueBorrow | NonMutatingUseContext::AddressOf | NonMutatingUseContext::Projection, ) => {} } } }