| // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use borrow_check::nll::constraints::ConstraintSet; |
| use borrow_check::nll::type_check::AtLocation; |
| use borrow_check::nll::{LocalWithRegion, NllLivenessMap}; |
| use borrow_check::nll::universal_regions::UniversalRegions; |
| use dataflow::move_paths::{HasMoveData, MoveData}; |
| use dataflow::MaybeInitializedPlaces; |
| use dataflow::{FlowAtLocation, FlowsAtLocation}; |
| use rustc::infer::canonical::QueryRegionConstraint; |
| use rustc::mir::{BasicBlock, Location, Mir}; |
| use rustc::traits::query::dropck_outlives::DropckOutlivesResult; |
| use rustc::traits::query::type_op::outlives::DropckOutlives; |
| use rustc::traits::query::type_op::TypeOp; |
| use rustc::ty::{RegionVid, Ty, TypeFoldable}; |
| use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
| use std::rc::Rc; |
| use util::liveness::{LiveVariableMap, LivenessResults}; |
| |
| use super::TypeChecker; |
| |
| crate mod liveness_map; |
| |
| /// Combines liveness analysis with initialization analysis to |
| /// determine which variables are live at which points, both due to |
| /// ordinary uses and drops. Returns a set of (ty, location) pairs |
| /// that indicate which types must be live at which point in the CFG. |
| /// This vector is consumed by `constraint_generation`. |
| /// |
| /// NB. This computation requires normalization; therefore, it must be |
| /// performed before |
| pub(super) fn generate<'gcx, 'tcx>( |
| cx: &mut TypeChecker<'_, 'gcx, 'tcx>, |
| mir: &Mir<'tcx>, |
| flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>, |
| move_data: &MoveData<'tcx>, |
| ) -> (LivenessResults<LocalWithRegion>, NllLivenessMap) { |
| let free_regions = { |
| let borrowck_context = cx.borrowck_context.as_ref().unwrap(); |
| regions_that_outlive_free_regions( |
| cx.infcx.num_region_vars(), |
| &borrowck_context.universal_regions, |
| &borrowck_context.constraints.outlives_constraints, |
| ) |
| }; |
| let liveness_map = NllLivenessMap::compute(cx.tcx(), &free_regions, mir); |
| let liveness = LivenessResults::compute(mir, &liveness_map); |
| |
| // For everything else, it is only live where it is actually used. |
| if !liveness_map.is_empty() { |
| let mut generator = TypeLivenessGenerator { |
| cx, |
| mir, |
| liveness: &liveness, |
| flow_inits, |
| move_data, |
| drop_data: FxHashMap(), |
| map: &liveness_map, |
| }; |
| |
| for bb in mir.basic_blocks().indices() { |
| generator.add_liveness_constraints(bb); |
| } |
| } |
| |
| (liveness, liveness_map) |
| } |
| |
| /// Compute all regions that are (currently) known to outlive free |
| /// regions. For these regions, we do not need to compute |
| /// liveness, since the outlives constraints will ensure that they |
| /// are live over the whole fn body anyhow. |
| fn regions_that_outlive_free_regions( |
| num_region_vars: usize, |
| universal_regions: &UniversalRegions<'tcx>, |
| constraint_set: &ConstraintSet, |
| ) -> FxHashSet<RegionVid> { |
| // Build a graph of the outlives constraints thus far. This is |
| // a reverse graph, so for each constraint `R1: R2` we have an |
| // edge `R2 -> R1`. Therefore, if we find all regions |
| // reachable from each free region, we will have all the |
| // regions that are forced to outlive some free region. |
| let rev_constraint_graph = constraint_set.reverse_graph(num_region_vars); |
| let rev_region_graph = rev_constraint_graph.region_graph(constraint_set); |
| |
| // Stack for the depth-first search. Start out with all the free regions. |
| let mut stack: Vec<_> = universal_regions.universal_regions().collect(); |
| |
| // Set of all free regions, plus anything that outlives them. Initially |
| // just contains the free regions. |
| let mut outlives_free_region: FxHashSet<_> = stack.iter().cloned().collect(); |
| |
| // Do the DFS -- for each thing in the stack, find all things |
| // that outlive it and add them to the set. If they are not, |
| // push them onto the stack for later. |
| while let Some(sub_region) = stack.pop() { |
| stack.extend( |
| rev_region_graph |
| .outgoing_regions(sub_region) |
| .filter(|&r| outlives_free_region.insert(r)), |
| ); |
| } |
| |
| // Return the final set of things we visited. |
| outlives_free_region |
| } |
| |
| struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> |
| where |
| 'typeck: 'gen, |
| 'flow: 'gen, |
| 'tcx: 'typeck + 'flow, |
| 'gcx: 'tcx, |
| { |
| cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, |
| mir: &'gen Mir<'tcx>, |
| liveness: &'gen LivenessResults<LocalWithRegion>, |
| flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>, |
| move_data: &'gen MoveData<'tcx>, |
| drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>, |
| map: &'gen NllLivenessMap, |
| } |
| |
| struct DropData<'tcx> { |
| dropck_result: DropckOutlivesResult<'tcx>, |
| region_constraint_data: Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>, |
| } |
| |
| impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { |
| /// Liveness constraints: |
| /// |
| /// > If a variable V is live at point P, then all regions R in the type of V |
| /// > must include the point P. |
| fn add_liveness_constraints(&mut self, bb: BasicBlock) { |
| debug!("add_liveness_constraints(bb={:?})", bb); |
| |
| self.liveness |
| .regular |
| .simulate_block(self.mir, bb, self.map, |location, live_locals| { |
| for live_local in live_locals.iter() { |
| let local = self.map.from_live_var(live_local); |
| let live_local_ty = self.mir.local_decls[local].ty; |
| Self::push_type_live_constraint(&mut self.cx, live_local_ty, location); |
| } |
| }); |
| |
| let mut all_live_locals: Vec<(Location, Vec<LocalWithRegion>)> = vec![]; |
| self.liveness |
| .drop |
| .simulate_block(self.mir, bb, self.map, |location, live_locals| { |
| all_live_locals.push((location, live_locals.iter().collect())); |
| }); |
| debug!( |
| "add_liveness_constraints: all_live_locals={:#?}", |
| all_live_locals |
| ); |
| |
| let terminator_index = self.mir.basic_blocks()[bb].statements.len(); |
| self.flow_inits.reset_to_entry_of(bb); |
| while let Some((location, live_locals)) = all_live_locals.pop() { |
| for live_local in live_locals { |
| debug!( |
| "add_liveness_constraints: location={:?} live_local={:?}", |
| location, live_local |
| ); |
| |
| if log_enabled!(::log::Level::Debug) { |
| self.flow_inits.each_state_bit(|mpi_init| { |
| debug!( |
| "add_liveness_constraints: location={:?} initialized={:?}", |
| location, |
| &self.flow_inits.operator().move_data().move_paths[mpi_init] |
| ); |
| }); |
| } |
| |
| let local = self.map.from_live_var(live_local); |
| let mpi = self.move_data.rev_lookup.find_local(local); |
| if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { |
| debug!( |
| "add_liveness_constraints: mpi={:?} has initialized child {:?}", |
| self.move_data.move_paths[mpi], |
| self.move_data.move_paths[initialized_child] |
| ); |
| |
| let local = self.map.from_live_var(live_local); |
| let live_local_ty = self.mir.local_decls[local].ty; |
| self.add_drop_live_constraint(live_local, live_local_ty, location); |
| } |
| } |
| |
| if location.statement_index == terminator_index { |
| debug!( |
| "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", |
| location |
| ); |
| self.flow_inits.reconstruct_terminator_effect(location); |
| } else { |
| debug!( |
| "add_liveness_constraints: reconstruct_statement_effect from {:#?}", |
| location |
| ); |
| self.flow_inits.reconstruct_statement_effect(location); |
| } |
| self.flow_inits.apply_local_effect(location); |
| } |
| } |
| |
| /// Some variable with type `live_ty` is "regular live" at |
| /// `location` -- i.e., it may be used later. This means that all |
| /// regions appearing in the type `live_ty` must be live at |
| /// `location`. |
| fn push_type_live_constraint<T>( |
| cx: &mut TypeChecker<'_, 'gcx, 'tcx>, |
| value: T, |
| location: Location, |
| ) where |
| T: TypeFoldable<'tcx>, |
| { |
| debug!( |
| "push_type_live_constraint(live_ty={:?}, location={:?})", |
| value, location |
| ); |
| |
| cx.tcx().for_each_free_region(&value, |live_region| { |
| if let Some(ref mut borrowck_context) = cx.borrowck_context { |
| let region_vid = borrowck_context |
| .universal_regions |
| .to_region_vid(live_region); |
| borrowck_context |
| .constraints |
| .liveness_constraints |
| .add_element(region_vid, location); |
| |
| if let Some(all_facts) = borrowck_context.all_facts { |
| let start_index = borrowck_context.location_table.start_index(location); |
| all_facts.region_live_at.push((region_vid, start_index)); |
| |
| let mid_index = borrowck_context.location_table.mid_index(location); |
| all_facts.region_live_at.push((region_vid, mid_index)); |
| } |
| } |
| }); |
| } |
| |
| /// Some variable with type `live_ty` is "drop live" at `location` |
| /// -- i.e., it may be dropped later. This means that *some* of |
| /// the regions in its type must be live at `location`. The |
| /// precise set will depend on the dropck constraints, and in |
| /// particular this takes `#[may_dangle]` into account. |
| fn add_drop_live_constraint( |
| &mut self, |
| dropped_local: LocalWithRegion, |
| dropped_ty: Ty<'tcx>, |
| location: Location, |
| ) { |
| debug!( |
| "add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})", |
| dropped_local, dropped_ty, location |
| ); |
| |
| let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ |
| let cx = &mut self.cx; |
| move || Self::compute_drop_data(cx, dropped_ty) |
| }); |
| |
| if let Some(data) = &drop_data.region_constraint_data { |
| self.cx.push_region_constraints(location.boring(), data); |
| } |
| |
| drop_data.dropck_result.report_overflows( |
| self.cx.infcx.tcx, |
| self.mir.source_info(location).span, |
| dropped_ty, |
| ); |
| |
| // All things in the `outlives` array may be touched by |
| // the destructor and must be live at this point. |
| for &kind in &drop_data.dropck_result.kinds { |
| Self::push_type_live_constraint(&mut self.cx, kind, location); |
| } |
| } |
| |
| fn compute_drop_data( |
| cx: &mut TypeChecker<'_, 'gcx, 'tcx>, |
| dropped_ty: Ty<'tcx>, |
| ) -> DropData<'tcx> { |
| debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); |
| |
| let param_env = cx.param_env; |
| let (dropck_result, region_constraint_data) = param_env |
| .and(DropckOutlives::new(dropped_ty)) |
| .fully_perform(cx.infcx) |
| .unwrap(); |
| |
| DropData { |
| dropck_result, |
| region_constraint_data, |
| } |
| } |
| } |