Skip to content

Commit fead75b

Browse files
committed
Defer nll-boring/polonius-relevant locals
1 parent 0cb7bdc commit fead75b

3 files changed

Lines changed: 113 additions & 33 deletions

File tree

compiler/rustc_borrowck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ mod nll;
8787
mod path_utils;
8888
mod place_ext;
8989
mod places_conflict;
90-
pub(crate) mod polonius;
90+
mod polonius;
9191
mod prefixes;
9292
mod region_infer;
9393
mod renumber;

compiler/rustc_borrowck/src/type_check/liveness/mod.rs

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,38 +42,57 @@ pub(super) fn generate<'tcx>(
4242
typeck.constraints.liveness_constraints.add_all_points(region);
4343
}
4444

45-
let mut free_regions = regions_that_outlive_free_regions(
45+
let free_regions = regions_that_outlive_free_regions(
4646
typeck.infcx.num_region_vars(),
4747
&typeck.universal_regions,
4848
&typeck.constraints.outlives_constraints,
4949
);
5050

51-
// NLLs can avoid computing some liveness data here because its constraints are
52-
// location-insensitive, but that doesn't work in polonius: locals whose type contains a region
53-
// that outlives a free region are not necessarily live everywhere in a flow-sensitive setting,
54-
// unlike NLLs.
55-
// We do record these regions in the polonius context, since they're used to differentiate
56-
// relevant and boring locals, which is a key distinction used later in diagnostics.
57-
// This additional liveness information is ultimately used for *loan* liveness,
58-
// so we don't need to compute it when there are no loans.
59-
// FIXME: this NLL optimization idea, to reduce work to relevant locals only, still makes sense
60-
// for polonius, and should be investigated to improve liveness performance.
61-
if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled()
62-
&& typeck.borrow_set.len() > 0
63-
{
64-
let (_, boring_locals) =
65-
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
66-
typeck.polonius_context.as_mut().unwrap().boring_nll_locals =
67-
boring_locals.into_iter().collect();
68-
free_regions = typeck.universal_regions.universal_regions_iter().collect();
69-
}
7051
let (relevant_live_locals, boring_locals) =
7152
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
7253

73-
let (deferred_locals, local_use_map) =
74-
trace::trace(typeck, location_map, move_data, &relevant_live_locals, &boring_locals);
54+
// Under Polonius Alpha, a larger set of locals are considered relevant: specifically,
55+
// locals containing regions *outliving* universal regions are relevant and only
56+
// locals containing solely universal regions are considered boring.
57+
//
58+
// However, we don't actually need liveness information for *all* these locals,
59+
// only when actually computing loans. So, we can defer computing the liveness
60+
// until we try to compute the loan, which is gated on `LocalizedConstraintGraph`
61+
// traversal.
62+
//
63+
// Potentially in theory, we could defer computing liveness for *all* locals,
64+
// but that's a much bigger refactor (many things rely on liveness of
65+
// NLL-relevant locals). So, we only defer NLL-boring/Polonius-relevant locals
66+
// for now.
67+
let deferred_locals = 'deferred: {
68+
// If we aren't going to be using the additional liveness information,
69+
// don't even bother computing the larger relevant set.
70+
// Similarly, since this liveness information is ultimately used for *loan*
71+
// liveness, we don't need to compute it when there are no loans.
72+
if typeck.polonius_context.is_none() || typeck.borrow_set.len() == 0 {
73+
break 'deferred vec![];
74+
}
75+
76+
let free_regions: FxHashSet<RegionVid> =
77+
typeck.universal_regions.universal_regions_iter().collect();
78+
let (polonius_relevant, _) =
79+
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
80+
81+
let boring: FxHashSet<Local> = boring_locals.iter().copied().collect();
82+
polonius_relevant.into_iter().filter(|local| boring.contains(local)).collect()
83+
};
84+
85+
let (deferred_locals, local_use_map) = trace::trace(
86+
typeck,
87+
location_map,
88+
move_data,
89+
&relevant_live_locals,
90+
&boring_locals,
91+
&deferred_locals,
92+
);
7593

7694
if let Some(polonius_context) = &mut typeck.polonius_context {
95+
polonius_context.boring_nll_locals = boring_locals.into_iter().collect();
7796
polonius_context.deferred_locals_for_liveness = deferred_locals;
7897
polonius_context.local_use_map = Some(local_use_map);
7998
}

compiler/rustc_borrowck/src/type_check/liveness/trace.rs

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ pub(super) fn trace<'tcx>(
4848
move_data: &MoveData<'tcx>,
4949
relevant_live_locals: &[Local],
5050
boring_locals: &[Local],
51+
deferred: &[Local],
5152
) -> (DeferredLocals<'tcx>, LocalUseMap) {
5253
let _timer = typeck.tcx().prof.generic_activity("borrowck_liveness_trace");
5354

54-
let local_use_map = LocalUseMap::build(&relevant_live_locals, location_map, typeck.body);
55+
// The use map must also cover the deferred locals: their liveness is computed later, from
56+
// this same map, when the loan liveness traversal first reaches one of their regions.
57+
let use_map_locals: Vec<Local> = relevant_live_locals.iter().chain(deferred).copied().collect();
58+
let local_use_map = LocalUseMap::build(&use_map_locals, location_map, typeck.body);
5559
let calc = LivenessCalculation::new(
5660
typeck.tcx(),
5761
typeck.body,
@@ -61,13 +65,14 @@ pub(super) fn trace<'tcx>(
6165
);
6266
let mut results = LivenessResults::new(typeck, calc);
6367

64-
let deferred_locals = DeferredLocals::default();
68+
let deferred: FxIndexSet<Local> = deferred.iter().copied().collect();
69+
let mut deferred_locals = DeferredLocals::default();
6570

66-
results.add_extra_drop_facts(relevant_live_locals);
71+
results.add_extra_drop_facts(relevant_live_locals, &deferred);
6772

6873
results.compute_for_all_locals(relevant_live_locals);
6974

70-
results.dropck_boring_locals(boring_locals);
75+
results.dropck_boring_locals(boring_locals, &deferred, &mut deferred_locals);
7176

7277
(deferred_locals, local_use_map)
7378
}
@@ -174,19 +179,72 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> {
174179
/// These are all the locals which do not potentially reference a region local
175180
/// to this body. Locals which only reference free regions are always drop-live
176181
/// and can therefore safely be dropped.
177-
fn dropck_boring_locals(&mut self, boring_locals: &[Local]) {
182+
fn dropck_boring_locals(
183+
&mut self,
184+
boring_locals: &[Local],
185+
deferred: &FxIndexSet<Local>,
186+
deferred_locals: &mut DeferredLocals<'tcx>,
187+
) {
178188
for &local in boring_locals {
179-
let local_ty = self.calc.body.local_decls[local].ty;
180-
let local_span = self.calc.body.local_decls[local].source_info.span;
181-
dropck_local(&self.typeck.infcx, &mut self.calc.drop_data, local_ty, local_span);
189+
self.dropck_boring_local(local, deferred, deferred_locals);
182190
}
183191
}
184192

193+
fn dropck_boring_local(
194+
&mut self,
195+
local: Local,
196+
deferred: &FxIndexSet<Local>,
197+
deferred_locals: &mut DeferredLocals<'tcx>,
198+
) {
199+
let typeck = &mut *self.typeck;
200+
let local_ty = self.calc.body.local_decls[local].ty;
201+
let local_span = self.calc.body.local_decls[local].source_info.span;
202+
let drop_data = dropck_local(&typeck.infcx, &mut self.calc.drop_data, local_ty, local_span);
203+
204+
if !deferred.contains(&local) {
205+
return;
206+
}
207+
208+
if let Some(data) = drop_data.region_constraint_data {
209+
let drop_locations =
210+
self.calc.local_use_map.drops(local).map(|p| self.calc.location_map.to_location(p));
211+
for drop_location in drop_locations {
212+
typeck.push_region_constraints(
213+
drop_location.to_locations(),
214+
ConstraintCategory::Boring,
215+
data,
216+
);
217+
}
218+
}
219+
220+
for &kind in &drop_data.dropck_result.kinds {
221+
polonius::legacy::emit_drop_facts(
222+
typeck.tcx(),
223+
local,
224+
&kind,
225+
typeck.universal_regions,
226+
typeck.polonius_facts,
227+
);
228+
}
229+
230+
deferred_locals.defer_local(
231+
typeck.infcx,
232+
typeck.universal_regions,
233+
local,
234+
local_ty,
235+
&drop_data.dropck_result.kinds,
236+
);
237+
}
238+
185239
/// Add extra drop facts needed for Polonius.
186240
///
187241
/// Add facts for all locals with free regions, since regions may outlive
188242
/// the function body only at certain nodes in the CFG.
189-
fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) {
243+
fn add_extra_drop_facts(
244+
&mut self,
245+
relevant_live_locals: &[Local],
246+
deferred: &FxIndexSet<Local>,
247+
) {
190248
// This collect is more necessary than immediately apparent
191249
// because these facts go into `add_drop_live_facts_for()`,
192250
// which also writes to `polonius_facts`, and so this is genuinely
@@ -206,7 +264,10 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> {
206264
.iter()
207265
.filter_map(|&(local, location_index)| {
208266
let local_ty = self.calc.body.local_decls[local].ty;
209-
if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() {
267+
if relevant_live_locals.contains(&local)
268+
|| deferred.contains(&local)
269+
|| !local_ty.has_free_regions()
270+
{
210271
return None;
211272
}
212273

0 commit comments

Comments
 (0)