Skip to content

Commit 3350edf

Browse files
committed
Replace BorrowckResults with Borrowck.
The results of most analyses end up in a `Results<'tcx, A>`, where `A` is the analysis. It's then possible to traverse the results via a `ResultsVisitor`, which relies on the `ResultsVisitable` trait. (That trait ends up using the same `apply_*` methods that were used when computing the analysis, albeit indirectly.) This pattern of "compute analysis results, then visit them" is common. But there is one exception. For borrow checking we compute three separate analyses (`Borrows`, `MaybeUninitializedPlaces`, and `EverInitializedPlaces`), combine them into a single `BorrowckResults`, and then do a single visit of that `BorrowckResults` with `MirBorrowckResults`. `BorrowckResults` is just different enough from `Results` that it requires the existence of `ResultsVisitable`, which abstracts over the traversal differences between `Results` and `BorrowckResults`. This commit changes things by introducing `Borrowck` and bundling the three borrowck analysis results into a standard `Results<Borrowck>` instead of the exceptional `BorrowckResults`. Once that's done, the results can be visited like any other analysis results. `BorrowckResults` is removed, as is `impl ResultsVisitable for BorrowckResults`. (It's instructive to see how similar the added `impl Analysis for Borrowck` is to the removed `impl ResultsVisitable for BorrowckResults`. They're both doing exactly the same things.) Overall this increases the number of lines of code and might not seem like a win. But it enables the removal of `ResultsVisitable` in the next commit, which results in many simplifications.
1 parent fbab782 commit 3350edf

File tree

5 files changed

+174
-77
lines changed

5 files changed

+174
-77
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+119-41
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,171 @@
1+
use std::fmt;
2+
13
use rustc_data_structures::fx::FxIndexMap;
24
use rustc_data_structures::graph;
35
use rustc_index::bit_set::BitSet;
4-
use rustc_middle::mir::{self, BasicBlock, Body, Location, Place, TerminatorEdges};
6+
use rustc_middle::mir::{
7+
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
8+
};
59
use rustc_middle::ty::{RegionVid, TyCtxt};
610
use rustc_mir_dataflow::fmt::DebugWithContext;
711
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
8-
use rustc_mir_dataflow::{Analysis, Forward, GenKill, Results, ResultsVisitable};
12+
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
913
use tracing::debug;
1014

1115
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
1216

13-
/// The results of the dataflow analyses used by the borrow checker.
14-
pub(crate) struct BorrowckResults<'a, 'tcx> {
15-
pub(crate) borrows: Results<'tcx, Borrows<'a, 'tcx>>,
16-
pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'tcx>>,
17-
pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'tcx>>,
18-
}
19-
20-
/// The transient state of the dataflow analyses used by the borrow checker.
21-
#[derive(Debug)]
22-
pub(crate) struct BorrowckDomain<'a, 'tcx> {
23-
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
24-
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
25-
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
17+
// This analysis is different to most others. Its results aren't computed with
18+
// `iterate_to_fixpoint`, but are instead composed from the results of three sub-analyses that are
19+
// computed individually with `iterate_to_fixpoint`.
20+
pub(crate) struct Borrowck<'a, 'tcx> {
21+
pub(crate) borrows: Borrows<'a, 'tcx>,
22+
pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
23+
pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
2624
}
2725

28-
impl<'a, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'tcx> {
29-
type Direction = Forward;
26+
impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
3027
type Domain = BorrowckDomain<'a, 'tcx>;
3128

29+
const NAME: &'static str = "borrowck";
30+
3231
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
3332
BorrowckDomain {
34-
borrows: self.borrows.analysis.bottom_value(body),
35-
uninits: self.uninits.analysis.bottom_value(body),
36-
ever_inits: self.ever_inits.analysis.bottom_value(body),
33+
borrows: self.borrows.bottom_value(body),
34+
uninits: self.uninits.bottom_value(body),
35+
ever_inits: self.ever_inits.bottom_value(body),
3736
}
3837
}
3938

40-
fn reset_to_block_entry(&self, state: &mut Self::Domain, block: BasicBlock) {
41-
state.borrows.clone_from(self.borrows.entry_set_for_block(block));
42-
state.uninits.clone_from(self.uninits.entry_set_for_block(block));
43-
state.ever_inits.clone_from(self.ever_inits.entry_set_for_block(block));
39+
fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
40+
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
41+
unreachable!();
4442
}
4543

46-
fn reconstruct_before_statement_effect(
44+
fn apply_before_statement_effect(
4745
&mut self,
4846
state: &mut Self::Domain,
4947
stmt: &mir::Statement<'tcx>,
5048
loc: Location,
5149
) {
52-
self.borrows.analysis.apply_before_statement_effect(&mut state.borrows, stmt, loc);
53-
self.uninits.analysis.apply_before_statement_effect(&mut state.uninits, stmt, loc);
54-
self.ever_inits.analysis.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
50+
self.borrows.apply_before_statement_effect(&mut state.borrows, stmt, loc);
51+
self.uninits.apply_before_statement_effect(&mut state.uninits, stmt, loc);
52+
self.ever_inits.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
5553
}
5654

57-
fn reconstruct_statement_effect(
55+
fn apply_statement_effect(
5856
&mut self,
5957
state: &mut Self::Domain,
6058
stmt: &mir::Statement<'tcx>,
6159
loc: Location,
6260
) {
63-
self.borrows.analysis.apply_statement_effect(&mut state.borrows, stmt, loc);
64-
self.uninits.analysis.apply_statement_effect(&mut state.uninits, stmt, loc);
65-
self.ever_inits.analysis.apply_statement_effect(&mut state.ever_inits, stmt, loc);
61+
self.borrows.apply_statement_effect(&mut state.borrows, stmt, loc);
62+
self.uninits.apply_statement_effect(&mut state.uninits, stmt, loc);
63+
self.ever_inits.apply_statement_effect(&mut state.ever_inits, stmt, loc);
6664
}
6765

68-
fn reconstruct_before_terminator_effect(
66+
fn apply_before_terminator_effect(
6967
&mut self,
7068
state: &mut Self::Domain,
7169
term: &mir::Terminator<'tcx>,
7270
loc: Location,
7371
) {
74-
self.borrows.analysis.apply_before_terminator_effect(&mut state.borrows, term, loc);
75-
self.uninits.analysis.apply_before_terminator_effect(&mut state.uninits, term, loc);
76-
self.ever_inits.analysis.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
72+
self.borrows.apply_before_terminator_effect(&mut state.borrows, term, loc);
73+
self.uninits.apply_before_terminator_effect(&mut state.uninits, term, loc);
74+
self.ever_inits.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
7775
}
7876

79-
fn reconstruct_terminator_effect(
77+
fn apply_terminator_effect<'mir>(
8078
&mut self,
8179
state: &mut Self::Domain,
82-
term: &mir::Terminator<'tcx>,
80+
term: &'mir mir::Terminator<'tcx>,
8381
loc: Location,
82+
) -> TerminatorEdges<'mir, 'tcx> {
83+
self.borrows.apply_terminator_effect(&mut state.borrows, term, loc);
84+
self.uninits.apply_terminator_effect(&mut state.uninits, term, loc);
85+
self.ever_inits.apply_terminator_effect(&mut state.ever_inits, term, loc);
86+
87+
// This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this
88+
// analysis doesn't use.
89+
TerminatorEdges::None
90+
}
91+
92+
fn apply_call_return_effect(
93+
&mut self,
94+
_state: &mut Self::Domain,
95+
_block: BasicBlock,
96+
_return_places: CallReturnPlaces<'_, 'tcx>,
97+
) {
98+
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
99+
unreachable!();
100+
}
101+
102+
fn apply_switch_int_edge_effects(
103+
&mut self,
104+
_block: BasicBlock,
105+
_discr: &mir::Operand<'tcx>,
106+
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
84107
) {
85-
self.borrows.analysis.apply_terminator_effect(&mut state.borrows, term, loc);
86-
self.uninits.analysis.apply_terminator_effect(&mut state.uninits, term, loc);
87-
self.ever_inits.analysis.apply_terminator_effect(&mut state.ever_inits, term, loc);
108+
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
109+
unreachable!();
88110
}
89111
}
90112

113+
impl JoinSemiLattice for BorrowckDomain<'_, '_> {
114+
fn join(&mut self, _other: &Self) -> bool {
115+
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
116+
unreachable!();
117+
}
118+
}
119+
120+
impl<'tcx, C> DebugWithContext<C> for BorrowckDomain<'_, 'tcx>
121+
where
122+
C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
123+
{
124+
fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125+
f.write_str("borrows: ")?;
126+
self.borrows.fmt_with(ctxt, f)?;
127+
f.write_str(" uninits: ")?;
128+
self.uninits.fmt_with(ctxt, f)?;
129+
f.write_str(" ever_inits: ")?;
130+
self.ever_inits.fmt_with(ctxt, f)?;
131+
Ok(())
132+
}
133+
134+
fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135+
if self == old {
136+
return Ok(());
137+
}
138+
139+
if self.borrows != old.borrows {
140+
f.write_str("borrows: ")?;
141+
self.borrows.fmt_diff_with(&old.borrows, ctxt, f)?;
142+
f.write_str("\n")?;
143+
}
144+
145+
if self.uninits != old.uninits {
146+
f.write_str("uninits: ")?;
147+
self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
148+
f.write_str("\n")?;
149+
}
150+
151+
if self.ever_inits != old.ever_inits {
152+
f.write_str("ever_inits: ")?;
153+
self.ever_inits.fmt_diff_with(&old.ever_inits, ctxt, f)?;
154+
f.write_str("\n")?;
155+
}
156+
157+
Ok(())
158+
}
159+
}
160+
161+
/// The transient state of the dataflow analyses used by the borrow checker.
162+
#[derive(Clone, Debug, PartialEq, Eq)]
163+
pub(crate) struct BorrowckDomain<'a, 'tcx> {
164+
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
165+
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
166+
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
167+
}
168+
91169
rustc_index::newtype_index! {
92170
#[orderable]
93171
#[debug_format = "bw{}"]

compiler/rustc_borrowck/src/lib.rs

+49-30
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,21 @@ use rustc_middle::mir::*;
3636
use rustc_middle::query::Providers;
3737
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
3838
use rustc_middle::{bug, span_bug};
39-
use rustc_mir_dataflow::Analysis;
4039
use rustc_mir_dataflow::impls::{
4140
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
4241
};
4342
use rustc_mir_dataflow::move_paths::{
4443
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
4544
};
45+
use rustc_mir_dataflow::{Analysis, EntrySets, Results};
4646
use rustc_session::lint::builtin::UNUSED_MUT;
4747
use rustc_span::{Span, Symbol};
4848
use smallvec::SmallVec;
4949
use tracing::{debug, instrument};
5050

5151
use crate::borrow_set::{BorrowData, BorrowSet};
5252
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
53-
use crate::dataflow::{BorrowIndex, BorrowckDomain, BorrowckResults, Borrows};
53+
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
5454
use crate::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName};
5555
use crate::location::LocationTable;
5656
use crate::nll::PoloniusOutput;
@@ -221,6 +221,10 @@ fn do_mir_borrowck<'tcx>(
221221
consumer_options,
222222
);
223223

224+
// `flow_inits` is large, so we drop it as soon as possible. This reduces
225+
// peak memory usage significantly on some benchmarks.
226+
drop(flow_inits);
227+
224228
// Dump MIR results into a file, if that is enabled. This let us
225229
// write unit-tests, as well as helping with debugging.
226230
nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
@@ -229,27 +233,6 @@ fn do_mir_borrowck<'tcx>(
229233
// information.
230234
nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req, &opaque_type_values, diags);
231235

232-
// The various `flow_*` structures can be large. We drop `flow_inits` here
233-
// so it doesn't overlap with the others below. This reduces peak memory
234-
// usage significantly on some benchmarks.
235-
drop(flow_inits);
236-
237-
let flow_borrows = Borrows::new(tcx, body, &regioncx, &borrow_set).iterate_to_fixpoint(
238-
tcx,
239-
body,
240-
Some("borrowck"),
241-
);
242-
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data).iterate_to_fixpoint(
243-
tcx,
244-
body,
245-
Some("borrowck"),
246-
);
247-
let flow_ever_inits = EverInitializedPlaces::new(body, &move_data).iterate_to_fixpoint(
248-
tcx,
249-
body,
250-
Some("borrowck"),
251-
);
252-
253236
let movable_coroutine =
254237
// The first argument is the coroutine type passed by value
255238
if let Some(local) = body.local_decls.raw.get(1)
@@ -334,16 +317,11 @@ fn do_mir_borrowck<'tcx>(
334317
// Compute and report region errors, if any.
335318
mbcx.report_region_errors(nll_errors);
336319

337-
let mut results = BorrowckResults {
338-
ever_inits: flow_ever_inits,
339-
uninits: flow_uninits,
340-
borrows: flow_borrows,
341-
};
342-
320+
let mut flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
343321
rustc_mir_dataflow::visit_results(
344322
body,
345323
traversal::reverse_postorder(body).map(|(bb, _)| bb),
346-
&mut results,
324+
&mut flow_results,
347325
&mut mbcx,
348326
);
349327

@@ -426,6 +404,47 @@ fn do_mir_borrowck<'tcx>(
426404
(result, body_with_facts)
427405
}
428406

407+
fn get_flow_results<'a, 'tcx>(
408+
tcx: TyCtxt<'tcx>,
409+
body: &'a Body<'tcx>,
410+
move_data: &'a MoveData<'tcx>,
411+
borrow_set: &'a BorrowSet<'tcx>,
412+
regioncx: &RegionInferenceContext<'tcx>,
413+
) -> Results<'tcx, Borrowck<'a, 'tcx>> {
414+
// We compute these three analyses individually, but them combine them into
415+
// a single results so that `mbcx` can visit them all together.
416+
let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
417+
tcx,
418+
body,
419+
Some("borrowck"),
420+
);
421+
let uninits = MaybeUninitializedPlaces::new(tcx, body, move_data).iterate_to_fixpoint(
422+
tcx,
423+
body,
424+
Some("borrowck"),
425+
);
426+
let ever_inits = EverInitializedPlaces::new(body, move_data).iterate_to_fixpoint(
427+
tcx,
428+
body,
429+
Some("borrowck"),
430+
);
431+
432+
let analysis = Borrowck {
433+
borrows: borrows.analysis,
434+
uninits: uninits.analysis,
435+
ever_inits: ever_inits.analysis,
436+
};
437+
438+
assert_eq!(borrows.entry_sets.len(), uninits.entry_sets.len());
439+
assert_eq!(borrows.entry_sets.len(), ever_inits.entry_sets.len());
440+
let entry_sets: EntrySets<'_, Borrowck<'_, '_>> =
441+
itertools::izip!(borrows.entry_sets, uninits.entry_sets, ever_inits.entry_sets)
442+
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
443+
.collect();
444+
445+
Results { analysis, entry_sets }
446+
}
447+
429448
pub(crate) struct BorrowckInferCtxt<'tcx> {
430449
pub(crate) infcx: InferCtxt<'tcx>,
431450
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,

compiler/rustc_mir_dataflow/src/framework/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mod visitor;
5555
pub use self::cursor::ResultsCursor;
5656
pub use self::direction::{Backward, Direction, Forward};
5757
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
58-
pub use self::results::Results;
58+
pub use self::results::{EntrySets, Results};
5959
pub use self::visitor::{ResultsVisitable, ResultsVisitor, visit_results};
6060

6161
/// Analysis domains are all bitsets of various kinds. This trait holds

compiler/rustc_mir_dataflow/src/framework/results.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::errors::{
1818
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
1919
};
2020

21-
type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
21+
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
2222

2323
/// A dataflow analysis that has converged to fixpoint.
2424
#[derive(Clone)]
@@ -27,7 +27,7 @@ where
2727
A: Analysis<'tcx>,
2828
{
2929
pub analysis: A,
30-
pub(super) entry_sets: EntrySets<'tcx, A>,
30+
pub entry_sets: EntrySets<'tcx, A>,
3131
}
3232

3333
impl<'tcx, A> Results<'tcx, A>

compiler/rustc_mir_dataflow/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ pub use self::drop_flag_effects::{
1818
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
1919
};
2020
pub use self::framework::{
21-
Analysis, Backward, Direction, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results,
22-
ResultsCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz, lattice,
23-
visit_results,
21+
Analysis, Backward, Direction, EntrySets, Forward, GenKill, JoinSemiLattice, MaybeReachable,
22+
Results, ResultsCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz,
23+
lattice, visit_results,
2424
};
2525
use self::move_paths::MoveData;
2626

0 commit comments

Comments
 (0)