diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index 6fd6c41bebe8..1a1094b570bd 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 f5cab56bd29e..4cb92262ff08 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 e564118197fe..20bf53f4d953 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 7b52bb65c189..d3ef2a369094 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 95acdab3e800..52f1547bce6f 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 b955a51e38d7..580dce3c0fe6 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 75194bf55bc9..2f8eab907c7b 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 f40e38c63f5a..f441085f242e 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