mirror of
https://github.com/rust-lang/rust.git
synced 2025-11-24 16:48:15 +00:00
420 lines
11 KiB
Rust
420 lines
11 KiB
Rust
use super::*;
|
|
|
|
impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
|
|
/// A "best" parent in some sense. See `parents` and
|
|
/// `postdom_upper_bound` for more details.
|
|
fn postdom_parent(&self, a: T) -> Option<T> {
|
|
self.mutual_immediate_postdominator(self.parents(a))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_one_step() {
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "b");
|
|
relation.add("a", "c");
|
|
let relation = relation.freeze();
|
|
assert!(relation.contains("a", "c"));
|
|
assert!(relation.contains("a", "b"));
|
|
assert!(!relation.contains("b", "a"));
|
|
assert!(!relation.contains("a", "d"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_many_steps() {
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "b");
|
|
relation.add("a", "c");
|
|
relation.add("a", "f");
|
|
|
|
relation.add("b", "c");
|
|
relation.add("b", "d");
|
|
relation.add("b", "e");
|
|
|
|
relation.add("e", "g");
|
|
let relation = relation.freeze();
|
|
|
|
assert!(relation.contains("a", "b"));
|
|
assert!(relation.contains("a", "c"));
|
|
assert!(relation.contains("a", "d"));
|
|
assert!(relation.contains("a", "e"));
|
|
assert!(relation.contains("a", "f"));
|
|
assert!(relation.contains("a", "g"));
|
|
|
|
assert!(relation.contains("b", "g"));
|
|
|
|
assert!(!relation.contains("a", "x"));
|
|
assert!(!relation.contains("b", "f"));
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_triangle() {
|
|
// a -> tcx
|
|
// ^
|
|
// |
|
|
// b
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "tcx");
|
|
relation.add("b", "tcx");
|
|
let relation = relation.freeze();
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["tcx"]);
|
|
assert_eq!(relation.parents("a"), vec!["tcx"]);
|
|
assert_eq!(relation.parents("b"), vec!["tcx"]);
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_best_choice1() {
|
|
// 0 -> 1 <- 3
|
|
// | ^ |
|
|
// | | |
|
|
// +--> 2 <--+
|
|
//
|
|
// mubs(0,3) = [1]
|
|
|
|
// This tests a particular state in the algorithm, in which we
|
|
// need the second pare down call to get the right result (after
|
|
// intersection, we have [1, 2], but 2 -> 1).
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("0", "1");
|
|
relation.add("0", "2");
|
|
|
|
relation.add("2", "1");
|
|
|
|
relation.add("3", "1");
|
|
relation.add("3", "2");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["2"]);
|
|
assert_eq!(relation.parents("0"), vec!["2"]);
|
|
assert_eq!(relation.parents("2"), vec!["1"]);
|
|
assert!(relation.parents("1").is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_best_choice2() {
|
|
// 0 -> 1 <- 3
|
|
// | | |
|
|
// | v |
|
|
// +--> 2 <--+
|
|
//
|
|
// mubs(0,3) = [2]
|
|
|
|
// Like the preceding test, but in this case intersection is [2,
|
|
// 1], and hence we rely on the first pare down call.
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("0", "1");
|
|
relation.add("0", "2");
|
|
|
|
relation.add("1", "2");
|
|
|
|
relation.add("3", "1");
|
|
relation.add("3", "2");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["1"]);
|
|
assert_eq!(relation.parents("0"), vec!["1"]);
|
|
assert_eq!(relation.parents("1"), vec!["2"]);
|
|
assert!(relation.parents("2").is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_no_best_choice() {
|
|
// in this case, the intersection yields [1, 2], and the "pare
|
|
// down" calls find nothing to remove.
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("0", "1");
|
|
relation.add("0", "2");
|
|
|
|
relation.add("3", "1");
|
|
relation.add("3", "2");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["1", "2"]);
|
|
assert_eq!(relation.parents("0"), vec!["1", "2"]);
|
|
assert_eq!(relation.parents("3"), vec!["1", "2"]);
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_best_choice_scc() {
|
|
// in this case, 1 and 2 form a cycle; we pick arbitrarily (but
|
|
// consistently).
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("0", "1");
|
|
relation.add("0", "2");
|
|
|
|
relation.add("1", "2");
|
|
relation.add("2", "1");
|
|
|
|
relation.add("3", "1");
|
|
relation.add("3", "2");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["1"]);
|
|
assert_eq!(relation.parents("0"), vec!["1"]);
|
|
}
|
|
|
|
#[test]
|
|
fn pdub_crisscross() {
|
|
// diagonal edges run left-to-right
|
|
// a -> a1 -> x
|
|
// \/ ^
|
|
// /\ |
|
|
// b -> b1 ---+
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "a1");
|
|
relation.add("a", "b1");
|
|
relation.add("b", "a1");
|
|
relation.add("b", "b1");
|
|
relation.add("a1", "x");
|
|
relation.add("b1", "x");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["a1", "b1"]);
|
|
assert_eq!(relation.postdom_upper_bound("a", "b"), Some("x"));
|
|
assert_eq!(relation.postdom_parent("a"), Some("x"));
|
|
assert_eq!(relation.postdom_parent("b"), Some("x"));
|
|
}
|
|
|
|
#[test]
|
|
fn pdub_crisscross_more() {
|
|
// diagonal edges run left-to-right
|
|
// a -> a1 -> a2 -> a3 -> x
|
|
// \/ \/ ^
|
|
// /\ /\ |
|
|
// b -> b1 -> b2 ---------+
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "a1");
|
|
relation.add("a", "b1");
|
|
relation.add("b", "a1");
|
|
relation.add("b", "b1");
|
|
|
|
relation.add("a1", "a2");
|
|
relation.add("a1", "b2");
|
|
relation.add("b1", "a2");
|
|
relation.add("b1", "b2");
|
|
|
|
relation.add("a2", "a3");
|
|
|
|
relation.add("a3", "x");
|
|
relation.add("b2", "x");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["a1", "b1"]);
|
|
assert_eq!(relation.minimal_upper_bounds("a1", "b1"), vec!["a2", "b2"]);
|
|
assert_eq!(relation.postdom_upper_bound("a", "b"), Some("x"));
|
|
|
|
assert_eq!(relation.postdom_parent("a"), Some("x"));
|
|
assert_eq!(relation.postdom_parent("b"), Some("x"));
|
|
}
|
|
|
|
#[test]
|
|
fn pdub_lub() {
|
|
// a -> a1 -> x
|
|
// ^
|
|
// |
|
|
// b -> b1 ---+
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "a1");
|
|
relation.add("b", "b1");
|
|
relation.add("a1", "x");
|
|
relation.add("b1", "x");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["x"]);
|
|
assert_eq!(relation.postdom_upper_bound("a", "b"), Some("x"));
|
|
|
|
assert_eq!(relation.postdom_parent("a"), Some("a1"));
|
|
assert_eq!(relation.postdom_parent("b"), Some("b1"));
|
|
assert_eq!(relation.postdom_parent("a1"), Some("x"));
|
|
assert_eq!(relation.postdom_parent("b1"), Some("x"));
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_intermediate_node_on_one_side_only() {
|
|
// a -> c -> d
|
|
// ^
|
|
// |
|
|
// b
|
|
|
|
// "digraph { a -> c -> d; b -> d; }",
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "c");
|
|
relation.add("c", "d");
|
|
relation.add("b", "d");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["d"]);
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_scc_1() {
|
|
// +-------------+
|
|
// | +----+ |
|
|
// | v | |
|
|
// a -> c -> d <-+
|
|
// ^
|
|
// |
|
|
// b
|
|
|
|
// "digraph { a -> c -> d; d -> c; a -> d; b -> d; }",
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "c");
|
|
relation.add("c", "d");
|
|
relation.add("d", "c");
|
|
relation.add("a", "d");
|
|
relation.add("b", "d");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]);
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_scc_2() {
|
|
// +----+
|
|
// v |
|
|
// a -> c -> d
|
|
// ^ ^
|
|
// | |
|
|
// +--- b
|
|
|
|
// "digraph { a -> c -> d; d -> c; b -> d; b -> c; }",
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "c");
|
|
relation.add("c", "d");
|
|
relation.add("d", "c");
|
|
relation.add("b", "d");
|
|
relation.add("b", "c");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]);
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_scc_3() {
|
|
// +---------+
|
|
// v |
|
|
// a -> c -> d -> e
|
|
// ^ ^
|
|
// | |
|
|
// b ---+
|
|
|
|
// "digraph { a -> c -> d -> e -> c; b -> d; b -> e; }",
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "c");
|
|
relation.add("c", "d");
|
|
relation.add("d", "e");
|
|
relation.add("e", "c");
|
|
relation.add("b", "d");
|
|
relation.add("b", "e");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]);
|
|
}
|
|
|
|
#[test]
|
|
fn mubs_scc_4() {
|
|
// +---------+
|
|
// v |
|
|
// a -> c -> d -> e
|
|
// | ^ ^
|
|
// +---------+ |
|
|
// |
|
|
// b ---+
|
|
|
|
// "digraph { a -> c -> d -> e -> c; a -> d; b -> e; }"
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "c");
|
|
relation.add("c", "d");
|
|
relation.add("d", "e");
|
|
relation.add("e", "c");
|
|
relation.add("a", "d");
|
|
relation.add("b", "e");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]);
|
|
}
|
|
|
|
#[test]
|
|
fn parent() {
|
|
// An example that was misbehaving in the compiler.
|
|
//
|
|
// 4 -> 1 -> 3
|
|
// \ | /
|
|
// \ v /
|
|
// 2 -> 0
|
|
//
|
|
// plus a bunch of self-loops
|
|
//
|
|
// Here `->` represents `<=` and `0` is `'static`.
|
|
|
|
let pairs = vec![
|
|
(2, /*->*/ 0),
|
|
(2, /*->*/ 2),
|
|
(0, /*->*/ 0),
|
|
(0, /*->*/ 0),
|
|
(1, /*->*/ 0),
|
|
(1, /*->*/ 1),
|
|
(3, /*->*/ 0),
|
|
(3, /*->*/ 3),
|
|
(4, /*->*/ 0),
|
|
(4, /*->*/ 1),
|
|
(1, /*->*/ 3),
|
|
];
|
|
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
for (a, b) in pairs {
|
|
relation.add(a, b);
|
|
}
|
|
let relation = relation.freeze();
|
|
|
|
let p = relation.postdom_parent(3);
|
|
assert_eq!(p, Some(0));
|
|
}
|
|
|
|
#[test]
|
|
fn minimal_scc_representative_1() {
|
|
// +---------+
|
|
// v |
|
|
// a -> c -> d -> e
|
|
// ^ ^
|
|
// | |
|
|
// b ---+
|
|
|
|
// "digraph { a -> c -> d -> e -> c; b -> d; b -> e; }",
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "c");
|
|
relation.add("c", "d");
|
|
relation.add("d", "e");
|
|
relation.add("e", "c");
|
|
relation.add("b", "d");
|
|
relation.add("b", "e");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_scc_representative("a"), "a");
|
|
assert_eq!(relation.minimal_scc_representative("b"), "b");
|
|
assert_eq!(relation.minimal_scc_representative("c"), "c");
|
|
assert_eq!(relation.minimal_scc_representative("d"), "c");
|
|
assert_eq!(relation.minimal_scc_representative("e"), "c");
|
|
}
|
|
|
|
#[test]
|
|
fn minimal_scc_representative_2() {
|
|
// "digraph { a -> b; a -> a; b -> a; c -> c}",
|
|
let mut relation = TransitiveRelationBuilder::default();
|
|
relation.add("a", "b");
|
|
relation.add("b", "a");
|
|
relation.add("a", "a");
|
|
relation.add("c", "c");
|
|
let relation = relation.freeze();
|
|
|
|
assert_eq!(relation.minimal_scc_representative("a"), "a");
|
|
assert_eq!(relation.minimal_scc_representative("b"), "a");
|
|
assert_eq!(relation.minimal_scc_representative("c"), "c");
|
|
}
|