Skip to content
13 changes: 7 additions & 6 deletions compiler/rustc_borrowck/src/consumers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ pub use super::polonius::legacy::{
PoloniusFacts as PoloniusInput, PoloniusLocationTable, PoloniusOutput, PoloniusRegionVid,
RichLocation, RustcFacts,
};
pub use super::region_infer::RegionInferenceContext;
use crate::BorrowCheckRootCtxt;
use crate::region_infer::InferredRegions;

/// Struct used during mir borrowck to collect bodies with facts for a typeck root and all
/// its nested bodies.
Expand Down Expand Up @@ -59,15 +59,15 @@ impl<'tcx> BorrowckConsumer<'tcx> {
#[derive(Debug, Copy, Clone)]
pub enum ConsumerOptions {
/// Retrieve the [`Body`] along with the [`BorrowSet`]
/// and [`RegionInferenceContext`]. If you would like the body only, use
/// and [`InferredRegions`]. 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,
InferredRegions,
/// The recommended option. Retrieves the maximal amount of information
/// without significant slowdowns.
///
/// Implies [`RegionInferenceContext`](ConsumerOptions::RegionInferenceContext),
/// Implies [`InferredRegions`](ConsumerOptions::InferredRegions),
/// and additionally retrieve the [`PoloniusLocationTable`] 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.
Expand All @@ -89,9 +89,10 @@ pub struct BodyWithBorrowckFacts<'tcx> {
pub promoted: IndexVec<Promoted, Body<'tcx>>,
/// The set of borrows occurring in `body` with data about them.
pub borrow_set: BorrowSet<'tcx>,
/// Context generated during borrowck, intended to be passed to
/// The inferred region values. These are included because they
/// are necessary as input to
/// [`calculate_borrows_out_of_scope_at_location`].
pub region_inference_context: RegionInferenceContext<'tcx>,
pub inferred_regions: InferredRegions<'tcx>,
/// The table that maps Polonius points to locations in the table.
/// Populated when using [`ConsumerOptions::PoloniusInputFacts`]
/// or [`ConsumerOptions::PoloniusOutputFacts`].
Expand Down
65 changes: 39 additions & 26 deletions compiler/rustc_borrowck/src/dataflow.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fmt;

use either::Either;
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
use rustc_middle::mir::{
Expand All @@ -11,10 +12,13 @@ use rustc_mir_dataflow::impls::{
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
MaybeUninitializedPlacesDomain,
};
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
use tracing::debug;

use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
use crate::polonius::{LiveLoans, PoloniusDiagnosticsContext};
use crate::region_infer::InferredRegions;
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, places_conflict};

// This analysis is different to most others. Its results aren't computed with
// `iterate_to_fixpoint`, but are instead composed from the results of three sub-analyses that are
Expand Down Expand Up @@ -182,34 +186,33 @@ struct OutOfScopePrecomputer<'a, 'tcx> {
visited: DenseBitSet<mir::BasicBlock>,
visit_stack: Vec<mir::BasicBlock>,
body: &'a Body<'tcx>,
regioncx: &'a RegionInferenceContext<'tcx>,
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
}

impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
fn compute(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
scc_values: &InferredRegions<'_>,
borrow_set: &BorrowSet<'tcx>,
) -> FxIndexMap<Location, Vec<BorrowIndex>> {
let mut prec = OutOfScopePrecomputer {
visited: DenseBitSet::new_empty(body.basic_blocks.len()),
visit_stack: vec![],
body,
regioncx,
borrows_out_of_scope_at_location: FxIndexMap::default(),
};
for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
let borrow_region = borrow_data.region;
let location = borrow_data.reserve_location;
prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
prec.precompute_borrows_out_of_scope(scc_values, borrow_index, borrow_region, location);
}

prec.borrows_out_of_scope_at_location
}

fn precompute_borrows_out_of_scope(
&mut self,
scc_values: &InferredRegions<'_>,
borrow_index: BorrowIndex,
borrow_region: RegionVid,
first_location: Location,
Expand All @@ -222,12 +225,9 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
let first_lo = first_location.statement_index;
let first_hi = first_bb_data.statements.len();

if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
borrow_region,
first_block,
first_lo,
first_hi,
) {
if let Some(kill_stmt) =
scc_values.first_non_contained_inclusive(borrow_region, first_block, first_lo, first_hi)
{
let kill_location = Location { block: first_block, statement_index: kill_stmt };
// If region does not contain a point at the location, then add to list and skip
// successor locations.
Expand Down Expand Up @@ -255,7 +255,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
let bb_data = &self.body[block];
let num_stmts = bb_data.statements.len();
if let Some(kill_stmt) =
self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
scc_values.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
{
let kill_location = Location { block, statement_index: kill_stmt };
// If region does not contain a point at the location, then add to list and skip
Expand Down Expand Up @@ -285,25 +285,26 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
// This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
scc_values: &InferredRegions<'_>,
borrow_set: &BorrowSet<'tcx>,
) -> FxIndexMap<Location, Vec<BorrowIndex>> {
OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
OutOfScopePrecomputer::compute(body, scc_values, borrow_set)
}

struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
visited: DenseBitSet<mir::BasicBlock>,
visit_stack: Vec<mir::BasicBlock>,
body: &'a Body<'tcx>,
regioncx: &'a RegionInferenceContext<'tcx>,

live_loans: &'a LiveLoans,
location_map: &'a DenseLocationMap,
loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
}

impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
fn compute(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
polonius_diagnostics: &PoloniusDiagnosticsContext,
location_map: &DenseLocationMap,
borrow_set: &BorrowSet<'tcx>,
) -> FxIndexMap<Location, Vec<BorrowIndex>> {
// The in-tree polonius analysis computes loans going out of scope using the
Expand All @@ -312,7 +313,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
visited: DenseBitSet::new_empty(body.basic_blocks.len()),
visit_stack: vec![],
body,
regioncx,
live_loans: &polonius_diagnostics.live_loans,
location_map,
loans_out_of_scope_at_location: FxIndexMap::default(),
};
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
Expand Down Expand Up @@ -412,7 +414,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
// Reachability is location-insensitive, and we could take advantage of that, by jumping
// to a further point than just the next statement: we can jump to the furthest point
// within the block where `r` is live.
if self.regioncx.is_loan_live_at(loan_idx, location) {
let point = self.location_map.point_from_location(location);
if self.is_loan_live_at(loan_idx, point) {
continue;
}

Expand All @@ -423,21 +426,31 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {

None
}

fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
self.live_loans.contains(point, loan_idx)
}
}

impl<'a, 'tcx> Borrows<'a, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
inference_results: Either<&'a InferredRegions<'_>, &'a PoloniusDiagnosticsContext>,
location_map: &DenseLocationMap,
borrow_set: &'a BorrowSet<'tcx>,
) -> Self {
let borrows_out_of_scope_at_location =
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
} else {
PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
};
let borrows_out_of_scope_at_location = match inference_results {
Either::Left(inferred_regions) => {
calculate_borrows_out_of_scope_at_location(body, inferred_regions, borrow_set)
}
Either::Right(polonius_diagnostics) => PoloniusOutOfScopePrecomputer::compute(
body,
polonius_diagnostics,
location_map,
borrow_set,
),
};
Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,8 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
// started MIR borrowchecking with, so the region
// constraints have already been taken. Use the data from
// our `mbcx` instead.
|vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin),
|vid| mbcx.regioncx.definitions[vid].universe,
|vid| RegionVariableOrigin::Nll(mbcx.definitions[vid].origin),
|vid| mbcx.definitions[vid].universe,
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3454,7 +3454,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {

let tcx = self.infcx.tcx;

let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
let return_ty = self.universal_regions().unnormalized_output_ty;

// to avoid panics
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
Expand Down
34 changes: 24 additions & 10 deletions compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use super::{RegionName, UseSpans, find_use};
use crate::borrow_set::BorrowData;
use crate::constraints::OutlivesConstraint;
use crate::nll::ConstraintDescription;
use crate::region_infer::{BlameConstraint, Cause};
use crate::region_infer::{BlameConstraint, Cause, ConstraintSearch};
use crate::{MirBorrowckCtxt, WriteKind};

#[derive(Debug)]
Expand Down Expand Up @@ -572,14 +572,23 @@ fn suggest_rewrite_if_let<G: EmissionGuarantee>(
}
}

impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
pub(crate) fn constraint_search<'mbc>(&'mbc self) -> ConstraintSearch<'mbc, 'tcx> {
ConstraintSearch {
definitions: self.definitions,
fr_static: self.universal_regions().fr_static,
constraint_graph: &self.constraint_graph,
constraints: &self.outlives_constraints,
}
}

fn free_region_constraint_info(
&self,
borrow_region: RegionVid,
outlived_region: RegionVid,
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<OutlivesConstraint<'tcx>>)
{
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
let (blame_constraint, path) = self.constraint_search().best_blame_constraint(
borrow_region,
NllRegionVariableOrigin::FreeRegion,
outlived_region,
Expand Down Expand Up @@ -611,14 +620,17 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
borrow: &BorrowData<'tcx>,
kind_place: Option<(WriteKind, Place<'tcx>)>,
) -> BorrowExplanation<'tcx> {
let regioncx = &self.regioncx;
let body: &Body<'_> = self.body;
let tcx = self.infcx.tcx;

let borrow_region_vid = borrow.region;
debug!(?borrow_region_vid);

let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
let mut region_sub = self.constraint_search().find_sub_region_live_at(
&self.liveness_constraints,
borrow_region_vid,
location,
);
debug!(?region_sub);

let mut use_location = location;
Expand All @@ -630,11 +642,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
// loop iteration. In this case, try using the loop terminator location in
// `find_sub_region_live_at`.
if let Some(loop_terminator_location) =
regioncx.find_loop_terminator_location(borrow.region, body)
self.scc_values.find_loop_terminator_location(borrow.region, body)
{
region_sub = self
.regioncx
.find_sub_region_live_at(borrow_region_vid, loop_terminator_location);
region_sub = self.constraint_search().find_sub_region_live_at(
&self.liveness_constraints,
borrow_region_vid,
loop_terminator_location,
);
debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);
use_location = loop_terminator_location;
use_in_later_iteration_of_loop = true;
Expand All @@ -658,7 +672,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
false
}
};
match find_use::find(body, regioncx, tcx, region_sub, use_location) {
match find_use::find(body, self.scc_values, tcx, region_sub, use_location) {
Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => {
let span = body.source_info(location).span;
let spans = self
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_borrowck/src/diagnostics/find_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,36 @@ use rustc_middle::mir::{self, Body, Local, Location};
use rustc_middle::ty::{RegionVid, TyCtxt};

use crate::def_use::{self, DefUse};
use crate::region_infer::{Cause, RegionInferenceContext};
use crate::region_infer::{Cause, InferredRegions};

pub(crate) fn find<'tcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
scc_values: &InferredRegions<'_>,
tcx: TyCtxt<'tcx>,
region_vid: RegionVid,
start_point: Location,
) -> Option<Cause> {
let mut uf = UseFinder { body, regioncx, tcx, region_vid, start_point };
let mut uf = UseFinder { body, tcx, region_vid, start_point, scc_values: &scc_values };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the explicit & here


uf.find()
}

struct UseFinder<'a, 'tcx> {
struct UseFinder<'a, 'b, 'tcx> {
body: &'a Body<'tcx>,
regioncx: &'a RegionInferenceContext<'tcx>,
scc_values: &'a InferredRegions<'b>,
tcx: TyCtxt<'tcx>,
region_vid: RegionVid,
start_point: Location,
}

impl<'a, 'tcx> UseFinder<'a, 'tcx> {
impl<'a, 'b, 'tcx> UseFinder<'a, 'b, 'tcx> {
fn find(&mut self) -> Option<Cause> {
let mut queue = VecDeque::new();
let mut visited = FxIndexSet::default();

queue.push_back(self.start_point);
while let Some(p) = queue.pop_front() {
if !self.regioncx.region_contains(self.region_vid, p) {
if !self.scc_values.region_contains(self.region_vid, p) {
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let tcx = self.infcx.tcx;
let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
let outlived = constraint.sub;
if let Some(origin) = self.regioncx.definitions.get(outlived)
if let Some(origin) = self.definitions.get(outlived)
&& let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
&& let Some(id) = placeholder.bound.kind.get_id()
&& let Some(placeholder_id) = id.as_local()
Expand Down
Loading