replace location-insensitive analysis with location-sensitive analysis
we're in in the endgame now set up the location-sensitive analysis end to end: - stop recording inflowing loans and loan liveness in liveness - replace location-insensitive liveness data with live loans computed by reachability - remove equivalence between polonius scopes and NLL scopes, and only run one scope computation
This commit is contained in:
parent
0c978bc4e6
commit
67a1bb1554
6 changed files with 58 additions and 139 deletions
|
@ -325,10 +325,17 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
||||||
let sccs = self.regioncx.constraint_sccs();
|
let sccs = self.regioncx.constraint_sccs();
|
||||||
let universal_regions = self.regioncx.universal_regions();
|
let universal_regions = self.regioncx.universal_regions();
|
||||||
|
|
||||||
|
// The loop below was useful for the location-insensitive analysis but shouldn't be
|
||||||
|
// impactful in the location-sensitive case. It seems that it does, however, as without it a
|
||||||
|
// handful of tests fail. That likely means some liveness or outlives data related to choice
|
||||||
|
// regions is missing
|
||||||
|
// FIXME: investigate the impact of loans traversing applied member constraints and why some
|
||||||
|
// tests fail otherwise.
|
||||||
|
//
|
||||||
// We first handle the cases where the loan doesn't go out of scope, depending on the
|
// We first handle the cases where the loan doesn't go out of scope, depending on the
|
||||||
// issuing region's successors.
|
// issuing region's successors.
|
||||||
for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
|
for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
|
||||||
// 1. Via applied member constraints
|
// Via applied member constraints
|
||||||
//
|
//
|
||||||
// The issuing region can flow into the choice regions, and they are either:
|
// The issuing region can flow into the choice regions, and they are either:
|
||||||
// - placeholders or free regions themselves,
|
// - placeholders or free regions themselves,
|
||||||
|
@ -346,14 +353,6 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Via regions that are live at all points: placeholders and free regions.
|
|
||||||
//
|
|
||||||
// If the issuing region outlives such a region, its loan escapes the function and
|
|
||||||
// cannot go out of scope. We can early return.
|
|
||||||
if self.regioncx.is_region_live_at_all_points(successor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_block = loan_issued_at.block;
|
let first_block = loan_issued_at.block;
|
||||||
|
@ -461,13 +460,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||||
regioncx: &RegionInferenceContext<'tcx>,
|
regioncx: &RegionInferenceContext<'tcx>,
|
||||||
borrow_set: &'a BorrowSet<'tcx>,
|
borrow_set: &'a BorrowSet<'tcx>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut borrows_out_of_scope_at_location =
|
let borrows_out_of_scope_at_location =
|
||||||
calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set);
|
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||||
|
calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
|
||||||
// The in-tree polonius analysis computes loans going out of scope using the set-of-loans
|
} else {
|
||||||
// model, and makes sure they're identical to the existing computation of the set-of-points
|
// The in-tree polonius analysis computes loans going out of scope using the
|
||||||
// model.
|
// set-of-loans model.
|
||||||
if tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
|
||||||
let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
|
let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
|
||||||
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
|
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
|
||||||
let issuing_region = loan_data.region;
|
let issuing_region = loan_data.region;
|
||||||
|
@ -480,15 +478,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
polonius_prec.loans_out_of_scope_at_location
|
||||||
borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
|
};
|
||||||
"polonius loan scopes differ from NLL borrow scopes, for body {:?}",
|
|
||||||
body.span,
|
|
||||||
);
|
|
||||||
|
|
||||||
borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;
|
|
||||||
}
|
|
||||||
|
|
||||||
Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
|
Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
||||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
|
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
|
||||||
// and use them to compute loan liveness.
|
// and use them to compute loan liveness.
|
||||||
let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| {
|
let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| {
|
||||||
polonius_context.compute_loan_liveness(infcx.tcx, ®ioncx, body, borrow_set)
|
polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
|
||||||
});
|
});
|
||||||
|
|
||||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||||
|
|
|
@ -98,7 +98,7 @@ impl PoloniusContext {
|
||||||
pub(crate) fn compute_loan_liveness<'tcx>(
|
pub(crate) fn compute_loan_liveness<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
regioncx: &RegionInferenceContext<'tcx>,
|
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
borrow_set: &BorrowSet<'tcx>,
|
borrow_set: &BorrowSet<'tcx>,
|
||||||
) -> LocalizedOutlivesConstraintSet {
|
) -> LocalizedOutlivesConstraintSet {
|
||||||
|
@ -126,15 +126,14 @@ impl PoloniusContext {
|
||||||
|
|
||||||
// Now that we have a complete graph, we can compute reachability to trace the liveness of
|
// Now that we have a complete graph, we can compute reachability to trace the liveness of
|
||||||
// loans for the next step in the chain, the NLL loan scope and active loans computations.
|
// loans for the next step in the chain, the NLL loan scope and active loans computations.
|
||||||
let _live_loans = compute_loan_liveness(
|
let live_loans = compute_loan_liveness(
|
||||||
tcx,
|
tcx,
|
||||||
body,
|
body,
|
||||||
regioncx.liveness_constraints(),
|
regioncx.liveness_constraints(),
|
||||||
borrow_set,
|
borrow_set,
|
||||||
&localized_outlives_constraints,
|
&localized_outlives_constraints,
|
||||||
);
|
);
|
||||||
// FIXME: record the live loans in the regioncx's liveness constraints, where the
|
regioncx.record_live_loans(live_loans);
|
||||||
// location-insensitive variant's data is stored.
|
|
||||||
|
|
||||||
localized_outlives_constraints
|
localized_outlives_constraints
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra
|
||||||
use crate::dataflow::BorrowIndex;
|
use crate::dataflow::BorrowIndex;
|
||||||
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
|
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
|
||||||
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
|
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
|
||||||
|
use crate::polonius::LiveLoans;
|
||||||
use crate::polonius::legacy::PoloniusOutput;
|
use crate::polonius::legacy::PoloniusOutput;
|
||||||
use crate::region_infer::reverse_sccs::ReverseSccGraph;
|
use crate::region_infer::reverse_sccs::ReverseSccGraph;
|
||||||
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
|
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
|
||||||
|
@ -2171,28 +2172,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static)
|
self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given region is considered live at all points: whether it is a
|
|
||||||
/// placeholder or a free region.
|
|
||||||
pub(crate) fn is_region_live_at_all_points(&self, region: RegionVid) -> bool {
|
|
||||||
// FIXME: there must be a cleaner way to find this information. At least, when
|
|
||||||
// higher-ranked subtyping is abstracted away from the borrowck main path, we'll only
|
|
||||||
// need to check whether this is a universal region.
|
|
||||||
let origin = self.region_definition(region).origin;
|
|
||||||
let live_at_all_points = matches!(
|
|
||||||
origin,
|
|
||||||
NllRegionVariableOrigin::Placeholder(_) | NllRegionVariableOrigin::FreeRegion
|
|
||||||
);
|
|
||||||
live_at_all_points
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the `loan_idx` is live at the given `location`: whether its issuing
|
|
||||||
/// region is contained within the type of a variable that is live at this point.
|
|
||||||
/// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`.
|
|
||||||
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool {
|
|
||||||
let point = self.liveness_constraints.point_from_location(location);
|
|
||||||
self.liveness_constraints.is_loan_live_at(loan_idx, point)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the representative `RegionVid` for a given SCC.
|
/// Returns the representative `RegionVid` for a given SCC.
|
||||||
/// See `RegionTracker` for how a region variable ID is chosen.
|
/// See `RegionTracker` for how a region variable ID is chosen.
|
||||||
///
|
///
|
||||||
|
@ -2208,6 +2187,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
pub(crate) fn liveness_constraints(&self) -> &LivenessValues {
|
pub(crate) fn liveness_constraints(&self) -> &LivenessValues {
|
||||||
&self.liveness_constraints
|
&self.liveness_constraints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active
|
||||||
|
/// loans dataflow computations.
|
||||||
|
pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) {
|
||||||
|
self.liveness_constraints.record_live_loans(live_loans);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the `loan_idx` is live at the given `location`: whether its issuing
|
||||||
|
/// region is contained within the type of a variable that is live at this point.
|
||||||
|
/// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`.
|
||||||
|
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool {
|
||||||
|
let point = self.liveness_constraints.point_from_location(location);
|
||||||
|
self.liveness_constraints.is_loan_live_at(loan_idx, point)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> RegionDefinition<'tcx> {
|
impl<'tcx> RegionDefinition<'tcx> {
|
||||||
|
|
|
@ -11,6 +11,7 @@ use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::BorrowIndex;
|
use crate::BorrowIndex;
|
||||||
|
use crate::polonius::LiveLoans;
|
||||||
|
|
||||||
rustc_index::newtype_index! {
|
rustc_index::newtype_index! {
|
||||||
/// A single integer representing a `ty::Placeholder`.
|
/// A single integer representing a `ty::Placeholder`.
|
||||||
|
@ -50,29 +51,8 @@ pub(crate) struct LivenessValues {
|
||||||
/// region is live, only that it is.
|
/// region is live, only that it is.
|
||||||
points: Option<SparseIntervalMatrix<RegionVid, PointIndex>>,
|
points: Option<SparseIntervalMatrix<RegionVid, PointIndex>>,
|
||||||
|
|
||||||
/// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at
|
/// When using `-Zpolonius=next`, the set of loans that are live at a given point in the CFG.
|
||||||
/// that point.
|
live_loans: Option<LiveLoans>,
|
||||||
pub(crate) loans: Option<LiveLoans>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data used to compute the loans that are live at a given point in the CFG, when using
|
|
||||||
/// `-Zpolonius=next`.
|
|
||||||
pub(crate) struct LiveLoans {
|
|
||||||
/// The set of loans that flow into a given region. When individual regions are marked as live
|
|
||||||
/// in the CFG, these inflowing loans are recorded as live.
|
|
||||||
pub(crate) inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
|
|
||||||
|
|
||||||
/// The set of loans that are live at a given point in the CFG.
|
|
||||||
pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LiveLoans {
|
|
||||||
pub(crate) fn new(num_loans: usize) -> Self {
|
|
||||||
LiveLoans {
|
|
||||||
live_loans: SparseBitMatrix::new(num_loans),
|
|
||||||
inflowing_loans: SparseBitMatrix::new(num_loans),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LivenessValues {
|
impl LivenessValues {
|
||||||
|
@ -82,7 +62,7 @@ impl LivenessValues {
|
||||||
live_regions: None,
|
live_regions: None,
|
||||||
points: Some(SparseIntervalMatrix::new(location_map.num_points())),
|
points: Some(SparseIntervalMatrix::new(location_map.num_points())),
|
||||||
location_map,
|
location_map,
|
||||||
loans: None,
|
live_loans: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +75,7 @@ impl LivenessValues {
|
||||||
live_regions: Some(Default::default()),
|
live_regions: Some(Default::default()),
|
||||||
points: None,
|
points: None,
|
||||||
location_map,
|
location_map,
|
||||||
loans: None,
|
live_loans: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +109,6 @@ impl LivenessValues {
|
||||||
} else if self.location_map.point_in_range(point) {
|
} else if self.location_map.point_in_range(point) {
|
||||||
self.live_regions.as_mut().unwrap().insert(region);
|
self.live_regions.as_mut().unwrap().insert(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When available, record the loans flowing into this region as live at the given point.
|
|
||||||
if let Some(loans) = self.loans.as_mut() {
|
|
||||||
if let Some(inflowing) = loans.inflowing_loans.row(region) {
|
|
||||||
loans.live_loans.union_row(point, inflowing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Records `region` as being live at all the given `points`.
|
/// Records `region` as being live at all the given `points`.
|
||||||
|
@ -146,17 +119,6 @@ impl LivenessValues {
|
||||||
} else if points.iter().any(|point| self.location_map.point_in_range(point)) {
|
} else if points.iter().any(|point| self.location_map.point_in_range(point)) {
|
||||||
self.live_regions.as_mut().unwrap().insert(region);
|
self.live_regions.as_mut().unwrap().insert(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When available, record the loans flowing into this region as live at the given points.
|
|
||||||
if let Some(loans) = self.loans.as_mut() {
|
|
||||||
if let Some(inflowing) = loans.inflowing_loans.row(region) {
|
|
||||||
if !inflowing.is_empty() {
|
|
||||||
for point in points.iter() {
|
|
||||||
loans.live_loans.union_row(point, inflowing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Records `region` as being live at all the control-flow points.
|
/// Records `region` as being live at all the control-flow points.
|
||||||
|
@ -213,12 +175,17 @@ impl LivenessValues {
|
||||||
self.location_map.to_location(point)
|
self.location_map.to_location(point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active
|
||||||
|
/// loans dataflow computations.
|
||||||
|
pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) {
|
||||||
|
self.live_loans = Some(live_loans);
|
||||||
|
}
|
||||||
|
|
||||||
/// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`.
|
/// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`.
|
||||||
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
|
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
|
||||||
self.loans
|
self.live_loans
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Accessing live loans requires `-Zpolonius=next`")
|
.expect("Accessing live loans requires `-Zpolonius=next`")
|
||||||
.live_loans
|
|
||||||
.contains(point, loan_idx)
|
.contains(point, loan_idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, Type
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::polonius;
|
use crate::polonius;
|
||||||
use crate::region_infer::values::{self, LiveLoans};
|
use crate::region_infer::values;
|
||||||
use crate::type_check::liveness::local_use_map::LocalUseMap;
|
use crate::type_check::liveness::local_use_map::LocalUseMap;
|
||||||
use crate::type_check::{NormalizeLocation, TypeChecker};
|
use crate::type_check::{NormalizeLocation, TypeChecker};
|
||||||
|
|
||||||
|
@ -44,37 +44,6 @@ pub(super) fn trace<'a, 'tcx>(
|
||||||
boring_locals: Vec<Local>,
|
boring_locals: Vec<Local>,
|
||||||
) {
|
) {
|
||||||
let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body);
|
let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body);
|
||||||
|
|
||||||
// When using `-Zpolonius=next`, compute the set of loans that can reach a given region.
|
|
||||||
if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
|
|
||||||
let borrow_set = &typeck.borrow_set;
|
|
||||||
let mut live_loans = LiveLoans::new(borrow_set.len());
|
|
||||||
let outlives_constraints = &typeck.constraints.outlives_constraints;
|
|
||||||
let graph = outlives_constraints.graph(typeck.infcx.num_region_vars());
|
|
||||||
let region_graph =
|
|
||||||
graph.region_graph(outlives_constraints, typeck.universal_regions.fr_static);
|
|
||||||
|
|
||||||
// Traverse each issuing region's constraints, and record the loan as flowing into the
|
|
||||||
// outlived region.
|
|
||||||
for (loan, issuing_region_data) in borrow_set.iter_enumerated() {
|
|
||||||
for succ in rustc_data_structures::graph::depth_first_search(
|
|
||||||
®ion_graph,
|
|
||||||
issuing_region_data.region,
|
|
||||||
) {
|
|
||||||
// We don't need to mention that a loan flows into its issuing region.
|
|
||||||
if succ == issuing_region_data.region {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
live_loans.inflowing_loans.insert(succ, loan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the inflowing loans in the liveness constraints: they will be used to compute live
|
|
||||||
// loans when liveness data is recorded there.
|
|
||||||
typeck.constraints.liveness_constraints.loans = Some(live_loans);
|
|
||||||
};
|
|
||||||
|
|
||||||
let cx = LivenessContext {
|
let cx = LivenessContext {
|
||||||
typeck,
|
typeck,
|
||||||
body,
|
body,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue