#![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] use std::collections::VecDeque; use std::rc::Rc; use crate::{ def_use::{self, DefUse}, region_infer::{Cause, RegionInferenceContext}, }; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; pub(crate) fn find<'tcx>( body: &Body<'tcx>, regioncx: &Rc>, tcx: TyCtxt<'tcx>, region_vid: RegionVid, start_point: Location, ) -> Option { let mut uf = UseFinder { body, regioncx, tcx, region_vid, start_point }; uf.find() } struct UseFinder<'cx, 'tcx> { body: &'cx Body<'tcx>, regioncx: &'cx Rc>, tcx: TyCtxt<'tcx>, region_vid: RegionVid, start_point: Location, } impl<'cx, 'tcx> UseFinder<'cx, 'tcx> { fn find(&mut self) -> Option { let mut queue = VecDeque::new(); let mut visited = FxIndexSet::default(); queue.push_back(self.start_point); while let Some(p) = queue.pop_front() { if !self.regioncx.region_contains(self.region_vid, p) { continue; } if !visited.insert(p) { continue; } let block_data = &self.body[p.block]; match self.def_use(p, block_data.visitable(p.statement_index)) { Some(DefUseResult::Def) => {} Some(DefUseResult::UseLive { local }) => { return Some(Cause::LiveVar(local, p)); } Some(DefUseResult::UseDrop { local }) => { return Some(Cause::DropVar(local, p)); } None => { if p.statement_index < block_data.statements.len() { queue.push_back(p.successor_within_block()); } else { queue.extend( block_data .terminator() .successors() .filter(|&bb| { Some(&mir::UnwindAction::Cleanup(bb)) != block_data.terminator().unwind() }) .map(|bb| Location { statement_index: 0, block: bb }), ); } } } } None } fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option { let mut visitor = DefUseVisitor { body: self.body, tcx: self.tcx, region_vid: self.region_vid, def_use_result: None, }; thing.apply(location, &mut visitor); visitor.def_use_result } } struct DefUseVisitor<'cx, 'tcx> { body: &'cx Body<'tcx>, tcx: TyCtxt<'tcx>, region_vid: RegionVid, def_use_result: Option, } enum DefUseResult { Def, UseLive { local: Local }, UseDrop { local: Local }, } impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> { fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { let local_ty = self.body.local_decls[local].ty; let mut found_it = false; self.tcx.for_each_free_region(&local_ty, |r| { if r.as_var() == self.region_vid { found_it = true; } }); if found_it { self.def_use_result = match def_use::categorize(context) { Some(DefUse::Def) => Some(DefUseResult::Def), Some(DefUse::Use) => Some(DefUseResult::UseLive { local }), Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }), None => None, }; } } }