| #![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<RegionInferenceContext<'tcx>>, |
| tcx: TyCtxt<'tcx>, |
| region_vid: RegionVid, |
| start_point: Location, |
| ) -> Option<Cause> { |
| let mut uf = UseFinder { body, regioncx, tcx, region_vid, start_point }; |
| |
| uf.find() |
| } |
| |
| struct UseFinder<'cx, 'tcx> { |
| body: &'cx Body<'tcx>, |
| regioncx: &'cx Rc<RegionInferenceContext<'tcx>>, |
| tcx: TyCtxt<'tcx>, |
| region_vid: RegionVid, |
| start_point: Location, |
| } |
| |
| impl<'cx, 'tcx> UseFinder<'cx, 'tcx> { |
| fn find(&mut self) -> Option<Cause> { |
| 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<DefUseResult> { |
| 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<DefUseResult>, |
| } |
| |
| 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, |
| }; |
| } |
| } |
| } |