Skip to content

Commit 6d9a4f9

Browse files
committed
Polonius facts: kill loans on Call terminators and StorageDead
1 parent 9a82f52 commit 6d9a4f9

File tree

2 files changed

+82
-16
lines changed

2 files changed

+82
-16
lines changed

src/librustc_mir/borrow_check/nll/constraint_generation.rs

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::borrow_check::nll::region_infer::values::LivenessValues;
66
use rustc::infer::InferCtxt;
77
use rustc::mir::visit::TyContext;
88
use rustc::mir::visit::Visitor;
9-
use rustc::mir::{BasicBlock, BasicBlockData, Location, Body, Place, PlaceBase, Rvalue};
10-
use rustc::mir::{SourceInfo, Statement, Terminator};
9+
use rustc::mir::{BasicBlock, BasicBlockData, Location, Body, Place, PlaceBase, Rvalue, TerminatorKind};
10+
use rustc::mir::{Local, SourceInfo, Statement, StatementKind, Terminator};
1111
use rustc::mir::UserTypeProjection;
1212
use rustc::ty::fold::TypeFoldable;
1313
use rustc::ty::{self, ClosureSubsts, GeneratorSubsts, RegionVid, Ty};
@@ -114,6 +114,17 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> {
114114
self.location_table
115115
.start_index(location.successor_within_block()),
116116
));
117+
118+
// If there are borrows on this now dead local, we need to record them as `killed`.
119+
if let StatementKind::StorageDead(ref local) = statement.kind {
120+
record_killed_borrows_for_local(
121+
all_facts,
122+
self.borrow_set,
123+
self.location_table,
124+
local,
125+
location,
126+
);
127+
}
117128
}
118129

119130
self.super_statement(statement, location);
@@ -127,20 +138,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> {
127138
) {
128139
// When we see `X = ...`, then kill borrows of
129140
// `(*X).foo` and so forth.
130-
if let Some(all_facts) = self.all_facts {
131-
if let Place {
132-
base: PlaceBase::Local(temp),
133-
projection: None,
134-
} = place {
135-
if let Some(borrow_indices) = self.borrow_set.local_map.get(temp) {
136-
all_facts.killed.reserve(borrow_indices.len());
137-
for &borrow_index in borrow_indices {
138-
let location_index = self.location_table.mid_index(location);
139-
all_facts.killed.push((borrow_index, location_index));
140-
}
141-
}
142-
}
143-
}
141+
self.record_killed_borrows_for_place(place, location);
144142

145143
self.super_assign(place, rvalue, location);
146144
}
@@ -167,6 +165,14 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> {
167165
}
168166
}
169167

168+
// A `Call` terminator's return value can be a local which has borrows,
169+
// so we need to record those as `killed` as well.
170+
if let TerminatorKind::Call { ref destination, .. } = terminator.kind {
171+
if let Some((place, _)) = destination {
172+
self.record_killed_borrows_for_place(place, location);
173+
}
174+
}
175+
170176
self.super_terminator(terminator, location);
171177
}
172178

@@ -201,4 +207,40 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> {
201207
self.liveness_constraints.add_element(vid, location);
202208
});
203209
}
210+
211+
/// When recording facts for Polonius, records the borrows on the specified place
212+
/// as `killed`. For example, when assigning to a local, or on a call's return destination.
213+
fn record_killed_borrows_for_place(&mut self, place: &Place<'tcx>, location: Location) {
214+
if let Some(all_facts) = self.all_facts {
215+
if let Place {
216+
base: PlaceBase::Local(local),
217+
projection: None,
218+
} = place {
219+
record_killed_borrows_for_local(
220+
all_facts,
221+
self.borrow_set,
222+
self.location_table,
223+
local,
224+
location,
225+
);
226+
}
227+
}
228+
}
229+
}
230+
231+
/// When recording facts for Polonius, records the borrows on the specified local as `killed`.
232+
fn record_killed_borrows_for_local(
233+
all_facts: &mut AllFacts,
234+
borrow_set: &BorrowSet<'_>,
235+
location_table: &LocationTable,
236+
local: &Local,
237+
location: Location,
238+
) {
239+
if let Some(borrow_indices) = borrow_set.local_map.get(local) {
240+
all_facts.killed.reserve(borrow_indices.len());
241+
for &borrow_index in borrow_indices {
242+
let location_index = location_table.mid_index(location);
243+
all_facts.killed.push((borrow_index, location_index));
244+
}
245+
}
204246
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// `Call` terminators can write to a local which has existing loans
2+
// and those need to be killed like a regular assignment to a local.
3+
// This is a simplified version of issue 47680, is correctly accepted
4+
// by NLL but was incorrectly rejected by Polonius because of these
5+
// missing `killed` facts.
6+
7+
// build-pass
8+
// compile-flags: -Z borrowck=mir -Z polonius
9+
// ignore-compare-mode-nll
10+
11+
struct Thing;
12+
13+
impl Thing {
14+
fn next(&mut self) -> &mut Self { unimplemented!() }
15+
}
16+
17+
fn main() {
18+
let mut temp = &mut Thing;
19+
20+
loop {
21+
let v = temp.next();
22+
temp = v; // accepted by NLL, was incorrectly rejected by Polonius
23+
}
24+
}

0 commit comments

Comments
 (0)