Skip to content

Commit

Permalink
Auto merge of rust-lang#125069 - amandasystems:scc-refactor, r=<try>
Browse files Browse the repository at this point in the history
Extend SCC construction to enable extra functionality

Do YOU feel like your SCC construction doesn't do enough? Then I have a patch for you! SCCs can now do *everything*! Well, almost.

This patch has been extracted from rust-lang#123720. It specifically enhances
`Sccs` to allow tracking arbitrary commutative properties (think min/max mappings on nodes vs arbitrary closures) of strongly connected components, including
- reachable values (max/min)
- SCC-internal values (max/min)

This helps with among other things universe computation. We can now identify
SCC universes as a reasonably straightforward "find max/min" operation during SCC construction. This is also included in this patch.

It's also more or less zero-cost; don't use the new features, don't pay for them.

This commit also vastly extends the documentation of the SCCs module, which I had a very hard time following. It may or may not have gotten easier to read for someone else.

I believe this logic can also be used in leak check, but haven't checked. Ha. ha. Ha.
  • Loading branch information
bors committed May 20, 2024
2 parents e8ada6a + b453158 commit f02f9b5
Show file tree
Hide file tree
Showing 6 changed files with 647 additions and 349 deletions.
111 changes: 95 additions & 16 deletions compiler/rustc_borrowck/src/constraints/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,106 @@
use rustc_data_structures::graph::scc::Sccs;
use crate::region_infer::RegionDefinition;
use crate::type_check::Locations;
use rustc_data_structures::graph::scc::{self, Sccs};
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
use rustc_middle::ty::{RegionVid, UniverseIndex, VarianceDiagInfo};
use rustc_span::Span;
use std::fmt;
use std::ops::Index;

use crate::type_check::Locations;

pub(crate) mod graph;

pub type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>;

/// An annotation for region graph SCCs that tracks
/// the values of its elements.
#[derive(Copy, Debug, Clone)]
pub struct RegionTracker {
/// The largest universe of a placeholder reached from this SCC.
/// This includes placeholders within this SCC.
max_placeholder_universe_reached: UniverseIndex,

/// The smallest universe index reachable form the nodes of this SCC.
min_reachable_universe: UniverseIndex,

/// The representative Region Variable Id for this SCC. We prefer
/// placeholders over existentially quantified variables, otherwise
/// it's the one with the smallest Region Variable ID.
pub representative: RegionVid,

/// Is the current representative a placeholder?
representative_is_placeholder: bool,

/// Is the current representative existentially quantified?
representative_is_existential: bool,
}

impl scc::Annotation for RegionTracker {
fn merge_scc(mut self, mut other: Self) -> Self {
// Prefer any placeholder over any existential
if other.representative_is_placeholder && self.representative_is_existential {
other.merge_min_max_seen(&self);
return other;
}

if self.representative_is_placeholder && other.representative_is_existential
|| (self.representative <= other.representative)
{
self.merge_min_max_seen(&other);
return self;
}
other.merge_min_max_seen(&self);
other
}

fn merge_reached(mut self, other: Self) -> Self {
// No update to in-component values, only add seen values.
self.merge_min_max_seen(&other);
self
}
}

impl RegionTracker {
pub fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
let (representative_is_placeholder, representative_is_existential) = match definition.origin
{
rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false),
rustc_infer::infer::NllRegionVariableOrigin::Placeholder(_) => (true, false),
rustc_infer::infer::NllRegionVariableOrigin::Existential { .. } => (false, true),
};

let placeholder_universe =
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };

Self {
max_placeholder_universe_reached: placeholder_universe,
min_reachable_universe: definition.universe,
representative: rvid,
representative_is_placeholder,
representative_is_existential,
}
}
pub fn universe(self) -> UniverseIndex {
self.min_reachable_universe
}

fn merge_min_max_seen(&mut self, other: &Self) {
self.max_placeholder_universe_reached = std::cmp::max(
self.max_placeholder_universe_reached,
other.max_placeholder_universe_reached,
);

self.min_reachable_universe =
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
}

/// Returns `true` if during the annotated SCC reaches a placeholder
/// with a universe larger than the smallest reachable one, `false` otherwise.
pub fn has_incompatible_universes(&self) -> bool {
self.universe().cannot_name(self.max_placeholder_universe_reached)
}
}

/// A set of NLL region constraints. These include "outlives"
/// constraints of the form `R1: R2`. Each constraint is identified by
/// a unique `OutlivesConstraintIndex` and you can index into the set
Expand Down Expand Up @@ -45,18 +136,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
}

/// Computes cycles (SCCs) in the graph of regions. In particular,
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
/// them into an SCC, and find the relationships between SCCs.
pub(crate) fn compute_sccs(
&self,
constraint_graph: &graph::NormalConstraintGraph,
static_region: RegionVid,
) -> Sccs<RegionVid, ConstraintSccIndex> {
let region_graph = &constraint_graph.region_graph(self, static_region);
Sccs::new(region_graph)
}

pub(crate) fn outlives(
&self,
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
Expand Down

0 comments on commit f02f9b5

Please sign in to comment.