use std::fmt::Debug;
use crate::bitset::BitSet;
pub trait MeetSemiLattice: Clone + Debug + PartialEq {
fn meet_into(&mut self, other: &Self) {
*self = self.meet(other);
}
#[must_use]
fn meet(&self, other: &Self) -> Self;
fn is_bottom(&self) -> bool;
}
pub trait JoinSemiLattice: Clone + Debug + PartialEq {
#[must_use]
fn join(&self, other: &Self) -> Self;
fn is_top(&self) -> bool;
}
pub trait Lattice: MeetSemiLattice + JoinSemiLattice {
fn top() -> Self;
fn bottom() -> Self;
}
impl MeetSemiLattice for BitSet {
fn meet(&self, other: &Self) -> Self {
let mut result = self.clone();
result.union_with(other);
result
}
fn meet_into(&mut self, other: &Self) {
self.union_with(other);
}
fn is_bottom(&self) -> bool {
self.is_full()
}
}
impl JoinSemiLattice for BitSet {
fn join(&self, other: &Self) -> Self {
let mut result = self.clone();
result.intersect_with(other);
result
}
fn is_top(&self) -> bool {
self.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, PartialEq)]
enum TwoPoint {
Top,
Bottom,
}
impl MeetSemiLattice for TwoPoint {
fn meet(&self, other: &Self) -> Self {
if *self == Self::Bottom || *other == Self::Bottom {
Self::Bottom
} else {
Self::Top
}
}
fn is_bottom(&self) -> bool {
*self == Self::Bottom
}
}
impl JoinSemiLattice for TwoPoint {
fn join(&self, other: &Self) -> Self {
if *self == Self::Top || *other == Self::Top {
Self::Top
} else {
Self::Bottom
}
}
fn is_top(&self) -> bool {
*self == Self::Top
}
}
impl Lattice for TwoPoint {
fn top() -> Self {
Self::Top
}
fn bottom() -> Self {
Self::Bottom
}
}
#[test]
fn meet_into_agrees_with_meet_for_every_lattice() {
fn check<L: MeetSemiLattice>(a: &L, b: &L) {
let expected = a.meet(b);
let mut actual = a.clone();
actual.meet_into(b);
assert_eq!(actual, expected, "meet_into must equal meet");
}
let mut a = BitSet::new(128);
a.insert(1);
a.insert(70);
let mut b = BitSet::new(128);
b.insert(70);
b.insert(100);
check(&a, &b);
check(&b, &a);
check(&a, &a.clone());
check(&BitSet::new(128), &a);
check(&a, &BitSet::new(128));
check(&a, &BitSet::lazy(128));
check(&BitSet::lazy(128), &a);
check(&TwoPoint::Top, &TwoPoint::Bottom);
check(&TwoPoint::Bottom, &TwoPoint::Top);
check(&TwoPoint::Top, &TwoPoint::Top);
}
#[test]
fn lattice_traits_are_re_exported_from_dataflow() {
use crate::analysis::dataflow::{JoinSemiLattice, Lattice, MeetSemiLattice};
fn assert_meet<T: MeetSemiLattice>() {}
fn assert_join<T: JoinSemiLattice>() {}
fn assert_lattice<T: Lattice>() {}
assert_meet::<BitSet>();
assert_join::<BitSet>();
assert_lattice::<TwoPoint>();
let (top, bottom) = (TwoPoint::top(), TwoPoint::bottom());
assert_eq!(top.meet(&top.join(&bottom)), top);
assert_eq!(top.join(&top.meet(&bottom)), top);
}
#[test]
fn test_bitset_meet_union() {
let mut a = BitSet::new(10);
a.insert(1);
a.insert(3);
let mut b = BitSet::new(10);
b.insert(2);
b.insert(3);
let result = a.meet(&b);
assert!(result.contains(1));
assert!(result.contains(2));
assert!(result.contains(3));
assert!(!result.contains(0));
assert!(!result.contains(4));
}
#[test]
fn test_bitset_meet_idempotent() {
let mut a = BitSet::new(10);
a.insert(1);
a.insert(5);
let result = a.meet(&a);
assert_eq!(a, result);
}
#[test]
fn test_bitset_meet_commutative() {
let mut a = BitSet::new(10);
a.insert(1);
a.insert(3);
let mut b = BitSet::new(10);
b.insert(2);
b.insert(4);
assert_eq!(a.meet(&b), b.meet(&a));
}
#[test]
fn test_bitset_meet_associative() {
let mut a = BitSet::new(10);
a.insert(1);
let mut b = BitSet::new(10);
b.insert(2);
let mut c = BitSet::new(10);
c.insert(3);
let left = a.meet(&b.meet(&c));
let right = a.meet(&b).meet(&c);
assert_eq!(left, right);
}
#[test]
fn test_bitset_join_intersection() {
let mut a = BitSet::new(10);
a.insert(1);
a.insert(2);
a.insert(3);
let mut b = BitSet::new(10);
b.insert(2);
b.insert(3);
b.insert(4);
let result = a.join(&b);
assert!(!result.contains(1));
assert!(result.contains(2));
assert!(result.contains(3));
assert!(!result.contains(4));
}
#[test]
fn test_bitset_join_idempotent() {
let mut a = BitSet::new(10);
a.insert(1);
a.insert(5);
let result = a.join(&a);
assert_eq!(a, result);
}
#[test]
fn test_bitset_join_commutative() {
let mut a = BitSet::new(10);
a.insert(1);
a.insert(3);
let mut b = BitSet::new(10);
b.insert(2);
b.insert(3);
assert_eq!(a.join(&b), b.join(&a));
}
#[test]
fn test_bitset_is_top_empty() {
let empty = BitSet::new(10);
assert!(empty.is_top());
let mut non_empty = BitSet::new(10);
non_empty.insert(0);
assert!(!non_empty.is_top());
}
#[test]
fn test_bitset_is_bottom_full() {
let full = BitSet::full(10);
assert!(full.is_bottom());
let mut partial = BitSet::new(10);
partial.insert(0);
assert!(!partial.is_bottom());
}
#[test]
fn test_bitset_meet_with_empty() {
let empty = BitSet::new(10);
let mut a = BitSet::new(10);
a.insert(1);
a.insert(2);
let result = a.meet(&empty);
assert!(result.contains(1));
assert!(result.contains(2));
assert_eq!(result.count(), 2);
}
#[test]
fn test_bitset_join_with_empty() {
let empty = BitSet::new(10);
let mut a = BitSet::new(10);
a.insert(1);
a.insert(2);
let result = a.join(&empty);
assert!(result.is_empty());
}
}