Skip to content

Commit 93ab533

Browse files
authored
Rollup merge of rust-lang#143666 - nilehmann:nested_bodies_in_consumers, r=lcnr
Re-expose nested bodies in rustc_borrowck::consumers After rust-lang#138499, it's not possible anymore to get borrowck information for nested bodies via `get_body_with_borrowck_facts`. This PR re-exposes nested bodies by returning a map containing the typeck root and all its nested bodies. To collect the bodies, a map is added to `BorrowCheckRootCtxt`, and a body is inserted every time `do_mir_borrowck` is called. r? `@lcnr`
2 parents 9e1872a + 3c7bf9f commit 93ab533

File tree

7 files changed

+107
-62
lines changed

7 files changed

+107
-62
lines changed

compiler/rustc_borrowck/src/consumers.rs

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! This file provides API for compiler consumers.
22
3+
use rustc_data_structures::fx::FxHashMap;
34
use rustc_hir::def_id::LocalDefId;
45
use rustc_index::IndexVec;
6+
use rustc_middle::bug;
57
use rustc_middle::mir::{Body, Promoted};
68
use rustc_middle::ty::TyCtxt;
79

@@ -17,7 +19,39 @@ pub use super::polonius::legacy::{
1719
pub use super::region_infer::RegionInferenceContext;
1820
use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
1921

20-
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
22+
/// Struct used during mir borrowck to collect bodies with facts for a typeck root and all
23+
/// its nested bodies.
24+
pub(crate) struct BorrowckConsumer<'tcx> {
25+
options: ConsumerOptions,
26+
bodies: FxHashMap<LocalDefId, BodyWithBorrowckFacts<'tcx>>,
27+
}
28+
29+
impl<'tcx> BorrowckConsumer<'tcx> {
30+
pub(crate) fn new(options: ConsumerOptions) -> Self {
31+
Self { options, bodies: Default::default() }
32+
}
33+
34+
pub(crate) fn insert_body(&mut self, def_id: LocalDefId, body: BodyWithBorrowckFacts<'tcx>) {
35+
if self.bodies.insert(def_id, body).is_some() {
36+
bug!("unexpected previous body for {def_id:?}");
37+
}
38+
}
39+
40+
/// Should the Polonius input facts be computed?
41+
pub(crate) fn polonius_input(&self) -> bool {
42+
matches!(
43+
self.options,
44+
ConsumerOptions::PoloniusInputFacts | ConsumerOptions::PoloniusOutputFacts
45+
)
46+
}
47+
48+
/// Should we run Polonius and collect the output facts?
49+
pub(crate) fn polonius_output(&self) -> bool {
50+
matches!(self.options, ConsumerOptions::PoloniusOutputFacts)
51+
}
52+
}
53+
54+
/// Options determining the output behavior of [`get_bodies_with_borrowck_facts`].
2155
///
2256
/// If executing under `-Z polonius` the choice here has no effect, and everything as if
2357
/// [`PoloniusOutputFacts`](ConsumerOptions::PoloniusOutputFacts) had been selected
@@ -43,17 +77,6 @@ pub enum ConsumerOptions {
4377
PoloniusOutputFacts,
4478
}
4579

46-
impl ConsumerOptions {
47-
/// Should the Polonius input facts be computed?
48-
pub(crate) fn polonius_input(&self) -> bool {
49-
matches!(self, Self::PoloniusInputFacts | Self::PoloniusOutputFacts)
50-
}
51-
/// Should we run Polonius and collect the output facts?
52-
pub(crate) fn polonius_output(&self) -> bool {
53-
matches!(self, Self::PoloniusOutputFacts)
54-
}
55-
}
56-
5780
/// A `Body` with information computed by the borrow checker. This struct is
5881
/// intended to be consumed by compiler consumers.
5982
///
@@ -82,25 +105,35 @@ pub struct BodyWithBorrowckFacts<'tcx> {
82105
pub output_facts: Option<Box<PoloniusOutput>>,
83106
}
84107

85-
/// This function computes borrowck facts for the given body. The [`ConsumerOptions`]
86-
/// determine which facts are returned. This function makes a copy of the body because
87-
/// it needs to regenerate the region identifiers. It should never be invoked during a
88-
/// typical compilation session due to the unnecessary overhead of returning
89-
/// [`BodyWithBorrowckFacts`].
108+
/// This function computes borrowck facts for the given def id and all its nested bodies.
109+
/// It must be called with a typeck root which will then borrowck all nested bodies as well.
110+
/// The [`ConsumerOptions`] determine which facts are returned. This function makes a copy
111+
/// of the bodies because it needs to regenerate the region identifiers. It should never be
112+
/// invoked during a typical compilation session due to the unnecessary overhead of
113+
/// returning [`BodyWithBorrowckFacts`].
90114
///
91115
/// Note:
92-
/// * This function will panic if the required body was already stolen. This
116+
/// * This function will panic if the required bodies were already stolen. This
93117
/// can, for example, happen when requesting a body of a `const` function
94118
/// because they are evaluated during typechecking. The panic can be avoided
95119
/// by overriding the `mir_borrowck` query. You can find a complete example
96-
/// that shows how to do this at `tests/run-make/obtain-borrowck/`.
120+
/// that shows how to do this at `tests/ui-fulldeps/obtain-borrowck.rs`.
97121
///
98122
/// * Polonius is highly unstable, so expect regular changes in its signature or other details.
99-
pub fn get_body_with_borrowck_facts(
123+
pub fn get_bodies_with_borrowck_facts(
100124
tcx: TyCtxt<'_>,
101-
def_id: LocalDefId,
125+
root_def_id: LocalDefId,
102126
options: ConsumerOptions,
103-
) -> BodyWithBorrowckFacts<'_> {
104-
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id);
105-
*do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap()
127+
) -> FxHashMap<LocalDefId, BodyWithBorrowckFacts<'_>> {
128+
let mut root_cx =
129+
BorrowCheckRootCtxt::new(tcx, root_def_id, Some(BorrowckConsumer::new(options)));
130+
131+
// See comment in `rustc_borrowck::mir_borrowck`
132+
let nested_bodies = tcx.nested_bodies_within(root_def_id);
133+
for def_id in nested_bodies {
134+
root_cx.get_or_insert_nested(def_id);
135+
}
136+
137+
do_mir_borrowck(&mut root_cx, root_def_id);
138+
root_cx.consumer.unwrap().bodies
106139
}

compiler/rustc_borrowck/src/lib.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use smallvec::SmallVec;
5151
use tracing::{debug, instrument};
5252

5353
use crate::borrow_set::{BorrowData, BorrowSet};
54-
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
54+
use crate::consumers::BodyWithBorrowckFacts;
5555
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
5656
use crate::diagnostics::{
5757
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
@@ -124,7 +124,7 @@ fn mir_borrowck(
124124
let opaque_types = ConcreteOpaqueTypes(Default::default());
125125
Ok(tcx.arena.alloc(opaque_types))
126126
} else {
127-
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
127+
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None);
128128
// We need to manually borrowck all nested bodies from the HIR as
129129
// we do not generate MIR for dead code. Not doing so causes us to
130130
// never check closures in dead code.
@@ -134,7 +134,7 @@ fn mir_borrowck(
134134
}
135135

136136
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
137-
do_mir_borrowck(&mut root_cx, def, None).0;
137+
do_mir_borrowck(&mut root_cx, def);
138138
debug_assert!(closure_requirements.is_none());
139139
debug_assert!(used_mut_upvars.is_empty());
140140
root_cx.finalize()
@@ -289,17 +289,12 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
289289

290290
/// Perform the actual borrow checking.
291291
///
292-
/// Use `consumer_options: None` for the default behavior of returning
293-
/// [`PropagatedBorrowCheckResults`] only. Otherwise, return [`BodyWithBorrowckFacts`]
294-
/// according to the given [`ConsumerOptions`].
295-
///
296292
/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
297293
#[instrument(skip(root_cx), level = "debug")]
298294
fn do_mir_borrowck<'tcx>(
299295
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
300296
def: LocalDefId,
301-
consumer_options: Option<ConsumerOptions>,
302-
) -> (PropagatedBorrowCheckResults<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
297+
) -> PropagatedBorrowCheckResults<'tcx> {
303298
let tcx = root_cx.tcx;
304299
let infcx = BorrowckInferCtxt::new(tcx, def);
305300
let (input_body, promoted) = tcx.mir_promoted(def);
@@ -343,7 +338,6 @@ fn do_mir_borrowck<'tcx>(
343338
&location_table,
344339
&move_data,
345340
&borrow_set,
346-
consumer_options,
347341
);
348342

349343
// Dump MIR results into a file, if that is enabled. This lets us
@@ -483,23 +477,24 @@ fn do_mir_borrowck<'tcx>(
483477
used_mut_upvars: mbcx.used_mut_upvars,
484478
};
485479

486-
let body_with_facts = if consumer_options.is_some() {
487-
Some(Box::new(BodyWithBorrowckFacts {
488-
body: body_owned,
489-
promoted,
490-
borrow_set,
491-
region_inference_context: regioncx,
492-
location_table: polonius_input.as_ref().map(|_| location_table),
493-
input_facts: polonius_input,
494-
output_facts: polonius_output,
495-
}))
496-
} else {
497-
None
498-
};
480+
if let Some(consumer) = &mut root_cx.consumer {
481+
consumer.insert_body(
482+
def,
483+
BodyWithBorrowckFacts {
484+
body: body_owned,
485+
promoted,
486+
borrow_set,
487+
region_inference_context: regioncx,
488+
location_table: polonius_input.as_ref().map(|_| location_table),
489+
input_facts: polonius_input,
490+
output_facts: polonius_output,
491+
},
492+
);
493+
}
499494

500495
debug!("do_mir_borrowck: result = {:#?}", result);
501496

502-
(result, body_with_facts)
497+
result
503498
}
504499

505500
fn get_flow_results<'a, 'tcx>(

compiler/rustc_borrowck/src/nll.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use rustc_span::sym;
1818
use tracing::{debug, instrument};
1919

2020
use crate::borrow_set::BorrowSet;
21-
use crate::consumers::ConsumerOptions;
2221
use crate::diagnostics::RegionErrors;
2322
use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
2423
use crate::polonius::PoloniusDiagnosticsContext;
@@ -83,12 +82,11 @@ pub(crate) fn compute_regions<'tcx>(
8382
location_table: &PoloniusLocationTable,
8483
move_data: &MoveData<'tcx>,
8584
borrow_set: &BorrowSet<'tcx>,
86-
consumer_options: Option<ConsumerOptions>,
8785
) -> NllOutput<'tcx> {
8886
let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
89-
let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
87+
let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input())
9088
|| is_polonius_legacy_enabled;
91-
let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
89+
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
9290
|| is_polonius_legacy_enabled;
9391
let mut polonius_facts =
9492
(polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());

compiler/rustc_borrowck/src/root_cx.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
66
use rustc_span::ErrorGuaranteed;
77
use smallvec::SmallVec;
88

9+
use crate::consumers::BorrowckConsumer;
910
use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults};
1011

1112
/// The shared context used by both the root as well as all its nested
@@ -16,16 +17,24 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> {
1617
concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
1718
nested_bodies: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
1819
tainted_by_errors: Option<ErrorGuaranteed>,
20+
/// This should be `None` during normal compilation. See [`crate::consumers`] for more
21+
/// information on how this is used.
22+
pub(crate) consumer: Option<BorrowckConsumer<'tcx>>,
1923
}
2024

2125
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
22-
pub(super) fn new(tcx: TyCtxt<'tcx>, root_def_id: LocalDefId) -> BorrowCheckRootCtxt<'tcx> {
26+
pub(super) fn new(
27+
tcx: TyCtxt<'tcx>,
28+
root_def_id: LocalDefId,
29+
consumer: Option<BorrowckConsumer<'tcx>>,
30+
) -> BorrowCheckRootCtxt<'tcx> {
2331
BorrowCheckRootCtxt {
2432
tcx,
2533
root_def_id,
2634
concrete_opaque_types: Default::default(),
2735
nested_bodies: Default::default(),
2836
tainted_by_errors: None,
37+
consumer,
2938
}
3039
}
3140

@@ -71,7 +80,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
7180
self.root_def_id.to_def_id()
7281
);
7382
if !self.nested_bodies.contains_key(&def_id) {
74-
let result = super::do_mir_borrowck(self, def_id, None).0;
83+
let result = super::do_mir_borrowck(self, def_id);
7584
if let Some(prev) = self.nested_bodies.insert(def_id, result) {
7685
bug!("unexpected previous nested body: {prev:?}");
7786
}

tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ const fn foo() -> usize {
2828
1
2929
}
3030

31+
fn with_nested_body(opt: Option<i32>) -> Option<i32> {
32+
opt.map(|x| x + 1)
33+
}
34+
3135
fn main() {
3236
let bar: [Bar; foo()] = [Bar::new()];
3337
assert_eq!(bar[0].provided(), foo());

tests/ui-fulldeps/obtain-borrowck.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@
99

1010
//! This program implements a rustc driver that retrieves MIR bodies with
1111
//! borrowck information. This cannot be done in a straightforward way because
12-
//! `get_body_with_borrowck_facts`–the function for retrieving a MIR body with
13-
//! borrowck facts–can panic if the body is stolen before it is invoked.
12+
//! `get_bodies_with_borrowck_facts`–the function for retrieving MIR bodies with
13+
//! borrowck facts–can panic if the bodies are stolen before it is invoked.
1414
//! Therefore, the driver overrides `mir_borrowck` query (this is done in the
15-
//! `config` callback), which retrieves the body that is about to be borrow
16-
//! checked and stores it in a thread local `MIR_BODIES`. Then, `after_analysis`
15+
//! `config` callback), which retrieves the bodies that are about to be borrow
16+
//! checked and stores them in a thread local `MIR_BODIES`. Then, `after_analysis`
1717
//! callback triggers borrow checking of all MIR bodies by retrieving
1818
//! `optimized_mir` and pulls out the MIR bodies with the borrowck information
1919
//! from the thread local storage.
2020
2121
extern crate rustc_borrowck;
22+
extern crate rustc_data_structures;
2223
extern crate rustc_driver;
2324
extern crate rustc_hir;
2425
extern crate rustc_interface;
@@ -30,6 +31,7 @@ use std::collections::HashMap;
3031
use std::thread_local;
3132

3233
use rustc_borrowck::consumers::{self, BodyWithBorrowckFacts, ConsumerOptions};
34+
use rustc_data_structures::fx::FxHashMap;
3335
use rustc_driver::Compilation;
3436
use rustc_hir::def::DefKind;
3537
use rustc_hir::def_id::LocalDefId;
@@ -129,13 +131,15 @@ thread_local! {
129131

130132
fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ProvidedValue<'tcx> {
131133
let opts = ConsumerOptions::PoloniusInputFacts;
132-
let body_with_facts = consumers::get_body_with_borrowck_facts(tcx, def_id, opts);
134+
let bodies_with_facts = consumers::get_bodies_with_borrowck_facts(tcx, def_id, opts);
133135
// SAFETY: The reader casts the 'static lifetime to 'tcx before using it.
134-
let body_with_facts: BodyWithBorrowckFacts<'static> =
135-
unsafe { std::mem::transmute(body_with_facts) };
136+
let bodies_with_facts: FxHashMap<LocalDefId, BodyWithBorrowckFacts<'static>> =
137+
unsafe { std::mem::transmute(bodies_with_facts) };
136138
MIR_BODIES.with(|state| {
137139
let mut map = state.borrow_mut();
138-
assert!(map.insert(def_id, body_with_facts).is_none());
140+
for (def_id, body_with_facts) in bodies_with_facts {
141+
assert!(map.insert(def_id, body_with_facts).is_none());
142+
}
139143
});
140144
let mut providers = Providers::default();
141145
rustc_borrowck::provide(&mut providers);

tests/ui-fulldeps/obtain-borrowck.run.stdout

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Bodies retrieved for:
33
::foo
44
::main
55
::main::{constant#0}
6+
::with_nested_body
7+
::with_nested_body::{closure#0}
68
::{impl#0}::new
79
::{impl#1}::provided
810
::{impl#1}::required

0 commit comments

Comments
 (0)