From fb1702f3010d8aedb54df5f4ed631e98fca9cb02 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 10:00:04 -0400 Subject: [PATCH 1/9] compute liveness later --- src/librustc_mir/borrow_check/nll/mod.rs | 22 ++++++----- .../borrow_check/nll/type_check/liveness.rs | 32 ++++++++------- .../borrow_check/nll/type_check/mod.rs | 39 +++++++++++-------- 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 973568a67f0..262d4a5cd3d 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -11,7 +11,7 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::{LocationIndex, LocationTable}; use borrow_check::nll::facts::AllFactsExt; -use borrow_check::nll::type_check::MirTypeckRegionConstraints; +use borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints}; use borrow_check::nll::region_infer::values::RegionValueElements; use borrow_check::nll::liveness_map::{NllLivenessMap, LocalWithRegion}; use dataflow::indexes::BorrowIndex; @@ -109,9 +109,12 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( let elements = &Rc::new(RegionValueElements::new(mir)); // Run the MIR type-checker. - let liveness_map = NllLivenessMap::compute(&mir); - let liveness = LivenessResults::compute(mir, &liveness_map); - let (constraint_sets, universal_region_relations) = type_check::type_check( + let MirTypeckResults { + constraints, + universal_region_relations, + liveness, + liveness_map, + } = type_check::type_check( infcx, param_env, mir, @@ -119,7 +122,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( &universal_regions, location_table, borrow_set, - &liveness, &mut all_facts, flow_inits, move_data, @@ -141,7 +143,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( mut liveness_constraints, outlives_constraints, type_tests, - } = constraint_sets; + } = constraints; constraint_generation::generate_constraints( infcx, @@ -205,6 +207,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( dump_mir_results( infcx, &liveness, + &liveness_map, MirSource::item(def_id), &mir, ®ioncx, @@ -221,6 +224,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_map: &NllLivenessMap, source: MirSource, mir: &Mir<'tcx>, regioncx: &RegionInferenceContext, @@ -230,8 +234,6 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( return; } - let map = &NllLivenessMap::compute(mir); - let regular_liveness_per_location: FxHashMap<_, _> = mir .basic_blocks() .indices() @@ -239,7 +241,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( let mut results = vec![]; liveness .regular - .simulate_block(&mir, bb, map, |location, local_set| { + .simulate_block(&mir, bb, liveness_map, |location, local_set| { results.push((location, local_set.clone())); }); results @@ -253,7 +255,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( let mut results = vec![]; liveness .drop - .simulate_block(&mir, bb, map, |location, local_set| { + .simulate_block(&mir, bb, liveness_map, |location, local_set| { results.push((location, local_set.clone())); }); results 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 2b9307db59a..f15881ee3c8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -36,23 +36,29 @@ use super::TypeChecker; pub(super) fn generate<'gcx, 'tcx>( cx: &mut TypeChecker<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, - liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, -) { - let mut generator = TypeLivenessGenerator { - cx, - mir, - liveness, - flow_inits, - move_data, - drop_data: FxHashMap(), - map: &NllLivenessMap::compute(mir), - }; +) -> (LivenessResults, NllLivenessMap) { + let liveness_map = NllLivenessMap::compute(&mir); + let liveness = LivenessResults::compute(mir, &liveness_map); - for bb in mir.basic_blocks().indices() { - generator.add_liveness_constraints(bb); + { + 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) } struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> 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 a18e2368bf7..4ca84df445e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -15,6 +15,7 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::LocationTable; use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; +use borrow_check::nll::liveness_map::NllLivenessMap; use borrow_check::nll::region_infer::values::{RegionValueElements, LivenessValues}; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; use borrow_check::nll::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; @@ -115,16 +116,12 @@ pub(crate) fn type_check<'gcx, 'tcx>( universal_regions: &Rc>, location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, - liveness: &LivenessResults, all_facts: &mut Option, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, elements: &Rc, errors_buffer: &mut Vec, -) -> ( - MirTypeckRegionConstraints<'tcx>, - Rc>, -) { +) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { liveness_constraints: LivenessValues::new(elements), @@ -147,7 +144,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( all_facts, ); - { + let (liveness, liveness_map) = { let mut borrowck_context = BorrowCheckContext { universal_regions, location_table, @@ -166,7 +163,6 @@ pub(crate) fn type_check<'gcx, 'tcx>( Some(&mut borrowck_context), Some(errors_buffer), |cx| { - liveness::generate(cx, mir, liveness, flow_inits, move_data); cx.equate_inputs_and_outputs( mir, mir_def_id, @@ -174,14 +170,20 @@ pub(crate) fn type_check<'gcx, 'tcx>( &universal_region_relations, &normalized_inputs_and_output, ); + liveness::generate(cx, mir, flow_inits, move_data) }, - ); - } + ) + }; - (constraints, universal_region_relations) + MirTypeckResults { + constraints, + universal_region_relations, + liveness, + liveness_map, + } } -fn type_check_internal<'a, 'gcx, 'tcx, F>( +fn type_check_internal<'a, 'gcx, 'tcx, R>( infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir_def_id: DefId, param_env: ty::ParamEnv<'gcx>, @@ -190,10 +192,8 @@ fn type_check_internal<'a, 'gcx, 'tcx, F>( implicit_region_bound: Option>, borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>, errors_buffer: Option<&mut Vec>, - mut extra: F, -) where - F: FnMut(&mut TypeChecker<'a, 'gcx, 'tcx>), -{ + mut extra: impl FnMut(&mut TypeChecker<'a, 'gcx, 'tcx>) -> R, +) -> R where { let mut checker = TypeChecker::new( infcx, mir, @@ -214,7 +214,7 @@ fn type_check_internal<'a, 'gcx, 'tcx, F>( checker.typeck_mir(mir, errors_buffer); } - extra(&mut checker); + extra(&mut checker) } fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { @@ -655,6 +655,13 @@ struct BorrowCheckContext<'a, 'tcx: 'a> { constraints: &'a mut MirTypeckRegionConstraints<'tcx>, } +crate struct MirTypeckResults<'tcx> { + crate constraints: MirTypeckRegionConstraints<'tcx>, + crate universal_region_relations: Rc>, + crate liveness: LivenessResults, + crate liveness_map: NllLivenessMap, +} + /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. crate struct MirTypeckRegionConstraints<'tcx> { From db7a07cfc6cdb54dff024fa4f809e139a92c067c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 10:07:35 -0400 Subject: [PATCH 2/9] update comment to more accurately describe the limitations --- src/librustc_mir/borrow_check/nll/constraints/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index 597241234cd..289557a9a07 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -37,9 +37,11 @@ impl ConstraintSet { } /// Constructs a graph from the constraint set; the graph makes it - /// easy to find the constraints affecting a particular region - /// (you should not mutate the set once this graph is - /// constructed). + /// easy to find the constraints affecting a particular region. + /// + /// NB: This graph contains a "frozen" view of the current + /// constraints. any new constraints added to the `ConstraintSet` + /// after the graph is built will not be present in the graph. crate fn graph(&self, num_region_vars: usize) -> graph::ConstraintGraph { graph::ConstraintGraph::new(self, num_region_vars) } From 5147d383d3552f9544c785258411377f3f98f048 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 13:40:00 -0400 Subject: [PATCH 3/9] promote liveness to a directory module --- .../borrow_check/nll/type_check/{liveness.rs => liveness/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/librustc_mir/borrow_check/nll/type_check/{liveness.rs => liveness/mod.rs} (100%) diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs similarity index 100% rename from src/librustc_mir/borrow_check/nll/type_check/liveness.rs rename to src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs From d1ce8e846bcef6b5ca8c42140c882d84c585b65b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 13:45:37 -0400 Subject: [PATCH 4/9] move `liveness_map` into the `liveness` module --- src/librustc_mir/borrow_check/nll/mod.rs | 3 +-- .../nll/{ => type_check/liveness}/liveness_map.rs | 0 src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs | 2 ++ src/librustc_mir/borrow_check/nll/type_check/mod.rs | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) rename src/librustc_mir/borrow_check/nll/{ => type_check/liveness}/liveness_map.rs (100%) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 262d4a5cd3d..f54d80d5f4f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -12,8 +12,8 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::{LocationIndex, LocationTable}; use borrow_check::nll::facts::AllFactsExt; use borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints}; +use borrow_check::nll::type_check::liveness::liveness_map::{NllLivenessMap, LocalWithRegion}; 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; @@ -47,7 +47,6 @@ crate mod region_infer; mod renumber; crate mod type_check; mod universal_regions; -crate mod liveness_map; mod constraints; diff --git a/src/librustc_mir/borrow_check/nll/liveness_map.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs similarity index 100% rename from src/librustc_mir/borrow_check/nll/liveness_map.rs rename to src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs index f15881ee3c8..7b52bb65c18 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs @@ -25,6 +25,8 @@ use util::liveness::{LivenessResults, LiveVariableMap }; 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 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 4ca84df445e..eb689f0a9c6 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -15,10 +15,10 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::LocationTable; use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; -use borrow_check::nll::liveness_map::NllLivenessMap; use borrow_check::nll::region_infer::values::{RegionValueElements, LivenessValues}; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; use borrow_check::nll::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; +use borrow_check::nll::type_check::liveness::liveness_map::NllLivenessMap; use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::LocalWithRegion; use borrow_check::nll::ToRegionVid; @@ -75,7 +75,7 @@ macro_rules! span_mirbug_and_err { mod constraint_conversion; pub mod free_region_relations; mod input_output; -mod liveness; +crate mod liveness; mod relate_tys; /// Type checks the given `mir` in the context of the inference From a92bf8dd29f3219bf39cc064954cfdad05c491a8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 13:46:14 -0400 Subject: [PATCH 5/9] liveness_map: rustfmt --- .../nll/type_check/liveness/liveness_map.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs index cbd9c9a4e1a..e564118197f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs @@ -16,9 +16,9 @@ //! liveness code so that it only operates over variables with regions in their //! types, instead of all variables. +use rustc::mir::{Local, Mir}; 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; @@ -32,11 +32,9 @@ crate struct NllLivenessMap { 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] } @@ -57,19 +55,21 @@ impl NllLivenessMap { /// 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 + 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 } - else { - None - } - }).collect(); + }) + .collect(); - Self { from_local, to_local } + Self { + from_local, + to_local, + } } } From 887296e8c32165de83deb5156f0255c1f286ee6a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 14:14:38 -0400 Subject: [PATCH 6/9] make it possible to customize the `RegionGraph` direction --- .../borrow_check/nll/constraints/graph.rs | 110 ++++++++++++++---- .../borrow_check/nll/constraints/mod.rs | 10 +- .../borrow_check/nll/region_infer/mod.rs | 4 +- 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index 5f05ae8ade5..6fd6c41bebe 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs @@ -8,27 +8,78 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet}; +use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet, OutlivesConstraint}; use rustc::ty::RegionVid; use rustc_data_structures::graph; use rustc_data_structures::indexed_vec::IndexVec; -crate struct ConstraintGraph { +/// The construct graph organizes the constraints by their end-points. +/// It can be used to view a `R1: R2` constraint as either an edge `R1 +/// -> R2` or `R2 -> R1` depending on the direction type `D`. +crate struct ConstraintGraph { + _direction: D, first_constraints: IndexVec>, next_constraints: IndexVec>, } -impl ConstraintGraph { +crate type NormalConstraintGraph = ConstraintGraph; + +/// Marker trait that controls whether a `R1: R2` constraint +/// represents an edge `R1 -> R2` or `R2 -> R1`. +crate trait ConstraintGraphDirecton: Copy + 'static { + fn start_region(c: &OutlivesConstraint) -> RegionVid; + fn end_region(c: &OutlivesConstraint) -> RegionVid; +} + +/// In normal mode, a `R1: R2` constraint results in an edge `R1 -> +/// R2`. This is what we use when constructing the SCCs for +/// inference. This is because we compute the value of R1 by union'ing +/// all the things that it relies on. +#[derive(Copy, Clone, Debug)] +crate struct Normal; + +impl ConstraintGraphDirecton for Normal { + fn start_region(c: &OutlivesConstraint) -> RegionVid { + c.sup + } + + fn end_region(c: &OutlivesConstraint) -> RegionVid { + c.sub + } +} + +/// In reverse mode, a `R1: R2` constraint results in an edge `R2 -> +/// R1`. We use this for optimizing liveness computation, because then +/// we wish to iterate from a region (e.g., R2) to all the regions +/// that will outlive it (e.g., R1). +#[derive(Copy, Clone, Debug)] +crate struct Reverse; + +impl ConstraintGraphDirecton for Reverse { + fn start_region(c: &OutlivesConstraint) -> RegionVid { + c.sub + } + + fn end_region(c: &OutlivesConstraint) -> RegionVid { + c.sup + } +} + +impl ConstraintGraph { /// Create a "dependency graph" where each region constraint `R1: /// R2` is treated as an edge `R1 -> R2`. We use this graph to /// construct SCCs for region inference but also for error /// reporting. - crate fn new(set: &ConstraintSet, num_region_vars: usize) -> Self { + crate fn new( + direction: D, + set: &ConstraintSet, + num_region_vars: usize, + ) -> Self { let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars); let mut next_constraints = IndexVec::from_elem(None, &set.constraints); for (idx, constraint) in set.constraints.iter_enumerated().rev() { - let head = &mut first_constraints[constraint.sup]; + let head = &mut first_constraints[D::start_region(constraint)]; let next = &mut next_constraints[idx]; debug_assert!(next.is_none()); *next = *head; @@ -36,13 +87,21 @@ impl ConstraintGraph { } Self { + _direction: direction, first_constraints, next_constraints, } } + /// Given the constraint set from which this graph was built + /// creates a region graph so that you can iterate over *regions* + /// and not constraints. + crate fn region_graph<'rg>(&'rg self, set: &'rg ConstraintSet) -> RegionGraph<'rg, D> { + RegionGraph::new(set, self) + } + /// Given a region `R`, iterate over all constraints `R: R1`. - crate fn outgoing_edges(&self, region_sup: RegionVid) -> Edges<'_> { + crate fn outgoing_edges(&self, region_sup: RegionVid) -> Edges<'_, D> { let first = self.first_constraints[region_sup]; Edges { graph: self, @@ -51,12 +110,12 @@ impl ConstraintGraph { } } -crate struct Edges<'s> { - graph: &'s ConstraintGraph, +crate struct Edges<'s, D: ConstraintGraphDirecton> { + graph: &'s ConstraintGraph, pointer: Option, } -impl<'s> Iterator for Edges<'s> { +impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> { type Item = ConstraintIndex; fn next(&mut self) -> Option { @@ -69,17 +128,20 @@ impl<'s> Iterator for Edges<'s> { } } -crate struct RegionGraph<'s> { +/// This struct brings together a constraint set and a (normal, not +/// reverse) constraint graph. It implements the graph traits and is +/// usd for doing the SCC computation. +crate struct RegionGraph<'s, D: ConstraintGraphDirecton> { set: &'s ConstraintSet, - constraint_graph: &'s ConstraintGraph, + constraint_graph: &'s ConstraintGraph, } -impl<'s> RegionGraph<'s> { +impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> { /// Create a "dependency graph" where each region constraint `R1: /// R2` is treated as an edge `R1 -> R2`. We use this graph to /// construct SCCs for region inference but also for error /// reporting. - crate fn new(set: &'s ConstraintSet, constraint_graph: &'s ConstraintGraph) -> Self { + crate fn new(set: &'s ConstraintSet, constraint_graph: &'s ConstraintGraph) -> Self { Self { set, constraint_graph, @@ -88,7 +150,7 @@ impl<'s> RegionGraph<'s> { /// Given a region `R`, iterate over all regions `R1` such that /// there exists a constraint `R: R1`. - crate fn sub_regions(&self, region_sup: RegionVid) -> Successors<'_> { + crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'_, D> { Successors { set: self.set, edges: self.constraint_graph.outgoing_edges(region_sup), @@ -96,39 +158,39 @@ impl<'s> RegionGraph<'s> { } } -crate struct Successors<'s> { +crate struct Successors<'s, D: ConstraintGraphDirecton> { set: &'s ConstraintSet, - edges: Edges<'s>, + edges: Edges<'s, D>, } -impl<'s> Iterator for Successors<'s> { +impl<'s, D: ConstraintGraphDirecton> Iterator for Successors<'s, D> { type Item = RegionVid; fn next(&mut self) -> Option { - self.edges.next().map(|c| self.set[c].sub) + self.edges.next().map(|c| D::end_region(&self.set[c])) } } -impl<'s> graph::DirectedGraph for RegionGraph<'s> { +impl<'s, D: ConstraintGraphDirecton> graph::DirectedGraph for RegionGraph<'s, D> { type Node = RegionVid; } -impl<'s> graph::WithNumNodes for RegionGraph<'s> { +impl<'s, D: ConstraintGraphDirecton> graph::WithNumNodes for RegionGraph<'s, D> { fn num_nodes(&self) -> usize { self.constraint_graph.first_constraints.len() } } -impl<'s> graph::WithSuccessors for RegionGraph<'s> { +impl<'s, D: ConstraintGraphDirecton> graph::WithSuccessors for RegionGraph<'s, D> { fn successors<'graph>( &'graph self, node: Self::Node, ) -> >::Iter { - self.sub_regions(node) + self.outgoing_regions(node) } } -impl<'s, 'graph> graph::GraphSuccessors<'graph> for RegionGraph<'s> { +impl<'s, 'graph, D: ConstraintGraphDirecton> graph::GraphSuccessors<'graph> for RegionGraph<'s, D> { type Item = RegionVid; - type Iter = Successors<'graph>; + type Iter = Successors<'graph, D>; } diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index 289557a9a07..f5cab56bd29 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -36,14 +36,14 @@ impl ConstraintSet { self.constraints.push(constraint); } - /// Constructs a graph from the constraint set; the graph makes it + /// Constructs a "normal" graph from the constraint set; the graph makes it /// easy to find the constraints affecting a particular region. /// /// NB: This graph contains a "frozen" view of the current /// constraints. any new constraints added to the `ConstraintSet` /// after the graph is built will not be present in the graph. - crate fn graph(&self, num_region_vars: usize) -> graph::ConstraintGraph { - graph::ConstraintGraph::new(self, num_region_vars) + crate fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph { + graph::ConstraintGraph::new(graph::Normal, self, num_region_vars) } /// Compute cycles (SCCs) in the graph of regions. In particular, @@ -51,9 +51,9 @@ impl ConstraintSet { /// them into an SCC, and find the relationships between SCCs. crate fn compute_sccs( &self, - constraint_graph: &graph::ConstraintGraph, + constraint_graph: &graph::NormalConstraintGraph, ) -> Sccs { - let region_graph = &graph::RegionGraph::new(self, constraint_graph); + let region_graph = &constraint_graph.region_graph(self); Sccs::new(region_graph) } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index afd4e2859ac..867b05639f5 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -9,7 +9,7 @@ // except according to those terms. use super::universal_regions::UniversalRegions; -use borrow_check::nll::constraints::graph::ConstraintGraph; +use borrow_check::nll::constraints::graph::NormalConstraintGraph; use borrow_check::nll::constraints::{ ConstraintIndex, ConstraintSccIndex, ConstraintSet, OutlivesConstraint, }; @@ -61,7 +61,7 @@ pub struct RegionInferenceContext<'tcx> { /// The constraint-set, but in graph form, making it easy to traverse /// the constraints adjacent to a particular region. Used to construct /// the SCC (see `constraint_sccs`) and for error reporting. - constraint_graph: Rc, + constraint_graph: Rc, /// The SCC computed from `constraints` and the constraint graph. Used to compute the values /// of each region. From 3b7989d1dffb0d1b36dd8f4a7b11ea98ed7dc2ba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 14:50:36 -0400 Subject: [PATCH 7/9] avoid computing liveness when a variable doesn't need it In particular, we skip computing liveness for a variable X if all the regions in its type are known to outlive free regions. --- .../borrow_check/nll/constraints/graph.rs | 2 + .../borrow_check/nll/constraints/mod.rs | 6 ++ .../nll/type_check/liveness/liveness_map.rs | 47 ++++++++----- .../nll/type_check/liveness/mod.rs | 69 +++++++++++++++++-- ...mote-ref-mut-in-let-issue-46557.nll.stderr | 34 ++++----- src/test/ui/nll/get_default.nll.stderr | 15 +++- src/test/ui/nll/get_default.stderr | 15 +++- .../ui/nll/return-ref-mut-issue-46557.stderr | 17 ++--- 8 files changed, 146 insertions(+), 59 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index 6fd6c41bebe..1a1094b570b 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs @@ -24,6 +24,8 @@ crate struct ConstraintGraph { crate type NormalConstraintGraph = ConstraintGraph; +crate type ReverseConstraintGraph = ConstraintGraph; + /// Marker trait that controls whether a `R1: R2` constraint /// represents an edge `R1 -> R2` or `R2 -> R1`. crate trait ConstraintGraphDirecton: Copy + 'static { diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index f5cab56bd29..4cb92262ff0 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -46,6 +46,12 @@ impl ConstraintSet { graph::ConstraintGraph::new(graph::Normal, self, num_region_vars) } + /// Like `graph`, but constraints a reverse graph where `R1: R2` + /// represents an edge `R2 -> R1`. + crate fn reverse_graph(&self, num_region_vars: usize) -> graph::ReverseConstraintGraph { + graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars) + } + /// Compute cycles (SCCs) in the graph of regions. In particular, /// find all regions R1, R2 such that R1: R2 and R2: R1 and group /// them into an SCC, and find the relationships between SCCs. diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs index e564118197f..20bf53f4d95 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs @@ -16,21 +16,25 @@ //! liveness code so that it only operates over variables with regions in their //! types, instead of all variables. +use borrow_check::nll::ToRegionVid; use rustc::mir::{Local, Mir}; -use rustc::ty::TypeFoldable; -use rustc_data_structures::indexed_vec::IndexVec; +use rustc::ty::{RegionVid, TyCtxt}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; 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. +/// Map between Local and LocalWithRegion indices: the purpose of this +/// map is to define the subset of local variables for which we need +/// to do a liveness computation. We only need to compute whether a +/// variable `X` is live if that variable contains some region `R` in +/// its type where `R` is not known to outlive a free region (i.e., +/// where `R` may be valid for just a subset of the fn body). crate struct NllLivenessMap { - /// For each local variable, contains either None (if the type has no regions) - /// or Some(i) with a suitable index. + /// For each local variable, contains `Some(i)` if liveness is + /// needed for this variable. pub from_local: IndexVec>, - /// For each LocalWithRegion, maps back to the original Local index. + + /// For each `LocalWithRegion`, maps back to the original `Local` index. pub to_local: IndexVec, } @@ -51,21 +55,32 @@ impl LiveVariableMap for NllLivenessMap { } 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 { + crate fn compute( + tcx: TyCtxt<'_, '_, 'tcx>, + free_regions: &FxHashSet, + mir: &Mir<'tcx>, + ) -> 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 { + if tcx.all_free_regions_meet(&local_decl.ty, |r| { + free_regions.contains(&r.to_region_vid()) + }) { + // If all the regions in the type are free regions + // (or there are no regions), then we don't need + // to track liveness for this variable. None + } else { + Some(to_local.push(local)) } }) .collect(); + debug!("{} total variables", mir.local_decls.len()); + debug!("{} variables need liveness", to_local.len()); + debug!("{} regions outlive free regions", free_regions.len()); + Self { from_local, to_local, diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs index 7b52bb65c18..d3ef2a36909 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs @@ -8,8 +8,10 @@ // 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::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}; @@ -18,10 +20,10 @@ 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::{Ty, TypeFoldable}; -use rustc_data_structures::fx::FxHashMap; +use rustc::ty::{RegionVid, Ty, TypeFoldable}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use std::rc::Rc; -use util::liveness::{LivenessResults, LiveVariableMap }; +use util::liveness::{LiveVariableMap, LivenessResults}; use super::TypeChecker; @@ -41,9 +43,18 @@ pub(super) fn generate<'gcx, 'tcx>( flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) -> (LivenessResults, NllLivenessMap) { - let liveness_map = NllLivenessMap::compute(&mir); + 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. { let mut generator = TypeLivenessGenerator { cx, @@ -63,6 +74,45 @@ pub(super) fn generate<'gcx, 'tcx>( (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 { + // 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, @@ -182,8 +232,13 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo 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); + 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); diff --git a/src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr b/src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr index 95acdab3e80..52f1547bce6 100644 --- a/src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr +++ b/src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr @@ -1,30 +1,24 @@ error[E0597]: borrowed value does not live long enough --> $DIR/promote-ref-mut-in-let-issue-46557.rs:15:21 | -LL | fn gimme_static_mut_let() -> &'static mut u32 { - | _______________________________________________- -LL | | let ref mut x = 1234543; //~ ERROR - | | ^^^^^^^ temporary value does not live long enough -LL | | x -LL | | } - | | - - | | | - | |_temporary value only lives until here - | borrow later used here +LL | let ref mut x = 1234543; //~ ERROR + | ^^^^^^^ temporary value does not live long enough +LL | x +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... error[E0597]: borrowed value does not live long enough --> $DIR/promote-ref-mut-in-let-issue-46557.rs:20:25 | -LL | fn gimme_static_mut_let_nested() -> &'static mut u32 { - | ______________________________________________________- -LL | | let (ref mut x, ) = (1234543, ); //~ ERROR - | | ^^^^^^^^^^^ temporary value does not live long enough -LL | | x -LL | | } - | | - - | | | - | |_temporary value only lives until here - | borrow later used here +LL | let (ref mut x, ) = (1234543, ); //~ ERROR + | ^^^^^^^^^^^ temporary value does not live long enough +LL | x +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... error[E0597]: borrowed value does not live long enough --> $DIR/promote-ref-mut-in-let-issue-46557.rs:25:11 diff --git a/src/test/ui/nll/get_default.nll.stderr b/src/test/ui/nll/get_default.nll.stderr index b955a51e38d..580dce3c0fe 100644 --- a/src/test/ui/nll/get_default.nll.stderr +++ b/src/test/ui/nll/get_default.nll.stderr @@ -63,9 +63,18 @@ LL | match map.get() { LL | Some(v) => { LL | map.set(String::new()); // Both AST and MIR error here | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here -... -LL | return v; - | - borrow later used here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1... + --> $DIR/get_default.rs:41:1 + | +LL | / fn err(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) --> $DIR/get_default.rs:51:17 diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr index 75194bf55bc..2f8eab907c7 100644 --- a/src/test/ui/nll/get_default.stderr +++ b/src/test/ui/nll/get_default.stderr @@ -63,9 +63,18 @@ LL | match map.get() { LL | Some(v) => { LL | map.set(String::new()); // Both AST and MIR error here | ^^^ mutable borrow occurs here -... -LL | return v; - | - borrow later used here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1... + --> $DIR/get_default.rs:41:1 + | +LL | / fn err(map: &mut Map) -> &String { +LL | | loop { +LL | | match map.get() { +LL | | Some(v) => { +... | +LL | | } +LL | | } + | |_^ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir) --> $DIR/get_default.rs:51:17 diff --git a/src/test/ui/nll/return-ref-mut-issue-46557.stderr b/src/test/ui/nll/return-ref-mut-issue-46557.stderr index f40e38c63f5..f441085f242 100644 --- a/src/test/ui/nll/return-ref-mut-issue-46557.stderr +++ b/src/test/ui/nll/return-ref-mut-issue-46557.stderr @@ -1,16 +1,13 @@ error[E0597]: borrowed value does not live long enough --> $DIR/return-ref-mut-issue-46557.rs:17:21 | -LL | fn gimme_static_mut() -> &'static mut u32 { - | ___________________________________________- -LL | | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597] - | | ^^^^^^^ temporary value does not live long enough -LL | | x -LL | | } - | | - - | | | - | |_temporary value only lives until here - | borrow later used here +LL | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597] + | ^^^^^^^ temporary value does not live long enough +LL | x +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... error: aborting due to previous error From 1bae4f5aecbd6c7a4daa09540e7e71f73b9a5c7a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Aug 2018 18:01:29 -0400 Subject: [PATCH 8/9] optimize redundant borrows --- src/librustc_mir/borrow_check/borrow_set.rs | 2 +- src/librustc_mir/borrow_check/place_ext.rs | 26 ++++++++++++++++----- src/librustc_mir/dataflow/impls/borrows.rs | 2 +- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index fe33dc0a58a..2e0cbf4326b 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs @@ -159,7 +159,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { location: mir::Location, ) { if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue { - if borrowed_place.is_unsafe_place(self.tcx, self.mir) { + if borrowed_place.ignore_borrow(self.tcx, self.mir) { return; } diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index b0517c5e61f..be0091068c2 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -15,8 +15,11 @@ use rustc::ty::{self, TyCtxt}; /// Extension methods for the `Place` type. crate trait PlaceExt<'tcx> { - /// True if this is a deref of a raw pointer. - fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool; + /// Returns true if we can safely ignore borrows of this place. + /// This is true whenever there is no action that the user can do + /// to the place `self` that would invalidate the borrow. This is true + /// for borrows of raw pointer dereferents as well as shared references. + fn ignore_borrow(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool; /// If this is a place like `x.f.g`, returns the local /// `x`. Returns `None` if this is based in a static. @@ -24,7 +27,7 @@ crate trait PlaceExt<'tcx> { } impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { - fn is_unsafe_place(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool { + fn ignore_borrow(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool { match self { Place::Promoted(_) | Place::Local(_) => false, @@ -36,12 +39,23 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => proj.base.is_unsafe_place(tcx, mir), + | ProjectionElem::Index(_) => proj.base.ignore_borrow(tcx, mir), + ProjectionElem::Deref => { let ty = proj.base.ty(mir, tcx).to_ty(tcx); match ty.sty { - ty::TyRawPtr(..) => true, - _ => proj.base.is_unsafe_place(tcx, mir), + // For both derefs of raw pointers and `&T` + // references, the original path is `Copy` and + // therefore not significant. In particular, + // there is nothing the user can do to the + // original path that would invalidate the + // newly created reference -- and if there + // were, then the user could have copied the + // original path into a new variable and + // borrowed *that* one, leaving the original + // path unborrowed. + ty::TyRawPtr(..) | ty::TyRef(_, _, hir::MutImmutable) => true, + _ => proj.base.ignore_borrow(tcx, mir), } } }, diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index ea59e42fd47..b0c4d37814e 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -233,7 +233,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { // propagate_call_return method. if let mir::Rvalue::Ref(region, _, ref place) = *rhs { - if place.is_unsafe_place(self.tcx, self.mir) { return; } + if place.ignore_borrow(self.tcx, self.mir) { return; } let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| { panic!("could not find BorrowIndex for location {:?}", location); }); From ff7f6d57de1112805272bae57ff5918d4f25e0ab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Aug 2018 04:17:46 -0400 Subject: [PATCH 9/9] don't walk MIR if no local variables need liveness This is true for tuple-stress and html5ever --- .../borrow_check/nll/type_check/liveness/liveness_map.rs | 5 +++++ src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs index 20bf53f4d95..89e8c76b22f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs @@ -86,6 +86,11 @@ impl NllLivenessMap { to_local, } } + + /// True if there are no local variables that need liveness computation. + crate fn is_empty(&self) -> bool { + self.to_local.is_empty() + } } /// Index given to each local variable whose type contains a region. diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs index d3ef2a36909..a9b69cfe761 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs @@ -55,7 +55,7 @@ pub(super) fn generate<'gcx, 'tcx>( 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,