Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8cf5101

Browse files
committedJun 12, 2024·
Auto merge of rust-lang#125069 - amandasystems:scc-refactor, r=nikomatsakis
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.
2 parents 8337ba9 + d63708b commit 8cf5101

File tree

5 files changed

+670
-337
lines changed

5 files changed

+670
-337
lines changed
 

‎compiler/rustc_borrowck/src/constraints/mod.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
use rustc_data_structures::graph::scc::Sccs;
1+
use crate::type_check::Locations;
22
use rustc_index::{IndexSlice, IndexVec};
33
use rustc_middle::mir::ConstraintCategory;
44
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
55
use rustc_span::Span;
66
use std::fmt;
77
use std::ops::Index;
88

9-
use crate::type_check::Locations;
10-
119
pub(crate) mod graph;
1210

1311
/// A set of NLL region constraints. These include "outlives"
@@ -45,18 +43,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
4543
graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
4644
}
4745

48-
/// Computes cycles (SCCs) in the graph of regions. In particular,
49-
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
50-
/// them into an SCC, and find the relationships between SCCs.
51-
pub(crate) fn compute_sccs(
52-
&self,
53-
constraint_graph: &graph::NormalConstraintGraph,
54-
static_region: RegionVid,
55-
) -> Sccs<RegionVid, ConstraintSccIndex> {
56-
let region_graph = &constraint_graph.region_graph(self, static_region);
57-
Sccs::new(region_graph)
58-
}
59-
6046
pub(crate) fn outlives(
6147
&self,
6248
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {

‎compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 188 additions & 184 deletions
Large diffs are not rendered by default.

‎compiler/rustc_borrowck/src/region_infer/opaque_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
8585
// Use the SCC representative instead of directly using `region`.
8686
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
8787
let scc = self.constraint_sccs.scc(region.as_var());
88-
let vid = self.scc_representatives[scc];
88+
let vid = self.scc_representative(scc);
8989
let named = match self.definitions[vid].origin {
9090
// Iterate over all universal regions in a consistent order and find the
9191
// *first* equal region. This makes sure that equal lifetimes will have
@@ -213,7 +213,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
213213
let scc = self.constraint_sccs.scc(vid);
214214

215215
// Special handling of higher-ranked regions.
216-
if !self.scc_universes[scc].is_root() {
216+
if !self.scc_universe(scc).is_root() {
217217
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
218218
// If the region contains a single placeholder then they're equal.
219219
Some((0, placeholder)) => {

‎compiler/rustc_data_structures/src/graph/scc/mod.rs

Lines changed: 272 additions & 129 deletions
Large diffs are not rendered by default.

‎compiler/rustc_data_structures/src/graph/scc/tests.rs

Lines changed: 207 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,53 @@ extern crate test;
33
use super::*;
44
use crate::graph::tests::TestGraph;
55

6+
#[derive(Copy, Clone, Debug)]
7+
struct MaxReached(usize);
8+
type UsizeSccs = Sccs<usize, usize, ()>;
9+
type MaxReachedSccs = Sccs<usize, usize, MaxReached>;
10+
11+
impl Annotation for MaxReached {
12+
fn merge_scc(self, other: Self) -> Self {
13+
Self(std::cmp::max(other.0, self.0))
14+
}
15+
16+
fn merge_reached(self, other: Self) -> Self {
17+
self.merge_scc(other)
18+
}
19+
}
20+
21+
impl PartialEq<usize> for MaxReached {
22+
fn eq(&self, other: &usize) -> bool {
23+
&self.0 == other
24+
}
25+
}
26+
27+
impl MaxReached {
28+
fn from_usize(nr: usize) -> Self {
29+
Self(nr)
30+
}
31+
}
32+
33+
#[derive(Copy, Clone, Debug)]
34+
struct MinMaxIn {
35+
min: usize,
36+
max: usize,
37+
}
38+
39+
impl Annotation for MinMaxIn {
40+
fn merge_scc(self, other: Self) -> Self {
41+
Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) }
42+
}
43+
44+
fn merge_reached(self, _other: Self) -> Self {
45+
self
46+
}
47+
}
48+
649
#[test]
750
fn diamond() {
851
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
9-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
52+
let sccs: UsizeSccs = Sccs::new(&graph);
1053
assert_eq!(sccs.num_sccs(), 4);
1154
assert_eq!(sccs.num_sccs(), 4);
1255
}
@@ -34,7 +77,7 @@ fn test_big_scc() {
3477
+-- 2 <--+
3578
*/
3679
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]);
37-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
80+
let sccs: UsizeSccs = Sccs::new(&graph);
3881
assert_eq!(sccs.num_sccs(), 1);
3982
}
4083

@@ -50,7 +93,7 @@ fn test_three_sccs() {
5093
+-- 2 <--+
5194
*/
5295
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]);
53-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
96+
let sccs: UsizeSccs = Sccs::new(&graph);
5497
assert_eq!(sccs.num_sccs(), 3);
5598
assert_eq!(sccs.scc(0), 1);
5699
assert_eq!(sccs.scc(1), 0);
@@ -106,7 +149,7 @@ fn test_find_state_2() {
106149
// 2 InCycleWith { 1 }
107150
// 3 InCycleWith { 0 }
108151

109-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
152+
let sccs: UsizeSccs = Sccs::new(&graph);
110153
assert_eq!(sccs.num_sccs(), 1);
111154
assert_eq!(sccs.scc(0), 0);
112155
assert_eq!(sccs.scc(1), 0);
@@ -130,7 +173,7 @@ fn test_find_state_3() {
130173
*/
131174
let graph =
132175
TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]);
133-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
176+
let sccs: UsizeSccs = Sccs::new(&graph);
134177
assert_eq!(sccs.num_sccs(), 2);
135178
assert_eq!(sccs.scc(0), 0);
136179
assert_eq!(sccs.scc(1), 0);
@@ -165,7 +208,7 @@ fn test_deep_linear() {
165208
nodes.push((i - 1, i));
166209
}
167210
let graph = TestGraph::new(0, nodes.as_slice());
168-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
211+
let sccs: UsizeSccs = Sccs::new(&graph);
169212
assert_eq!(sccs.num_sccs(), NR_NODES);
170213
assert_eq!(sccs.scc(0), NR_NODES - 1);
171214
assert_eq!(sccs.scc(NR_NODES - 1), 0);
@@ -210,7 +253,164 @@ fn bench_sccc(b: &mut test::Bencher) {
210253
graph[21] = (7, 4);
211254
let graph = TestGraph::new(0, &graph[..]);
212255
b.iter(|| {
213-
let sccs: Sccs<_, usize> = Sccs::new(&graph);
256+
let sccs: UsizeSccs = Sccs::new(&graph);
214257
assert_eq!(sccs.num_sccs(), 3);
215258
});
216259
}
260+
261+
#[test]
262+
fn test_max_self_loop() {
263+
let graph = TestGraph::new(0, &[(0, 0)]);
264+
let sccs: MaxReachedSccs =
265+
Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) });
266+
assert_eq!(sccs.annotation(0), 17);
267+
}
268+
269+
#[test]
270+
fn test_max_branch() {
271+
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]);
272+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
273+
assert_eq!(sccs.annotation(sccs.scc(0)), 4);
274+
assert_eq!(sccs.annotation(sccs.scc(1)), 3);
275+
assert_eq!(sccs.annotation(sccs.scc(2)), 4);
276+
}
277+
#[test]
278+
fn test_single_cycle_max() {
279+
let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]);
280+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
281+
assert_eq!(sccs.annotation(sccs.scc(2)), 4);
282+
assert_eq!(sccs.annotation(sccs.scc(0)), 4);
283+
}
284+
285+
#[test]
286+
fn test_simple_cycle_max() {
287+
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]);
288+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
289+
assert_eq!(sccs.num_sccs(), 1);
290+
}
291+
292+
#[test]
293+
fn test_double_cycle_max() {
294+
let graph =
295+
TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]);
296+
let sccs: MaxReachedSccs =
297+
Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) });
298+
299+
assert_eq!(sccs.annotation(sccs.scc(0)).0, 2);
300+
}
301+
302+
#[test]
303+
fn test_bug_minimised() {
304+
let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]);
305+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n {
306+
3 => MaxReached(1),
307+
_ => MaxReached(0),
308+
});
309+
assert_eq!(sccs.annotation(sccs.scc(2)), 1);
310+
assert_eq!(sccs.annotation(sccs.scc(1)), 0);
311+
assert_eq!(sccs.annotation(sccs.scc(4)), 0);
312+
}
313+
314+
#[test]
315+
fn test_bug_max_leak_minimised() {
316+
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]);
317+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
318+
4 => MaxReached(1),
319+
_ => MaxReached(0),
320+
});
321+
322+
assert_eq!(sccs.annotation(sccs.scc(2)), 0);
323+
assert_eq!(sccs.annotation(sccs.scc(3)), 1);
324+
assert_eq!(sccs.annotation(sccs.scc(0)), 1);
325+
}
326+
327+
#[test]
328+
fn test_bug_max_leak() {
329+
let graph = TestGraph::new(
330+
8,
331+
&[
332+
(0, 0),
333+
(0, 18),
334+
(0, 19),
335+
(0, 1),
336+
(0, 2),
337+
(0, 7),
338+
(0, 8),
339+
(0, 23),
340+
(18, 0),
341+
(18, 12),
342+
(19, 0),
343+
(19, 25),
344+
(12, 18),
345+
(12, 3),
346+
(12, 5),
347+
(3, 12),
348+
(3, 21),
349+
(3, 22),
350+
(5, 13),
351+
(21, 3),
352+
(22, 3),
353+
(13, 5),
354+
(13, 4),
355+
(4, 13),
356+
(4, 0),
357+
(2, 11),
358+
(7, 6),
359+
(6, 20),
360+
(20, 6),
361+
(8, 17),
362+
(17, 9),
363+
(9, 16),
364+
(16, 26),
365+
(26, 15),
366+
(15, 10),
367+
(10, 14),
368+
(14, 27),
369+
(23, 24),
370+
],
371+
);
372+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
373+
22 => MaxReached(1),
374+
24 => MaxReached(2),
375+
27 => MaxReached(2),
376+
_ => MaxReached(0),
377+
});
378+
379+
assert_eq!(sccs.annotation(sccs.scc(2)), 0);
380+
assert_eq!(sccs.annotation(sccs.scc(7)), 0);
381+
assert_eq!(sccs.annotation(sccs.scc(8)), 2);
382+
assert_eq!(sccs.annotation(sccs.scc(23)), 2);
383+
assert_eq!(sccs.annotation(sccs.scc(3)), 2);
384+
assert_eq!(sccs.annotation(sccs.scc(0)), 2);
385+
}
386+
387+
#[test]
388+
fn test_bug_max_zero_stick_shape() {
389+
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]);
390+
391+
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
392+
4 => MaxReached(1),
393+
_ => MaxReached(0),
394+
});
395+
396+
assert_eq!(sccs.annotation(sccs.scc(0)), 1);
397+
assert_eq!(sccs.annotation(sccs.scc(1)), 1);
398+
assert_eq!(sccs.annotation(sccs.scc(2)), 1);
399+
assert_eq!(sccs.annotation(sccs.scc(3)), 1);
400+
assert_eq!(sccs.annotation(sccs.scc(4)), 1);
401+
}
402+
403+
#[test]
404+
fn test_min_max_in() {
405+
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]);
406+
let sccs: Sccs<usize, usize, MinMaxIn> =
407+
Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w });
408+
409+
assert_eq!(sccs.annotation(sccs.scc(2)).min, 2);
410+
assert_eq!(sccs.annotation(sccs.scc(2)).max, 2);
411+
assert_eq!(sccs.annotation(sccs.scc(0)).min, 0);
412+
assert_eq!(sccs.annotation(sccs.scc(0)).max, 4);
413+
assert_eq!(sccs.annotation(sccs.scc(3)).min, 0);
414+
assert_eq!(sccs.annotation(sccs.scc(3)).max, 4);
415+
assert_eq!(sccs.annotation(sccs.scc(5)).min, 5);
416+
}

0 commit comments

Comments
 (0)
This repository has been archived.