Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ struct CollectRegionConstraintsResult<'tcx> {
deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
polonius_facts: Option<AllFacts<RustcFacts>>,
polonius_context: Option<PoloniusContext>,
polonius_context: Option<PoloniusContext<'tcx>>,
}

/// Start borrow checking by collecting the region constraints for
Expand Down Expand Up @@ -798,7 +798,7 @@ pub(crate) struct MirBorrowckCtxt<'a, 'diag, 'tcx> {
/// Results of Polonius analysis.
polonius_output: Option<&'a PoloniusOutput>,
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
polonius_context: Option<&'a PoloniusContext>,
polonius_context: Option<&'a PoloniusContext<'tcx>>,
}

// Check that:
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_borrowck/src/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub(crate) struct NllOutput<'tcx> {

/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
/// localized typeck and liveness constraints.
pub polonius_context: Option<PoloniusContext>,
pub polonius_context: Option<PoloniusContext<'tcx>>,
}

/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
Expand Down Expand Up @@ -121,7 +121,7 @@ pub(crate) fn compute_regions<'tcx>(
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
constraints: MirTypeckRegionConstraints<'tcx>,
mut polonius_facts: Option<AllFacts<RustcFacts>>,
mut polonius_context: Option<PoloniusContext>,
mut polonius_context: Option<PoloniusContext<'tcx>>,
) -> NllOutput<'tcx> {
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
Expand All @@ -144,20 +144,20 @@ pub(crate) fn compute_regions<'tcx>(
&lowered_constraints,
);

let num_points = location_map.num_points();

// If requested for `-Zpolonius=next`, compute loan liveness information.
// This is done prior to `RegionInferenceContext::new`, because we may add
// additional liveness constraints.
if let Some(polonius_context) = polonius_context.as_mut() {
let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness");
polonius_context.compute_loan_liveness(
infcx,
&mut lowered_constraints.liveness_constraints,
lowered_constraints.outlives_constraints.outlives().iter().copied(),
&universal_region_relations.universal_regions,
body,
move_data,
&location_map,
borrow_set,
num_points,
);
}

Expand Down
71 changes: 28 additions & 43 deletions compiler/rustc_borrowck/src/polonius/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_index::IndexVec;
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::RegionVid;
use rustc_mir_dataflow::points::PointIndex;
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};

use crate::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::dataflow::BorrowIndex;
use crate::polonius::{ConstraintDirection, LiveRegionVariances};
use crate::region_infer::values::LivenessValues;
use crate::polonius::ConstraintDirection;
use crate::polonius::liveness::RegionLiveness;
use crate::type_check::Locations;
use crate::universal_regions::UniversalRegions;

/// A localized outlives constraint reifies the CFG location where the outlives constraint holds,
/// within the origins themselves as if they were different from point to point: from `a: b`
Expand Down Expand Up @@ -49,11 +48,17 @@ pub(super) struct LocalizedConstraintGraph {
logical_edges: IndexVec<RegionVid, FxIndexSet<RegionVid>>,
}

/// The source of liveness information for a given region.
pub(super) trait LivenessSource<'loc> {
fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_>;
fn location_map(&self) -> &'loc DenseLocationMap;
}

/// The visitor interface when traversing a `LocalizedConstraintGraph`.
pub(super) trait LocalizedConstraintGraphVisitor {
/// Callback called when traversing a given `loan` encounters a localized `node` it hasn't
/// visited before.
fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode) {}
fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode, _is_live: bool) {}

/// Callback called when discovering a new `successor` node for the `current_node`.
fn on_successor_discovered(&mut self, _current_node: LocalizedNode, _successor: LocalizedNode) {
Expand All @@ -63,7 +68,7 @@ pub(super) trait LocalizedConstraintGraphVisitor {
impl LocalizedConstraintGraph {
/// Traverses the constraints and returns the indexed graph of edges per node.
pub(super) fn new<'tcx>(
liveness: &LivenessValues,
location_map: &DenseLocationMap,
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
) -> Self {
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
Expand All @@ -80,7 +85,7 @@ impl LocalizedConstraintGraph {
Locations::Single(location) => {
let node = LocalizedNode {
region: outlives_constraint.sup,
point: liveness.point_from_location(location),
point: location_map.point_from_location(location),
};
edges.entry(node).or_default().insert(outlives_constraint.sub);
}
Expand All @@ -92,20 +97,18 @@ impl LocalizedConstraintGraph {

/// Traverses the localized constraint graph per-loan, and notifies the `visitor` of discovered
/// nodes and successors.
pub(super) fn traverse<'tcx>(
pub(super) fn traverse<'tcx, 'loc>(
&self,
body: &Body<'tcx>,
liveness: &LivenessValues,
live_region_variances: &LiveRegionVariances,
universal_regions: &UniversalRegions<'tcx>,
borrow_set: &BorrowSet<'tcx>,
liveness_source: &mut impl LivenessSource<'loc>,
visitor: &mut impl LocalizedConstraintGraphVisitor,
) {
let live_regions = liveness.points();

let mut visited = FxHashSet::default();
let mut stack = Vec::new();

let location_map = liveness_source.location_map();

// Compute reachability per loan by traversing each loan's subgraph starting from where it
// is introduced.
for (loan_idx, loan) in borrow_set.iter_enumerated() {
Expand All @@ -114,15 +117,16 @@ impl LocalizedConstraintGraph {

let start_node = LocalizedNode {
region: loan.region,
point: liveness.point_from_location(loan.reserve_location),
point: location_map.point_from_location(loan.reserve_location),
};
visited.insert(start_node);
stack.push(start_node);

while let Some(node) = stack.pop() {
let liveness = liveness_source.liveness_for_region(node.region);
// We've reached a node we haven't visited before.
let location = liveness.location_from_point(node.point);
visitor.on_node_traversed(loan_idx, node);
let location = location_map.to_location(node.point);
visitor.on_node_traversed(loan_idx, node, liveness.is_live_at(node.point));

// When we find a _new_ successor, we'd like to
// - visit it eventually,
Expand All @@ -140,26 +144,6 @@ impl LocalizedConstraintGraph {
// - the materialized logical edges that exist virtually at all points for this
// node's region, localized at this point.

// Universal regions propagate loans along the CFG, i.e. forwards only.
let is_universal_region = universal_regions.is_universal_region(node.region);

// Note: there currently are cases related to promoted and const generics, where we don't yet
// have variance information (possibly about temporary regions created when typeck sanitizes the
// promoteds). Until that is done, we conservatively fallback to maximizing reachability by
// adding a bidirectional edge here. This will not limit traversal whatsoever, and thus
// propagate liveness when needed.
//
// FIXME: add the missing variance information and remove this fallback bidirectional edge.
let liveness_direction = if is_universal_region {
ConstraintDirection::Forward
} else {
live_region_variances
.get(node.region)
.copied()
.flatten()
.unwrap_or(ConstraintDirection::Bidirectional)
};

// The physical edges present at this node are:
//
// 1. the typeck edges that flow from region to region *at this point*.
Expand All @@ -178,14 +162,14 @@ impl LocalizedConstraintGraph {
//
// We still need to check liveness for each next point though.
if matches!(
liveness_direction,
liveness.direction,
ConstraintDirection::Forward | ConstraintDirection::Bidirectional
) {
if body[location.block].statements.get(location.statement_index).is_some() {
// Intra-block edges, straight line constraints from each point to its successor
// within the same block.
let next_point = node.point + 1;
if live_regions.contains(node.region, next_point) {
if liveness.is_live_at(next_point) {
successor_found(LocalizedNode {
region: node.region,
point: next_point,
Expand All @@ -197,8 +181,8 @@ impl LocalizedConstraintGraph {
for successor_block in body[location.block].terminator().successors() {
let next_location =
Location { block: successor_block, statement_index: 0 };
let next_point = liveness.point_from_location(next_location);
if live_regions.contains(node.region, next_point) {
let next_point = location_map.point_from_location(next_location);
if liveness.is_live_at(next_point) {
successor_found(LocalizedNode {
region: node.region,
point: next_point,
Expand All @@ -219,9 +203,9 @@ impl LocalizedConstraintGraph {
// Liveness flows into the regions live at the next point. So, in a backwards view, we'll link
// the region from the current point, if it's live there, to the previous point.
if matches!(
liveness_direction,
liveness.direction,
ConstraintDirection::Backward | ConstraintDirection::Bidirectional
) && live_regions.contains(node.region, node.point)
) && liveness.is_live_at(node.point)
{
if location.statement_index > 0 {
// Backward edges to the predecessor point in the same block.
Expand All @@ -239,7 +223,8 @@ impl LocalizedConstraintGraph {
block: pred_block,
statement_index: body[pred_block].statements.len(),
};
let previous_point = liveness.point_from_location(previous_location);
let previous_point =
location_map.point_from_location(previous_location);
successor_found(LocalizedNode {
region: node.region,
point: previous_point,
Expand Down
48 changes: 36 additions & 12 deletions compiler/rustc_borrowck/src/polonius/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,55 @@ use rustc_index::IndexVec;
use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::points::PointIndex;
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
use rustc_session::config::MirIncludeSpans;

use crate::borrow_set::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::dataflow::BorrowIndex;
use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext};
use crate::polonius::liveness::RegionLiveness;
use crate::polonius::{
LiveRegionVariances, LivenessSource, LocalizedConstraintGraphVisitor, LocalizedNode,
PoloniusContext,
};
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::universal_regions::UniversalRegions;
use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext};

/// The polonius MIR dump template: a regular HTML file for easy editing, with special dummy
/// sections to be replaced by real contents.
const TEMPLATE: &str = include_str!("./dump/polonius-mir-dump.template.html");

/// A `LivenessSource` for already-existing liveness and variance data.
struct CachedLivenessSource<'a, 'tcx> {
live_region_variances: &'a LiveRegionVariances,
universal_regions: &'a UniversalRegions<'tcx>,
liveness: &'a LivenessValues,
}

impl<'a, 'tcx> LivenessSource<'a> for CachedLivenessSource<'a, 'tcx> {
fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_> {
RegionLiveness::new(
region,
self.live_region_variances,
self.universal_regions,
self.liveness.points(),
)
}
fn location_map(&self) -> &'a DenseLocationMap {
self.liveness.location_map()
}
}

/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
pub(crate) fn dump_polonius_mir<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
polonius_context: Option<&PoloniusContext>,
polonius_context: Option<&PoloniusContext<'tcx>>,
) {
let tcx = infcx.tcx;
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
Expand All @@ -41,16 +67,14 @@ pub(crate) fn dump_polonius_mir<'tcx>(

// If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its
// constraints here.
let mut liveness_source = CachedLivenessSource {
live_region_variances: &polonius_context.live_region_variances,
universal_regions: regioncx.universal_regions(),
liveness: regioncx.liveness_constraints(),
};
let mut collector = MirDumpCollector::default();
if let Some(graph) = &polonius_context.graph {
graph.traverse(
body,
regioncx.liveness_constraints(),
&polonius_context.live_region_variances,
regioncx.universal_regions(),
borrow_set,
&mut collector,
);
graph.traverse(body, borrow_set, &mut liveness_source, &mut collector);
}

let extra_data = &|pass_where, out: &mut dyn io::Write| {
Expand Down Expand Up @@ -98,7 +122,7 @@ struct MirDumpCollector {
}

impl LocalizedConstraintGraphVisitor for MirDumpCollector {
fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) {
fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode, _is_live: bool) {
self.reachability.entry(loan).or_default().push(node);
}

Expand Down
Loading
Loading