diff --git a/src/librustc_mir/borrow_check/nll/liveness_map.rs b/src/librustc_mir/borrow_check/nll/liveness_map.rs new file mode 100644 index 00000000000..cbd9c9a4e1a --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/liveness_map.rs @@ -0,0 +1,77 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! For the NLL computation, we need to compute liveness, but only for those +//! local variables whose types contain regions. The others are not of interest +//! to us. This file defines a new index type (LocalWithRegion) that indexes into +//! a list of "variables whose type contain regions". It also defines a map from +//! Local to LocalWithRegion and vice versa -- this map can be given to the +//! liveness code so that it only operates over variables with regions in their +//! types, instead of all variables. + +use rustc::ty::TypeFoldable; +use rustc_data_structures::indexed_vec::IndexVec; +use rustc::mir::{Mir, Local}; +use util::liveness::LiveVariableMap; + +use rustc_data_structures::indexed_vec::Idx; + +/// Map between Local and LocalWithRegion indices: this map is supplied to the +/// liveness code so that it will only analyze those variables whose types +/// contain regions. +crate struct NllLivenessMap { + /// For each local variable, contains either None (if the type has no regions) + /// or Some(i) with a suitable index. + pub from_local: IndexVec>, + /// For each LocalWithRegion, maps back to the original Local index. + pub to_local: IndexVec, + +} + +impl LiveVariableMap for NllLivenessMap { + + fn from_local(&self, local: Local) -> Option { + self.from_local[local] + } + + type LiveVar = LocalWithRegion; + + fn from_live_var(&self, local: Self::LiveVar) -> Local { + self.to_local[local] + } + + fn num_variables(&self) -> usize { + self.to_local.len() + } +} + +impl NllLivenessMap { + /// Iterates over the variables in Mir and assigns each Local whose type contains + /// regions a LocalWithRegion index. Returns a map for converting back and forth. + pub fn compute(mir: &Mir) -> Self { + let mut to_local = IndexVec::default(); + let from_local: IndexVec> = mir + .local_decls + .iter_enumerated() + .map(|(local, local_decl)| { + if local_decl.ty.has_free_regions() { + Some(to_local.push(local)) + } + else { + None + } + }).collect(); + + Self { from_local, to_local } + } +} + +/// Index given to each local variable whose type contains a region. +newtype_index!(LocalWithRegion); diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 5fcf46f6903..76f8fa206be 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -13,6 +13,7 @@ use borrow_check::location::{LocationIndex, LocationTable}; use borrow_check::nll::facts::AllFactsExt; use borrow_check::nll::type_check::MirTypeckRegionConstraints; use borrow_check::nll::region_infer::values::RegionValueElements; +use borrow_check::nll::liveness_map::{NllLivenessMap, LocalWithRegion}; use dataflow::indexes::BorrowIndex; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; @@ -30,7 +31,7 @@ use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; use transform::MirSource; -use util::liveness::{LivenessResults, LocalSet}; +use util::liveness::{LivenessResults, LiveVarSet}; use self::mir_util::PassWhere; use polonius_engine::{Algorithm, Output}; @@ -45,6 +46,7 @@ crate mod region_infer; mod renumber; crate mod type_check; mod universal_regions; +crate mod liveness_map; mod constraints; @@ -103,7 +105,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( let elements = &Rc::new(RegionValueElements::new(mir, universal_regions.len())); // Run the MIR type-checker. - let liveness = &LivenessResults::compute(mir); + let liveness_map = NllLivenessMap::compute(&mir); + let liveness = LivenessResults::compute(mir, &liveness_map); let constraint_sets = type_check::type_check( infcx, param_env, @@ -193,7 +196,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // write unit-tests, as well as helping with debugging. dump_mir_results( infcx, - liveness, + &liveness, MirSource::item(def_id), &mir, ®ioncx, @@ -209,7 +212,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, - liveness: &LivenessResults, + liveness: &LivenessResults, source: MirSource, mir: &Mir<'tcx>, regioncx: &RegionInferenceContext, @@ -219,6 +222,8 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( return; } + let map = &NllLivenessMap::compute(mir); + let regular_liveness_per_location: FxHashMap<_, _> = mir .basic_blocks() .indices() @@ -226,7 +231,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( let mut results = vec![]; liveness .regular - .simulate_block(&mir, bb, |location, local_set| { + .simulate_block(&mir, bb, map, |location, local_set| { results.push((location, local_set.clone())); }); results @@ -240,7 +245,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( let mut results = vec![]; liveness .drop - .simulate_block(&mir, bb, |location, local_set| { + .simulate_block(&mir, bb, map, |location, local_set| { results.push((location, local_set.clone())); }); results @@ -405,7 +410,10 @@ impl ToRegionVid for RegionVid { } } -fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String { +fn live_variable_set( + regular: &LiveVarSet, + drops: &LiveVarSet +) -> String { // sort and deduplicate: let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect(); diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index cd468eabd5f..2b9307db59a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use borrow_check::nll::{NllLivenessMap, LocalWithRegion}; use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::MaybeInitializedPlaces; use dataflow::{FlowAtLocation, FlowsAtLocation}; use rustc::infer::canonical::QueryRegionConstraint; -use rustc::mir::Local; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::traits::query::type_op::outlives::DropckOutlives; @@ -21,7 +21,7 @@ use rustc::traits::query::type_op::TypeOp; use rustc::ty::{Ty, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; use std::rc::Rc; -use util::liveness::LivenessResults; +use util::liveness::{LivenessResults, LiveVariableMap }; use super::TypeChecker; @@ -36,7 +36,7 @@ use super::TypeChecker; pub(super) fn generate<'gcx, 'tcx>( cx: &mut TypeChecker<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, - liveness: &LivenessResults, + liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) { @@ -47,6 +47,7 @@ pub(super) fn generate<'gcx, 'tcx>( flow_inits, move_data, drop_data: FxHashMap(), + map: &NllLivenessMap::compute(mir), }; for bb in mir.basic_blocks().indices() { @@ -63,10 +64,11 @@ where { cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, mir: &'gen Mir<'tcx>, - liveness: &'gen LivenessResults, + liveness: &'gen LivenessResults, flow_inits: &'gen mut FlowAtLocation>, move_data: &'gen MoveData<'tcx>, drop_data: FxHashMap, DropData<'tcx>>, + map: &'gen NllLivenessMap, } struct DropData<'tcx> { @@ -84,17 +86,18 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo self.liveness .regular - .simulate_block(self.mir, bb, |location, live_locals| { + .simulate_block(self.mir, bb, self.map, |location, live_locals| { for live_local in live_locals.iter() { - let live_local_ty = self.mir.local_decls[live_local].ty; + 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)> = vec![]; + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; self.liveness .drop - .simulate_block(self.mir, bb, |location, live_locals| { + .simulate_block(self.mir, bb, self.map, |location, live_locals| { all_live_locals.push((location, live_locals.iter().collect())); }); debug!( @@ -121,7 +124,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo }); } - let mpi = self.move_data.rev_lookup.find_local(live_local); + 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 {:?}", @@ -129,7 +133,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo self.move_data.move_paths[initialized_child] ); - let live_local_ty = self.mir.local_decls[live_local].ty; + 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); } } @@ -190,7 +195,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo /// particular this takes `#[may_dangle]` into account. fn add_drop_live_constraint( &mut self, - dropped_local: Local, + dropped_local: LocalWithRegion, dropped_ty: Ty<'tcx>, location: Location, ) { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index e188c9d7559..e23f9b20a10 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -19,6 +19,7 @@ use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; use borrow_check::nll::region_infer::values::{RegionValues, RegionValueElements}; use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::ToRegionVid; +use borrow_check::nll::LocalWithRegion; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; @@ -109,7 +110,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( universal_regions: &UniversalRegions<'tcx>, location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, - liveness: &LivenessResults, + liveness: &LivenessResults, all_facts: &mut Option, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index a6017fafcc8..a43f17e40df 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -66,7 +66,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor}; use rustc::ty::{self, TyCtxt, AdtDef, Ty}; use rustc::ty::subst::Substs; use util::dump_mir; -use util::liveness::{self, LivenessMode}; +use util::liveness::{self, IdentityMap, LivenessMode}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_set::IdxSetBuf; use std::collections::HashMap; @@ -130,7 +130,7 @@ struct SuspensionPoint { state: u32, resume: BasicBlock, drop: Option, - storage_liveness: liveness::LocalSet, + storage_liveness: liveness::LiveVarSet, } struct TransformVisitor<'a, 'tcx: 'a> { @@ -145,7 +145,7 @@ struct TransformVisitor<'a, 'tcx: 'a> { remap: HashMap, usize)>, // A map from a suspension point in a block to the locals which have live storage at that point - storage_liveness: HashMap, + storage_liveness: HashMap>, // A list of suspension points, generated during the transform suspension_points: Vec, @@ -317,7 +317,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>, new_ret_local } -struct StorageIgnored(liveness::LocalSet); +struct StorageIgnored(liveness::LiveVarSet); impl<'tcx> Visitor<'tcx> for StorageIgnored { fn visit_statement(&mut self, @@ -332,7 +332,7 @@ impl<'tcx> Visitor<'tcx> for StorageIgnored { } } -struct BorrowedLocals(liveness::LocalSet); +struct BorrowedLocals(liveness::LiveVarSet); fn mark_as_borrowed<'tcx>(place: &Place<'tcx>, locals: &mut BorrowedLocals) { match *place { @@ -361,12 +361,12 @@ impl<'tcx> Visitor<'tcx> for BorrowedLocals { } } -fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +fn locals_live_across_suspend_points<'a, 'tcx,>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, source: MirSource, movable: bool) -> - (liveness::LocalSet, - HashMap) { + (liveness::LiveVarSet, + HashMap>) { let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let node_id = tcx.hir.as_local_node_id(source.def_id).unwrap(); @@ -396,12 +396,23 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; // Calculate the liveness of MIR locals ignoring borrows. - let mut set = liveness::LocalSet::new_empty(mir.local_decls.len()); - let mut liveness = liveness::liveness_of_locals(mir, LivenessMode { - include_regular_use: true, - include_drops: true, - }); - liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness); + let mut set = liveness::LiveVarSet::new_empty(mir.local_decls.len()); + let mut liveness = liveness::liveness_of_locals( + mir, + LivenessMode { + include_regular_use: true, + include_drops: true, + }, + &IdentityMap::new(mir), + ); + liveness::dump_mir( + tcx, + "generator_liveness", + source, + mir, + &IdentityMap::new(mir), + &liveness, + ); let mut storage_liveness_map = HashMap::new(); @@ -468,7 +479,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) -> (HashMap, usize)>, GeneratorLayout<'tcx>, - HashMap) + HashMap>) { // Use a liveness analysis to compute locals which are live across a suspension point let (live_locals, storage_liveness) = locals_live_across_suspend_points(tcx, diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index e1d5e302c3a..6c5b38a806e 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -33,32 +33,75 @@ //! generator yield points, all pre-existing references are invalidated, so this //! doesn't matter). -use rustc::mir::*; -use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc_data_structures::indexed_set::IdxSetBuf; -use rustc_data_structures::work_queue::WorkQueue; -use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; -use rustc::ty::item_path; use rustc::mir::visit::MirVisitable; -use std::path::{Path, PathBuf}; +use rustc::mir::visit::{PlaceContext, Visitor}; +use rustc::mir::Local; +use rustc::mir::*; +use rustc::ty::{item_path, TyCtxt}; +use rustc_data_structures::indexed_set::IdxSetBuf; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::work_queue::WorkQueue; use std::fs; -use rustc::ty::TyCtxt; use std::io::{self, Write}; +use std::path::{Path, PathBuf}; use transform::MirSource; +use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; -pub type LocalSet = IdxSetBuf; +pub type LiveVarSet = IdxSetBuf; /// This gives the result of the liveness analysis at the boundary of /// basic blocks. You can use `simulate_block` to obtain the /// intra-block results. -pub struct LivenessResult { +/// +/// The `V` type defines the set of variables that we computed +/// liveness for. This is often `Local`, in which case we computed +/// liveness for all variables -- but it can also be some other type, +/// which indicates a subset of the variables within the graph. +pub struct LivenessResult { /// Liveness mode in use when these results were computed. pub mode: LivenessMode, /// Live variables on exit to each basic block. This is equal to /// the union of the `ins` for each successor. - pub outs: IndexVec, + pub outs: IndexVec>, +} + +/// Defines the mapping to/from the MIR local variables (`Local`) to +/// the "live variable indices" we are using in a particular +/// computation. +pub trait LiveVariableMap { + type LiveVar; + + fn from_local(&self, local: Local) -> Option; + fn from_live_var(&self, local: Self::LiveVar) -> Local; + fn num_variables(&self) -> usize; +} + +#[derive(Debug)] +pub struct IdentityMap<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, +} + +impl<'a, 'tcx> IdentityMap<'a, 'tcx> { + pub fn new(mir: &'a Mir<'tcx>) -> Self { + Self { mir } + } +} + +impl<'a, 'tcx> LiveVariableMap for IdentityMap<'a, 'tcx> { + type LiveVar = Local; + + fn from_local(&self, local: Local) -> Option { + Some(local) + } + + fn from_live_var(&self, local: Self::LiveVar) -> Local { + local + } + + fn num_variables(&self) -> usize { + self.mir.local_decls.len() + } } #[derive(Copy, Clone, Debug)] @@ -80,18 +123,21 @@ pub struct LivenessMode { } /// A combination of liveness results, used in NLL. -pub struct LivenessResults { +pub struct LivenessResults { /// Liveness results where a regular use makes a variable X live, /// but not a drop. - pub regular: LivenessResult, + pub regular: LivenessResult, /// Liveness results where a drop makes a variable X live, /// but not a regular use. - pub drop: LivenessResult, + pub drop: LivenessResult, } -impl LivenessResults { - pub fn compute<'tcx>(mir: &Mir<'tcx>) -> LivenessResults { +impl LivenessResults { + pub fn compute<'tcx>( + mir: &Mir<'tcx>, + map: &impl LiveVariableMap, + ) -> LivenessResults { LivenessResults { regular: liveness_of_locals( &mir, @@ -99,6 +145,7 @@ impl LivenessResults { include_regular_use: true, include_drops: false, }, + map, ), drop: liveness_of_locals( @@ -107,6 +154,7 @@ impl LivenessResults { include_regular_use: false, include_drops: true, }, + map, ), } } @@ -115,19 +163,26 @@ impl LivenessResults { /// Compute which local variables are live within the given function /// `mir`. The liveness mode `mode` determines what sorts of uses are /// considered to make a variable live (e.g., do drops count?). -pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult { - let locals = mir.local_decls.len(); - let def_use: IndexVec<_, _> = mir.basic_blocks() +pub fn liveness_of_locals<'tcx, V: Idx>( + mir: &Mir<'tcx>, + mode: LivenessMode, + map: &impl LiveVariableMap, +) -> LivenessResult { + let num_live_vars = map.num_variables(); + + let def_use: IndexVec<_, DefsUses> = mir + .basic_blocks() .iter() - .map(|b| block(mode, b, locals)) + .map(|b| block(mode, map, b, num_live_vars)) .collect(); - let mut outs: IndexVec<_, _> = mir.basic_blocks() + let mut outs: IndexVec<_, LiveVarSet> = mir + .basic_blocks() .indices() - .map(|_| LocalSet::new_empty(locals)) + .map(|_| LiveVarSet::new_empty(num_live_vars)) .collect(); - let mut bits = LocalSet::new_empty(locals); + let mut bits = LiveVarSet::new_empty(num_live_vars); // queue of things that need to be re-processed, and a set containing // the things currently in the queue @@ -156,14 +211,19 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> Liveness LivenessResult { mode, outs } } -impl LivenessResult { +impl LivenessResult { /// Walks backwards through the statements/terminator in the given /// basic block `block`. At each point within `block`, invokes /// the callback `op` with the current location and the set of /// variables that are live on entry to that location. - pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP) - where - OP: FnMut(Location, &LocalSet), + pub fn simulate_block<'tcx, OP>( + &self, + mir: &Mir<'tcx>, + block: BasicBlock, + map: &impl LiveVariableMap, + mut callback: OP, + ) where + OP: FnMut(Location, &LiveVarSet), { let data = &mir[block]; @@ -179,18 +239,23 @@ impl LivenessResult { block, statement_index, }; - let locals = mir.local_decls.len(); + let num_live_vars = map.num_variables(); let mut visitor = DefsUsesVisitor { mode: self.mode, + map, defs_uses: DefsUses { - defs: LocalSet::new_empty(locals), - uses: LocalSet::new_empty(locals), + defs: LiveVarSet::new_empty(num_live_vars), + uses: LiveVarSet::new_empty(num_live_vars), }, }; // Visit the various parts of the basic block in reverse. If we go // forward, the logic in `add_def` and `add_use` would be wrong. - visitor.update_bits_and_do_callback(terminator_location, &data.terminator, &mut bits, - &mut callback); + visitor.update_bits_and_do_callback( + terminator_location, + &data.terminator, + &mut bits, + &mut callback, + ); // Compute liveness before each statement (in rev order) and invoke callback. for statement in data.statements.iter().rev() { @@ -200,8 +265,12 @@ impl LivenessResult { statement_index, }; visitor.defs_uses.clear(); - visitor.update_bits_and_do_callback(statement_location, statement, &mut bits, - &mut callback); + visitor.update_bits_and_do_callback( + statement_location, + statement, + &mut bits, + &mut callback, + ); } } } @@ -225,11 +294,9 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Opti // We let Call define the result in both the success and // unwind cases. This is not really correct, however it // does not seem to be observable due to the way that we - // generate MIR. See the test case - // `mir-opt/nll/liveness-call-subtlety.rs`. To do things - // properly, we would apply the def in call only to the - // input from the success path and not the unwind - // path. -nmatsakis + // generate MIR. To do things properly, we would apply + // the def in call only to the input from the success + // path and not the unwind path. -nmatsakis PlaceContext::Call | // Storage live and storage dead aren't proper defines, but we can ignore @@ -281,28 +348,33 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Opti } } -struct DefsUsesVisitor { +struct DefsUsesVisitor<'lv, V, M> +where + V: Idx, + M: LiveVariableMap + 'lv, +{ mode: LivenessMode, - defs_uses: DefsUses, + map: &'lv M, + defs_uses: DefsUses, } #[derive(Eq, PartialEq, Clone)] -struct DefsUses { - defs: LocalSet, - uses: LocalSet, +struct DefsUses { + defs: LiveVarSet, + uses: LiveVarSet, } -impl DefsUses { +impl DefsUses { fn clear(&mut self) { self.uses.clear(); self.defs.clear(); } - fn apply(&self, bits: &mut LocalSet) -> bool { + fn apply(&self, bits: &mut LiveVarSet) -> bool { bits.subtract(&self.defs) | bits.union(&self.uses) } - fn add_def(&mut self, index: Local) { + fn add_def(&mut self, index: V) { // If it was used already in the block, remove that use // now that we found a definition. // @@ -316,7 +388,7 @@ impl DefsUses { self.defs.add(&index); } - fn add_use(&mut self, index: Local) { + fn add_use(&mut self, index: V) { // Inverse of above. // // Example: @@ -332,15 +404,22 @@ impl DefsUses { } } -impl DefsUsesVisitor { +impl<'lv, V, M> DefsUsesVisitor<'lv, V, M> +where + V: Idx, + M: LiveVariableMap, +{ /// Update `bits` with the effects of `value` and call `callback`. We /// should always visit in reverse order. This method assumes that we have /// not visited anything before; if you have, clear `bits` first. - fn update_bits_and_do_callback<'tcx, OP>(&mut self, location: Location, - value: &impl MirVisitable<'tcx>, bits: &mut LocalSet, - callback: &mut OP) - where - OP: FnMut(Location, &LocalSet), + fn update_bits_and_do_callback<'tcx, OP>( + &mut self, + location: Location, + value: &impl MirVisitable<'tcx>, + bits: &mut LiveVarSet, + callback: &mut OP, + ) where + OP: FnMut(Location, &LiveVarSet), { value.apply(location, self); self.defs_uses.apply(bits); @@ -348,28 +427,34 @@ impl DefsUsesVisitor { } } -impl<'tcx> Visitor<'tcx> for DefsUsesVisitor { +impl<'tcx, 'lv, V, M> Visitor<'tcx> for DefsUsesVisitor<'lv, V, M> +where + V: Idx, + M: LiveVariableMap, +{ fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) { - match categorize(context, self.mode) { - Some(DefUse::Def) => { - self.defs_uses.add_def(local); + if let Some(v_index) = self.map.from_local(local) { + match categorize(context, self.mode) { + Some(DefUse::Def) => self.defs_uses.add_def(v_index), + Some(DefUse::Use) => self.defs_uses.add_use(v_index), + None => (), } - - Some(DefUse::Use) => { - self.defs_uses.add_use(local); - } - - None => {} } } } -fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses { +fn block<'tcx, V: Idx>( + mode: LivenessMode, + map: &impl LiveVariableMap, + b: &BasicBlockData<'tcx>, + locals: usize, +) -> DefsUses { let mut visitor = DefsUsesVisitor { mode, + map, defs_uses: DefsUses { - defs: LocalSet::new_empty(locals), - uses: LocalSet::new_empty(locals), + defs: LiveVarSet::new_empty(locals), + uses: LiveVarSet::new_empty(locals), }, }; @@ -388,12 +473,13 @@ fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> D visitor.defs_uses } -pub fn dump_mir<'a, 'tcx>( +pub fn dump_mir<'a, 'tcx, V: Idx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, source: MirSource, mir: &Mir<'tcx>, - result: &LivenessResult, + map: &impl LiveVariableMap, + result: &LivenessResult, ) { if !dump_enabled(tcx, pass_name, source) { return; @@ -402,16 +488,17 @@ pub fn dump_mir<'a, 'tcx>( // see notes on #41697 below tcx.item_path_str(source.def_id) }); - dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result); + dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, map, result); } -fn dump_matched_mir_node<'a, 'tcx>( +fn dump_matched_mir_node<'a, 'tcx, V: Idx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, node_path: &str, source: MirSource, mir: &Mir<'tcx>, - result: &LivenessResult, + map: &dyn LiveVariableMap, + result: &LivenessResult, ) { let mut file_path = PathBuf::new(); file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); @@ -423,25 +510,25 @@ fn dump_matched_mir_node<'a, 'tcx>( writeln!(file, "// source = {:?}", source)?; writeln!(file, "// pass_name = {}", pass_name)?; writeln!(file, "")?; - write_mir_fn(tcx, source, mir, &mut file, result)?; + write_mir_fn(tcx, source, mir, map, &mut file, result)?; Ok(()) }); } -pub fn write_mir_fn<'a, 'tcx>( +pub fn write_mir_fn<'a, 'tcx, V: Idx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &Mir<'tcx>, + map: &dyn LiveVariableMap, w: &mut dyn Write, - result: &LivenessResult, + result: &LivenessResult, ) -> io::Result<()> { write_mir_intro(tcx, src, mir, w)?; for block in mir.basic_blocks().indices() { - let print = |w: &mut dyn Write, prefix, result: &IndexVec| { - let live: Vec = mir.local_decls - .indices() - .filter(|i| result[block].contains(i)) - .map(|i| format!("{:?}", i)) + let print = |w: &mut dyn Write, prefix, result: &IndexVec>| { + let live: Vec = result[block].iter() + .map(|v| map.from_live_var(v)) + .map(|local| format!("{:?}", local)) .collect(); writeln!(w, "{} {{{}}}", prefix, live.join(", ")) }; diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs deleted file mode 100644 index 5fdea4208df..00000000000 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags:-Zborrowck=mir - -fn can_panic() -> Box { - Box::new(44) -} - -fn main() { - let mut x = Box::new(22); - x = can_panic(); -} - -// Check that: -// - `_1` is the variable corresponding to `x` -// and -// - `_1` is live when `can_panic` is called (because it may be dropped) -// -// END RUST SOURCE -// START rustc.main.nll.0.mir -// bb0: { -// | Live variables on entry to bb0[0]: [] -// StorageLive(_1); -// | Live variables on entry to bb0[1]: [] -// _1 = const >::new(const 22usize) -> [return: bb2, unwind: bb1]; -// | Live variables on exit from bb0: [_1 (drop)] -// } -// END rustc.main.nll.0.mir -// START rustc.main.nll.0.mir -// bb2: { -// | Live variables on entry to bb2[0]: [_1 (drop)] -// StorageLive(_2); -// | Live variables on entry to bb2[1]: [_1 (drop)] -// _2 = const can_panic() -> [return: bb3, unwind: bb4]; -// | Live variables on exit from bb2: [_1 (drop), _2] -// } -// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs deleted file mode 100644 index 001499b657d..00000000000 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags:-Zborrowck=mir - -#![allow(warnings)] - -fn use_x(_: usize) -> bool { true } - -fn main() { - let mut x = 22; - loop { - // Key point: `x` not live on entry to this basic block. - x = 55; - if use_x(x) { break; } - } -} - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// bb3: { -// | Live variables on entry to bb3[0]: [] -// _1 = const 55usize; -// | Live variables on entry to bb3[1]: [_1] -// StorageLive(_3); -// | Live variables on entry to bb3[2]: [_1] -// StorageLive(_4); -// | Live variables on entry to bb3[3]: [_1] -// _4 = _1; -// | Live variables on entry to bb3[4]: [_4] -// _3 = const use_x(move _4) -> [return: bb4, unwind: bb1]; -// | Live variables on exit from bb3: [_3] -// } -// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs deleted file mode 100644 index fbe20d76ea7..00000000000 --- a/src/test/mir-opt/nll/liveness-interblock.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags:-Zborrowck=mir - -fn cond() -> bool { false } - -fn make_live(_: usize) { } - -fn make_dead() { } - -fn main() { - let x = 5; - - if cond() { - make_live(x); - } else { - // x should be dead on entry to this block - make_dead(); - } -} - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// bb3: { -// | Live variables on entry to bb3[0]: [_1] -// StorageLive(_4); -// | Live variables on entry to bb3[1]: [_1] -// _4 = _1; -// | Live variables on entry to bb3[2]: [_4] -// _3 = const make_live(move _4) -> [return: bb5, unwind: bb1]; -// | Live variables on exit from bb3: [] -// } -// END rustc.main.nll.0.mir -// START rustc.main.nll.0.mir -// bb4: { -// | Live variables on entry to bb4[0]: [] -// _5 = const make_dead() -> [return: bb6, unwind: bb1]; -// | Live variables on exit from bb4: [] -// } -// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs deleted file mode 100644 index 187d9e6ca89..00000000000 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Basic test for liveness constraints: the region (`R1`) that appears -// in the type of `p` includes the points after `&v[0]` up to (but not -// including) the call to `use_x`. The `else` branch is not included. - -// compile-flags:-Zborrowck=mir -Zverbose -// ^^^^^^^^^ force compiler to dump more region information - -#![allow(warnings)] - -fn use_x(_: usize) -> bool { true } - -fn main() { - let mut v = [1, 2, 3]; - let p = &v[0]; - if true { - use_x(*p); - } else { - use_x(22); - } -} - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// | '_#2r | {bb2[0..=1], bb3[0..=1]} -// | '_#3r | {bb2[1], bb3[0..=1]} -// ... -// let _2: &'_#3r usize; -// END rustc.main.nll.0.mir -// START rustc.main.nll.0.mir -// bb2: { -// | Live variables on entry to bb2[0]: [_1, _3] -// _2 = &'_#2r _1[_3]; -// | Live variables on entry to bb2[1]: [_2] -// switchInt(const true) -> [false: bb4, otherwise: bb3]; -// | Live variables on exit from bb2: [_2] -// } -// END rustc.main.nll.0.mir -// START rustc.main.nll.0.mir -// bb3: { -// | Live variables on entry to bb3[0]: [_2] -// StorageLive(_7); -// | Live variables on entry to bb3[1]: [_2] -// _7 = (*_2); -// | Live variables on entry to bb3[2]: [_7] -// _6 = const use_x(move _7) -> [return: bb5, unwind: bb1]; -// | Live variables on exit from bb3: [] -// } -// END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs deleted file mode 100644 index 62064fa94f2..00000000000 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test for the subregion constraints. In this case, the region R3 on -// `p` includes two disjoint regions of the control-flow graph. The -// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3, -// but only at a particular point, and hence they wind up including -// distinct regions. -// -// FIXME(#43234) -- Well, this used to be true, but we modified NLL -// for the time being to not take location into account. - -// compile-flags:-Zborrowck=mir -Zverbose -// ^^^^^^^^^ force compiler to dump more region information - -#![allow(warnings)] - -fn use_x(_: usize) -> bool { true } - -fn main() { - let mut v = [1, 2, 3]; - let mut p = &v[0]; - if true { - use_x(*p); - } else { - use_x(22); - } - - p = &v[1]; - use_x(*p); -} - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// | '_#2r | {bb2[0..=1], bb3[0..=1], bb8[2..=4]} -// ... -// | '_#4r | {bb2[1], bb3[0..=1], bb8[1..=4]} -// | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]} -// ... -// let mut _2: &'_#5r usize; -// ... -// _2 = &'_#2r _1[_3]; -// ... -// _2 = &'_#4r (*_10); -// END rustc.main.nll.0.mir