]> git.lizzy.rs Git - rust.git/commitdiff
avoid computing liveness when a variable doesn't need it
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 7 Aug 2018 18:50:36 +0000 (14:50 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Tue, 7 Aug 2018 19:05:16 +0000 (15:05 -0400)
In particular, we skip computing liveness for a variable X if all the
regions in its type are known to outlive free regions.

src/librustc_mir/borrow_check/nll/constraints/graph.rs
src/librustc_mir/borrow_check/nll/constraints/mod.rs
src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs
src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs
src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr
src/test/ui/nll/get_default.nll.stderr
src/test/ui/nll/get_default.stderr
src/test/ui/nll/return-ref-mut-issue-46557.stderr

index 6fd6c41bebe83824df09585ee725b916b93bcc4c..1a1094b570bd107ca5de46a493a8874f75eb554a 100644 (file)
@@ -24,6 +24,8 @@
 
 crate type NormalConstraintGraph = ConstraintGraph<Normal>;
 
+crate type ReverseConstraintGraph = ConstraintGraph<Reverse>;
+
 /// Marker trait that controls whether a `R1: R2` constraint
 /// represents an edge `R1 -> R2` or `R2 -> R1`.
 crate trait ConstraintGraphDirecton: Copy + 'static {
index f5cab56bd29ee62fec8fea83483e0df8b9fc1460..4cb92262ff08590a9d2c6f8ab00f4c25419eb13d 100644 (file)
@@ -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.
index e564118197feae3bab66df5bc94d6f37efff73e8..20bf53f4d953b9ff05202a5d17a9809116ca1931 100644 (file)
 //! 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<Local, Option<LocalWithRegion>>,
-    /// For each LocalWithRegion, maps back to the original Local index.
+
+    /// For each `LocalWithRegion`, maps back to the original `Local` index.
     pub to_local: IndexVec<LocalWithRegion, Local>,
 }
 
@@ -51,21 +55,32 @@ fn num_variables(&self) -> usize {
 }
 
 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<RegionVid>,
+        mir: &Mir<'tcx>,
+    ) -> Self {
         let mut to_local = IndexVec::default();
         let from_local: IndexVec<Local, Option<_>> = 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,
index 7b52bb65c189226fcf0e331d623a30a5df69f199..d3ef2a369094a733964b9839def3885fc6e4e684 100644 (file)
@@ -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};
 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<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
     move_data: &MoveData<'tcx>,
 ) -> (LivenessResults<LocalWithRegion>, 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<RegionVid> {
+    // Build a graph of the outlives constraints thus far. This is
+    // a reverse graph, so for each constraint `R1: R2` we have an
+    // edge `R2 -> R1`. Therefore, if we find all regions
+    // reachable from each free region, we will have all the
+    // regions that are forced to outlive some free region.
+    let rev_constraint_graph = constraint_set.reverse_graph(num_region_vars);
+    let rev_region_graph = rev_constraint_graph.region_graph(constraint_set);
+
+    // Stack for the depth-first search. Start out with all the free regions.
+    let mut stack: Vec<_> = universal_regions.universal_regions().collect();
+
+    // Set of all free regions, plus anything that outlives them. Initially
+    // just contains the free regions.
+    let mut outlives_free_region: FxHashSet<_> = stack.iter().cloned().collect();
+
+    // Do the DFS -- for each thing in the stack, find all things
+    // that outlive it and add them to the set. If they are not,
+    // push them onto the stack for later.
+    while let Some(sub_region) = stack.pop() {
+        stack.extend(
+            rev_region_graph
+                .outgoing_regions(sub_region)
+                .filter(|&r| outlives_free_region.insert(r)),
+        );
+    }
+
+    // Return the final set of things we visited.
+    outlives_free_region
+}
+
 struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
 where
     'typeck: 'gen,
@@ -182,8 +232,13 @@ fn push_type_live_constraint<T>(
 
         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);
index 95acdab3e80012e3c7c94d179e2667901c7fc3b2..52f1547bce6f86f33b7bcd359e41186aef326d06 100644 (file)
@@ -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
index b955a51e38d73433184f516491839ff030d7074c..580dce3c0fe632691f65bc1d8d35d1e153275397 100644 (file)
@@ -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
index 75194bf55bc9f1a3b9864d3648790578ea782852..2f8eab907c7bb5185353420d28460e9ac2cd38e6 100644 (file)
@@ -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
index f40e38c63f5ac28634c488df2b68d67a60134e13..f441085f242edddc102c2078ba1fd1691134b523 100644 (file)
@@ -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