1
Fork 0

Allow consumers to retrieve borrowck output

This commit is contained in:
Jonáš Fiala 2023-04-29 16:57:50 +02:00 committed by Dominik Stolz
parent cda5becc27
commit 388071a2cb
7 changed files with 111 additions and 48 deletions

View file

@ -30,7 +30,7 @@ pub struct BorrowSet<'tcx> {
/// Map from local to all the borrows on that local. /// Map from local to all the borrows on that local.
pub local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>, pub local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
pub(crate) locals_state_at_exit: LocalsStateAtExit, pub locals_state_at_exit: LocalsStateAtExit,
} }
impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
@ -153,7 +153,7 @@ impl<'tcx> BorrowSet<'tcx> {
self.activation_map.get(&location).map_or(&[], |activations| &activations[..]) self.activation_map.get(&location).map_or(&[], |activations| &activations[..])
} }
pub(crate) fn len(&self) -> usize { pub fn len(&self) -> usize {
self.location_map.len() self.location_map.len()
} }

View file

@ -9,16 +9,56 @@ use rustc_middle::mir::Body;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
pub use super::{ pub use super::{
dataflow::{calculate_borrows_out_of_scope_at_location, BorrowIndex, Borrows},
facts::{AllFacts as PoloniusInput, RustcFacts}, facts::{AllFacts as PoloniusInput, RustcFacts},
location::{LocationTable, RichLocation}, location::{LocationTable, RichLocation},
nll::PoloniusOutput, nll::PoloniusOutput,
region_infer::RegionInferenceContext,
BodyWithBorrowckFacts, BodyWithBorrowckFacts,
}; };
/// This function computes Polonius facts for the given body. It makes a copy of /// Options determining the output behavior of [`get_body_with_borrowck_facts`].
/// the body because it needs to regenerate the region identifiers. This function ///
/// should never be invoked during a typical compilation session due to performance /// If executing under `-Z polonius` the choice here has no effect, and everything as if
/// issues with Polonius. /// [`PoloniusOutputFacts`](ConsumerOptions::PoloniusOutputFacts) had been selected
/// will be retrieved.
#[derive(Debug, Copy, Clone)]
pub enum ConsumerOptions {
/// Retrieve the [`Body`] along with the [`BorrowSet`](super::borrow_set::BorrowSet)
/// and [`RegionInferenceContext`]. If you would like the body only, use
/// [`TyCtxt::mir_promoted`].
///
/// These can be used in conjunction with [`calculate_borrows_out_of_scope_at_location`].
RegionInferenceContext,
/// The recommended option. Retrieves the maximal amount of information
/// without significant slowdowns.
///
/// Implies [`RegionInferenceContext`](ConsumerOptions::RegionInferenceContext),
/// and additionally retrieve the [`LocationTable`] and [`PoloniusInput`] that
/// would be given to Polonius. Critically, this does not run Polonius, which
/// one may want to avoid due to performance issues on large bodies.
PoloniusInputFacts,
/// Implies [`PoloniusInputFacts`](ConsumerOptions::PoloniusInputFacts),
/// and additionally runs Polonius to calculate the [`PoloniusOutput`].
PoloniusOutputFacts,
}
impl ConsumerOptions {
/// Should the Polonius input facts be computed?
pub(crate) fn polonius_input(&self) -> bool {
matches!(self, Self::PoloniusInputFacts | Self::PoloniusOutputFacts)
}
/// Should we run Polonius and collect the output facts?
pub(crate) fn polonius_output(&self) -> bool {
matches!(self, Self::PoloniusOutputFacts)
}
}
/// This function computes borrowck facts for the given body. The [`ConsumerOptions`]
/// determine which facts are returned. This function makes a copy of the body because
/// it needs to regenerate the region identifiers. It should never be invoked during a
/// typical compilation session due to the unnecessary overhead of returning
/// [`BodyWithBorrowckFacts`].
/// ///
/// Note: /// Note:
/// * This function will panic if the required body was already stolen. This /// * This function will panic if the required body was already stolen. This
@ -28,10 +68,14 @@ pub use super::{
/// that shows how to do this at `tests/run-make/obtain-borrowck/`. /// that shows how to do this at `tests/run-make/obtain-borrowck/`.
/// ///
/// * Polonius is highly unstable, so expect regular changes in its signature or other details. /// * Polonius is highly unstable, so expect regular changes in its signature or other details.
pub fn get_body_with_borrowck_facts(tcx: TyCtxt<'_>, def: LocalDefId) -> BodyWithBorrowckFacts<'_> { pub fn get_body_with_borrowck_facts(
tcx: TyCtxt<'_>,
def: LocalDefId,
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
let (input_body, promoted) = tcx.mir_promoted(def); let (input_body, promoted) = tcx.mir_promoted(def);
let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def)).build(); let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def)).build();
let input_body: &Body<'_> = &input_body.borrow(); let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow(); let promoted: &IndexSlice<_, _> = &promoted.borrow();
*super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap() *super::do_mir_borrowck(&infcx, input_body, promoted, Some(options)).1.unwrap()
} }

View file

@ -231,14 +231,12 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
} }
} }
impl<'a, 'tcx> Borrows<'a, 'tcx> { pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
pub(crate) fn new( body: &Body<'tcx>,
tcx: TyCtxt<'tcx>, regioncx: &RegionInferenceContext<'tcx>,
body: &'a Body<'tcx>, borrow_set: &BorrowSet<'tcx>,
nonlexical_regioncx: &'a RegionInferenceContext<'tcx>, ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
borrow_set: &'a BorrowSet<'tcx>, let mut prec = OutOfScopePrecomputer::new(body, regioncx);
) -> Self {
let mut prec = OutOfScopePrecomputer::new(body, nonlexical_regioncx);
for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
let borrow_region = borrow_data.region; let borrow_region = borrow_data.region;
let location = borrow_data.reserve_location; let location = borrow_data.reserve_location;
@ -246,12 +244,19 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location); prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
} }
Borrows { prec.borrows_out_of_scope_at_location
tcx,
body,
borrow_set,
borrows_out_of_scope_at_location: prec.borrows_out_of_scope_at_location,
} }
impl<'a, 'tcx> Borrows<'a, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
nonlexical_regioncx: &'a RegionInferenceContext<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
) -> Self {
let borrows_out_of_scope_at_location =
calculate_borrows_out_of_scope_at_location(body, nonlexical_regioncx, borrow_set);
Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
} }
pub fn location(&self, idx: BorrowIndex) -> &Location { pub fn location(&self, idx: BorrowIndex) -> &Location {

View file

@ -62,6 +62,7 @@ use crate::session_diagnostics::VarNeedNotMut;
use self::diagnostics::{AccessKind, RegionName}; use self::diagnostics::{AccessKind, RegionName};
use self::location::LocationTable; use self::location::LocationTable;
use self::prefixes::PrefixSet; use self::prefixes::PrefixSet;
use consumers::ConsumerOptions;
use facts::AllFacts; use facts::AllFacts;
use self::path_utils::*; use self::path_utils::*;
@ -144,7 +145,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build(); tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build();
let input_body: &Body<'_> = &input_body.borrow(); let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow(); let promoted: &IndexSlice<_, _> = &promoted.borrow();
let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, false).0; let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, None).0;
debug!("mir_borrowck done"); debug!("mir_borrowck done");
tcx.arena.alloc(opt_closure_req) tcx.arena.alloc(opt_closure_req)
@ -152,15 +153,15 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
/// Perform the actual borrow checking. /// Perform the actual borrow checking.
/// ///
/// If `return_body_with_facts` is true, then return the body with non-erased /// Use `consumer_options: None` for the default behavior of returning
/// region ids on which the borrow checking was performed together with Polonius /// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
/// facts. /// to the given [`ConsumerOptions`].
#[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")] #[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
fn do_mir_borrowck<'tcx>( fn do_mir_borrowck<'tcx>(
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
input_body: &Body<'tcx>, input_body: &Body<'tcx>,
input_promoted: &IndexSlice<Promoted, Body<'tcx>>, input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
return_body_with_facts: bool, consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) { ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let def = input_body.source.def_id().expect_local(); let def = input_body.source.def_id().expect_local();
debug!(?def); debug!(?def);
@ -241,8 +242,6 @@ fn do_mir_borrowck<'tcx>(
let borrow_set = let borrow_set =
Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.unstable_opts.polonius;
// Compute non-lexical lifetimes. // Compute non-lexical lifetimes.
let nll::NllOutput { let nll::NllOutput {
regioncx, regioncx,
@ -262,7 +261,7 @@ fn do_mir_borrowck<'tcx>(
&mdpe.move_data, &mdpe.move_data,
&borrow_set, &borrow_set,
&upvars, &upvars,
use_polonius, consumer_options,
); );
// Dump MIR results into a file, if that is enabled. This let us // Dump MIR results into a file, if that is enabled. This let us
@ -444,13 +443,15 @@ fn do_mir_borrowck<'tcx>(
tainted_by_errors, tainted_by_errors,
}; };
let body_with_facts = if return_body_with_facts { let body_with_facts = if consumer_options.is_some() {
let output_facts = mbcx.polonius_output.expect("Polonius output was not computed"); let output_facts = mbcx.polonius_output;
Some(Box::new(BodyWithBorrowckFacts { Some(Box::new(BodyWithBorrowckFacts {
body: body_owned, body: body_owned,
input_facts: *polonius_input.expect("Polonius input facts were not generated"), borrow_set,
region_inference_context: regioncx,
location_table: polonius_input.as_ref().map(|_| location_table_owned),
input_facts: polonius_input,
output_facts, output_facts,
location_table: location_table_owned,
})) }))
} else { } else {
None None
@ -469,12 +470,20 @@ fn do_mir_borrowck<'tcx>(
pub struct BodyWithBorrowckFacts<'tcx> { pub struct BodyWithBorrowckFacts<'tcx> {
/// A mir body that contains region identifiers. /// A mir body that contains region identifiers.
pub body: Body<'tcx>, pub body: Body<'tcx>,
/// Polonius input facts. /// The set of borrows occurring in `body` with data about them.
pub input_facts: AllFacts, pub borrow_set: Rc<BorrowSet<'tcx>>,
/// Polonius output facts. /// Context generated during borrowck, intended to be passed to
pub output_facts: Rc<self::nll::PoloniusOutput>, /// [`OutOfScopePrecomputer`](dataflow::OutOfScopePrecomputer).
/// The table that maps Polonius points to locations in the table. pub region_inference_context: Rc<RegionInferenceContext<'tcx>>,
pub location_table: LocationTable, /// The table that maps Polonius points to locations in the table. Populated
/// when using [`ConsumerOptions::PoloniusInputFacts`] or above.
pub location_table: Option<LocationTable>,
/// Polonius input facts. Populated when using
/// [`ConsumerOptions::PoloniusInputFacts`] or above.
pub input_facts: Option<Box<AllFacts>>,
/// Polonius output facts. Populated when using
/// [`ConsumerOptions::PoloniusOutputFacts`] or above.
pub output_facts: Option<Rc<self::nll::PoloniusOutput>>,
} }
pub struct BorrowckInferCtxt<'cx, 'tcx> { pub struct BorrowckInferCtxt<'cx, 'tcx> {

View file

@ -27,6 +27,7 @@ use rustc_mir_dataflow::ResultsCursor;
use crate::{ use crate::{
borrow_set::BorrowSet, borrow_set::BorrowSet,
constraint_generation, constraint_generation,
consumers::ConsumerOptions,
diagnostics::RegionErrors, diagnostics::RegionErrors,
facts::{AllFacts, AllFactsExt, RustcFacts}, facts::{AllFacts, AllFactsExt, RustcFacts},
invalidation, invalidation,
@ -165,10 +166,14 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
move_data: &MoveData<'tcx>, move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>, borrow_set: &BorrowSet<'tcx>,
upvars: &[Upvar<'tcx>], upvars: &[Upvar<'tcx>],
use_polonius: bool, consumer_options: Option<ConsumerOptions>,
) -> NllOutput<'tcx> { ) -> NllOutput<'tcx> {
let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
|| infcx.tcx.sess.opts.unstable_opts.polonius;
let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
|| infcx.tcx.sess.opts.unstable_opts.polonius;
let mut all_facts = let mut all_facts =
(use_polonius || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default()); (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default());
let universal_regions = Rc::new(universal_regions); let universal_regions = Rc::new(universal_regions);
@ -189,7 +194,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
move_data, move_data,
elements, elements,
upvars, upvars,
use_polonius, polonius_input,
); );
if let Some(all_facts) = &mut all_facts { if let Some(all_facts) = &mut all_facts {
@ -284,7 +289,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
all_facts.write_to_dir(dir_path, location_table).unwrap(); all_facts.write_to_dir(dir_path, location_table).unwrap();
} }
if use_polonius { if polonius_output {
let algorithm = let algorithm =
env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid")); env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid"));
let algorithm = Algorithm::from_str(&algorithm).unwrap(); let algorithm = Algorithm::from_str(&algorithm).unwrap();

View file

@ -7,7 +7,7 @@ use rustc_middle::mir::{Body, Mutability, Place};
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
/// Extension methods for the `Place` type. /// Extension methods for the `Place` type.
pub(crate) trait PlaceExt<'tcx> { pub trait PlaceExt<'tcx> {
/// Returns `true` if we can safely ignore borrows of this place. /// Returns `true` if we can safely ignore borrows of this place.
/// This is true whenever there is no action that the user can do /// This is true whenever there is no action that the user can do
/// to the place `self` that would invalidate the borrow. This is true /// to the place `self` that would invalidate the borrow. This is true

View file

@ -16,7 +16,7 @@ use std::iter;
/// being run in the calling context, the conservative choice is to assume the compared indices /// being run in the calling context, the conservative choice is to assume the compared indices
/// are disjoint (and therefore, do not overlap). /// are disjoint (and therefore, do not overlap).
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum PlaceConflictBias { pub enum PlaceConflictBias {
Overlap, Overlap,
NoOverlap, NoOverlap,
} }
@ -24,7 +24,7 @@ pub(crate) enum PlaceConflictBias {
/// Helper function for checking if places conflict with a mutable borrow and deep access depth. /// Helper function for checking if places conflict with a mutable borrow and deep access depth.
/// This is used to check for places conflicting outside of the borrow checking code (such as in /// This is used to check for places conflicting outside of the borrow checking code (such as in
/// dataflow). /// dataflow).
pub(crate) fn places_conflict<'tcx>( pub fn places_conflict<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
body: &Body<'tcx>, body: &Body<'tcx>,
borrow_place: Place<'tcx>, borrow_place: Place<'tcx>,