Skip to content

Commit 3b7989d

Browse files
committed
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.
1 parent 887296e commit 3b7989d

File tree

8 files changed

+146
-59
lines changed

8 files changed

+146
-59
lines changed

src/librustc_mir/borrow_check/nll/constraints/graph.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ crate struct ConstraintGraph<D: ConstraintGraphDirecton> {
2424

2525
crate type NormalConstraintGraph = ConstraintGraph<Normal>;
2626

27+
crate type ReverseConstraintGraph = ConstraintGraph<Reverse>;
28+
2729
/// Marker trait that controls whether a `R1: R2` constraint
2830
/// represents an edge `R1 -> R2` or `R2 -> R1`.
2931
crate trait ConstraintGraphDirecton: Copy + 'static {

src/librustc_mir/borrow_check/nll/constraints/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ impl ConstraintSet {
4646
graph::ConstraintGraph::new(graph::Normal, self, num_region_vars)
4747
}
4848

49+
/// Like `graph`, but constraints a reverse graph where `R1: R2`
50+
/// represents an edge `R2 -> R1`.
51+
crate fn reverse_graph(&self, num_region_vars: usize) -> graph::ReverseConstraintGraph {
52+
graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
53+
}
54+
4955
/// Compute cycles (SCCs) in the graph of regions. In particular,
5056
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
5157
/// them into an SCC, and find the relationships between SCCs.

src/librustc_mir/borrow_check/nll/type_check/liveness/liveness_map.rs

+31-16
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,25 @@
1616
//! liveness code so that it only operates over variables with regions in their
1717
//! types, instead of all variables.
1818
19+
use borrow_check::nll::ToRegionVid;
1920
use rustc::mir::{Local, Mir};
20-
use rustc::ty::TypeFoldable;
21-
use rustc_data_structures::indexed_vec::IndexVec;
21+
use rustc::ty::{RegionVid, TyCtxt};
22+
use rustc_data_structures::fx::FxHashSet;
23+
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
2224
use util::liveness::LiveVariableMap;
2325

24-
use rustc_data_structures::indexed_vec::Idx;
25-
26-
/// Map between Local and LocalWithRegion indices: this map is supplied to the
27-
/// liveness code so that it will only analyze those variables whose types
28-
/// contain regions.
26+
/// Map between Local and LocalWithRegion indices: the purpose of this
27+
/// map is to define the subset of local variables for which we need
28+
/// to do a liveness computation. We only need to compute whether a
29+
/// variable `X` is live if that variable contains some region `R` in
30+
/// its type where `R` is not known to outlive a free region (i.e.,
31+
/// where `R` may be valid for just a subset of the fn body).
2932
crate struct NllLivenessMap {
30-
/// For each local variable, contains either None (if the type has no regions)
31-
/// or Some(i) with a suitable index.
33+
/// For each local variable, contains `Some(i)` if liveness is
34+
/// needed for this variable.
3235
pub from_local: IndexVec<Local, Option<LocalWithRegion>>,
33-
/// For each LocalWithRegion, maps back to the original Local index.
36+
37+
/// For each `LocalWithRegion`, maps back to the original `Local` index.
3438
pub to_local: IndexVec<LocalWithRegion, Local>,
3539
}
3640

@@ -51,21 +55,32 @@ impl LiveVariableMap for NllLivenessMap {
5155
}
5256

5357
impl NllLivenessMap {
54-
/// Iterates over the variables in Mir and assigns each Local whose type contains
55-
/// regions a LocalWithRegion index. Returns a map for converting back and forth.
56-
pub fn compute(mir: &Mir) -> Self {
58+
crate fn compute(
59+
tcx: TyCtxt<'_, '_, 'tcx>,
60+
free_regions: &FxHashSet<RegionVid>,
61+
mir: &Mir<'tcx>,
62+
) -> Self {
5763
let mut to_local = IndexVec::default();
5864
let from_local: IndexVec<Local, Option<_>> = mir.local_decls
5965
.iter_enumerated()
6066
.map(|(local, local_decl)| {
61-
if local_decl.ty.has_free_regions() {
62-
Some(to_local.push(local))
63-
} else {
67+
if tcx.all_free_regions_meet(&local_decl.ty, |r| {
68+
free_regions.contains(&r.to_region_vid())
69+
}) {
70+
// If all the regions in the type are free regions
71+
// (or there are no regions), then we don't need
72+
// to track liveness for this variable.
6473
None
74+
} else {
75+
Some(to_local.push(local))
6576
}
6677
})
6778
.collect();
6879

80+
debug!("{} total variables", mir.local_decls.len());
81+
debug!("{} variables need liveness", to_local.len());
82+
debug!("{} regions outlive free regions", free_regions.len());
83+
6984
Self {
7085
from_local,
7186
to_local,

src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs

+62-7
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use borrow_check::nll::{NllLivenessMap, LocalWithRegion};
11+
use borrow_check::nll::constraints::ConstraintSet;
1212
use borrow_check::nll::type_check::AtLocation;
13+
use borrow_check::nll::{LocalWithRegion, NllLivenessMap};
14+
use borrow_check::nll::universal_regions::UniversalRegions;
1315
use dataflow::move_paths::{HasMoveData, MoveData};
1416
use dataflow::MaybeInitializedPlaces;
1517
use dataflow::{FlowAtLocation, FlowsAtLocation};
@@ -18,10 +20,10 @@ use rustc::mir::{BasicBlock, Location, Mir};
1820
use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
1921
use rustc::traits::query::type_op::outlives::DropckOutlives;
2022
use rustc::traits::query::type_op::TypeOp;
21-
use rustc::ty::{Ty, TypeFoldable};
22-
use rustc_data_structures::fx::FxHashMap;
23+
use rustc::ty::{RegionVid, Ty, TypeFoldable};
24+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2325
use std::rc::Rc;
24-
use util::liveness::{LivenessResults, LiveVariableMap };
26+
use util::liveness::{LiveVariableMap, LivenessResults};
2527

2628
use super::TypeChecker;
2729

@@ -41,9 +43,18 @@ pub(super) fn generate<'gcx, 'tcx>(
4143
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
4244
move_data: &MoveData<'tcx>,
4345
) -> (LivenessResults<LocalWithRegion>, NllLivenessMap) {
44-
let liveness_map = NllLivenessMap::compute(&mir);
46+
let free_regions = {
47+
let borrowck_context = cx.borrowck_context.as_ref().unwrap();
48+
regions_that_outlive_free_regions(
49+
cx.infcx.num_region_vars(),
50+
&borrowck_context.universal_regions,
51+
&borrowck_context.constraints.outlives_constraints,
52+
)
53+
};
54+
let liveness_map = NllLivenessMap::compute(cx.tcx(), &free_regions, mir);
4555
let liveness = LivenessResults::compute(mir, &liveness_map);
4656

57+
// For everything else, it is only live where it is actually used.
4758
{
4859
let mut generator = TypeLivenessGenerator {
4960
cx,
@@ -63,6 +74,45 @@ pub(super) fn generate<'gcx, 'tcx>(
6374
(liveness, liveness_map)
6475
}
6576

77+
/// Compute all regions that are (currently) known to outlive free
78+
/// regions. For these regions, we do not need to compute
79+
/// liveness, since the outlives constraints will ensure that they
80+
/// are live over the whole fn body anyhow.
81+
fn regions_that_outlive_free_regions(
82+
num_region_vars: usize,
83+
universal_regions: &UniversalRegions<'tcx>,
84+
constraint_set: &ConstraintSet,
85+
) -> FxHashSet<RegionVid> {
86+
// Build a graph of the outlives constraints thus far. This is
87+
// a reverse graph, so for each constraint `R1: R2` we have an
88+
// edge `R2 -> R1`. Therefore, if we find all regions
89+
// reachable from each free region, we will have all the
90+
// regions that are forced to outlive some free region.
91+
let rev_constraint_graph = constraint_set.reverse_graph(num_region_vars);
92+
let rev_region_graph = rev_constraint_graph.region_graph(constraint_set);
93+
94+
// Stack for the depth-first search. Start out with all the free regions.
95+
let mut stack: Vec<_> = universal_regions.universal_regions().collect();
96+
97+
// Set of all free regions, plus anything that outlives them. Initially
98+
// just contains the free regions.
99+
let mut outlives_free_region: FxHashSet<_> = stack.iter().cloned().collect();
100+
101+
// Do the DFS -- for each thing in the stack, find all things
102+
// that outlive it and add them to the set. If they are not,
103+
// push them onto the stack for later.
104+
while let Some(sub_region) = stack.pop() {
105+
stack.extend(
106+
rev_region_graph
107+
.outgoing_regions(sub_region)
108+
.filter(|&r| outlives_free_region.insert(r)),
109+
);
110+
}
111+
112+
// Return the final set of things we visited.
113+
outlives_free_region
114+
}
115+
66116
struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
67117
where
68118
'typeck: 'gen,
@@ -182,8 +232,13 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
182232

183233
cx.tcx().for_each_free_region(&value, |live_region| {
184234
if let Some(ref mut borrowck_context) = cx.borrowck_context {
185-
let region_vid = borrowck_context.universal_regions.to_region_vid(live_region);
186-
borrowck_context.constraints.liveness_constraints.add_element(region_vid, location);
235+
let region_vid = borrowck_context
236+
.universal_regions
237+
.to_region_vid(live_region);
238+
borrowck_context
239+
.constraints
240+
.liveness_constraints
241+
.add_element(region_vid, location);
187242

188243
if let Some(all_facts) = borrowck_context.all_facts {
189244
let start_index = borrowck_context.location_table.start_index(location);

src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr

+14-20
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
error[E0597]: borrowed value does not live long enough
22
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:15:21
33
|
4-
LL | fn gimme_static_mut_let() -> &'static mut u32 {
5-
| _______________________________________________-
6-
LL | | let ref mut x = 1234543; //~ ERROR
7-
| | ^^^^^^^ temporary value does not live long enough
8-
LL | | x
9-
LL | | }
10-
| | -
11-
| | |
12-
| |_temporary value only lives until here
13-
| borrow later used here
4+
LL | let ref mut x = 1234543; //~ ERROR
5+
| ^^^^^^^ temporary value does not live long enough
6+
LL | x
7+
LL | }
8+
| - temporary value only lives until here
9+
|
10+
= note: borrowed value must be valid for the static lifetime...
1411

1512
error[E0597]: borrowed value does not live long enough
1613
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:20:25
1714
|
18-
LL | fn gimme_static_mut_let_nested() -> &'static mut u32 {
19-
| ______________________________________________________-
20-
LL | | let (ref mut x, ) = (1234543, ); //~ ERROR
21-
| | ^^^^^^^^^^^ temporary value does not live long enough
22-
LL | | x
23-
LL | | }
24-
| | -
25-
| | |
26-
| |_temporary value only lives until here
27-
| borrow later used here
15+
LL | let (ref mut x, ) = (1234543, ); //~ ERROR
16+
| ^^^^^^^^^^^ temporary value does not live long enough
17+
LL | x
18+
LL | }
19+
| - temporary value only lives until here
20+
|
21+
= note: borrowed value must be valid for the static lifetime...
2822

2923
error[E0597]: borrowed value does not live long enough
3024
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:25:11

src/test/ui/nll/get_default.nll.stderr

+12-3
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,18 @@ LL | match map.get() {
6363
LL | Some(v) => {
6464
LL | map.set(String::new()); // Both AST and MIR error here
6565
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
66-
...
67-
LL | return v;
68-
| - borrow later used here
66+
|
67+
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1...
68+
--> $DIR/get_default.rs:41:1
69+
|
70+
LL | / fn err(map: &mut Map) -> &String {
71+
LL | | loop {
72+
LL | | match map.get() {
73+
LL | | Some(v) => {
74+
... |
75+
LL | | }
76+
LL | | }
77+
| |_^
6978

7079
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
7180
--> $DIR/get_default.rs:51:17

src/test/ui/nll/get_default.stderr

+12-3
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,18 @@ LL | match map.get() {
6363
LL | Some(v) => {
6464
LL | map.set(String::new()); // Both AST and MIR error here
6565
| ^^^ mutable borrow occurs here
66-
...
67-
LL | return v;
68-
| - borrow later used here
66+
|
67+
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1...
68+
--> $DIR/get_default.rs:41:1
69+
|
70+
LL | / fn err(map: &mut Map) -> &String {
71+
LL | | loop {
72+
LL | | match map.get() {
73+
LL | | Some(v) => {
74+
... |
75+
LL | | }
76+
LL | | }
77+
| |_^
6978

7079
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
7180
--> $DIR/get_default.rs:51:17

src/test/ui/nll/return-ref-mut-issue-46557.stderr

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
error[E0597]: borrowed value does not live long enough
22
--> $DIR/return-ref-mut-issue-46557.rs:17:21
33
|
4-
LL | fn gimme_static_mut() -> &'static mut u32 {
5-
| ___________________________________________-
6-
LL | | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597]
7-
| | ^^^^^^^ temporary value does not live long enough
8-
LL | | x
9-
LL | | }
10-
| | -
11-
| | |
12-
| |_temporary value only lives until here
13-
| borrow later used here
4+
LL | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597]
5+
| ^^^^^^^ temporary value does not live long enough
6+
LL | x
7+
LL | }
8+
| - temporary value only lives until here
9+
|
10+
= note: borrowed value must be valid for the static lifetime...
1411

1512
error: aborting due to previous error
1613

0 commit comments

Comments
 (0)