1 //! Traits used to represent [lattices] for use as the domain of a dataflow analysis. 2 //! 3 //! # Overview 4 //! 5 //! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse 6 //! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that 7 //! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are 8 //! *incomparable*, not equal. 9 //! 10 //! ```text 11 //! {X, Y} <- top 12 //! / \ 13 //! {X} {Y} 14 //! \ / 15 //! {} <- bottom 16 //! 17 //! ``` 18 //! 19 //! The defining characteristic of a lattice—the one that differentiates it from a [partially 20 //! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for 21 //! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the 22 //! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator 23 //! but not the other are known as semilattices. Dataflow analysis only uses the join operator and 24 //! will work with any join-semilattice, but both should be specified when possible. 25 //! 26 //! ## `PartialOrd` 27 //! 28 //! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`] 29 //! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This 30 //! is because most standard library types use lexicographic ordering instead of set inclusion for 31 //! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a 32 //! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The 33 //! only benefit would be the ability to check that the least upper (or greatest lower) bound 34 //! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs. 35 //! 36 //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) 37 //! [set inclusion]: https://en.wikipedia.org/wiki/Subset 38 //! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram 39 //! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set 40 41 use rustc_index::bit_set::BitSet; 42 use rustc_index::vec::{Idx, IndexVec}; 43 use std::iter; 44 45 /// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements 46 /// in the set. 47 /// 48 /// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum 49 /// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set 50 pub trait JoinSemiLattice: Eq { 51 /// Computes the least upper bound of two elements, storing the result in `self` and returning 52 /// `true` if `self` has changed. 53 /// 54 /// The lattice join operator is abbreviated as `∨`. join(&mut self, other: &Self) -> bool55 fn join(&mut self, other: &Self) -> bool; 56 } 57 58 /// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of 59 /// elements in the set. 60 /// 61 /// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not 62 /// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both 63 /// so that they can be used with [`Dual`]. 64 /// 65 /// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum 66 /// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set 67 pub trait MeetSemiLattice: Eq { 68 /// Computes the greatest lower bound of two elements, storing the result in `self` and 69 /// returning `true` if `self` has changed. 70 /// 71 /// The lattice meet operator is abbreviated as `∧`. meet(&mut self, other: &Self) -> bool72 fn meet(&mut self, other: &Self) -> bool; 73 } 74 75 /// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom: 76 /// 77 /// ```text 78 /// true 79 /// | 80 /// false 81 /// ``` 82 impl JoinSemiLattice for bool { join(&mut self, other: &Self) -> bool83 fn join(&mut self, other: &Self) -> bool { 84 if let (false, true) = (*self, *other) { 85 *self = true; 86 return true; 87 } 88 89 false 90 } 91 } 92 93 impl MeetSemiLattice for bool { meet(&mut self, other: &Self) -> bool94 fn meet(&mut self, other: &Self) -> bool { 95 if let (true, false) = (*self, *other) { 96 *self = false; 97 return true; 98 } 99 100 false 101 } 102 } 103 104 /// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation 105 /// of the least upper bounds of each element of the tuple (or list). 106 /// 107 /// In other words: 108 /// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ) 109 impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> { join(&mut self, other: &Self) -> bool110 fn join(&mut self, other: &Self) -> bool { 111 assert_eq!(self.len(), other.len()); 112 113 let mut changed = false; 114 for (a, b) in iter::zip(self, other) { 115 changed |= a.join(b); 116 } 117 changed 118 } 119 } 120 121 impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> { meet(&mut self, other: &Self) -> bool122 fn meet(&mut self, other: &Self) -> bool { 123 assert_eq!(self.len(), other.len()); 124 125 let mut changed = false; 126 for (a, b) in iter::zip(self, other) { 127 changed |= a.meet(b); 128 } 129 changed 130 } 131 } 132 133 /// A `BitSet` represents the lattice formed by the powerset of all possible values of 134 /// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, 135 /// one for each possible value of `T`. 136 impl<T: Idx> JoinSemiLattice for BitSet<T> { join(&mut self, other: &Self) -> bool137 fn join(&mut self, other: &Self) -> bool { 138 self.union(other) 139 } 140 } 141 142 impl<T: Idx> MeetSemiLattice for BitSet<T> { meet(&mut self, other: &Self) -> bool143 fn meet(&mut self, other: &Self) -> bool { 144 self.intersect(other) 145 } 146 } 147 148 /// The counterpart of a given semilattice `T` using the [inverse order]. 149 /// 150 /// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a 151 /// powerset has the empty set as its top element and the full set as its bottom element and uses 152 /// set *intersection* as its join operator. 153 /// 154 /// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory) 155 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 156 pub struct Dual<T>(pub T); 157 158 impl<T> std::borrow::Borrow<T> for Dual<T> { borrow(&self) -> &T159 fn borrow(&self) -> &T { 160 &self.0 161 } 162 } 163 164 impl<T> std::borrow::BorrowMut<T> for Dual<T> { borrow_mut(&mut self) -> &mut T165 fn borrow_mut(&mut self) -> &mut T { 166 &mut self.0 167 } 168 } 169 170 impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> { join(&mut self, other: &Self) -> bool171 fn join(&mut self, other: &Self) -> bool { 172 self.0.meet(&other.0) 173 } 174 } 175 176 impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> { meet(&mut self, other: &Self) -> bool177 fn meet(&mut self, other: &Self) -> bool { 178 self.0.join(&other.0) 179 } 180 } 181 182 /// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no 183 /// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]: 184 /// 185 /// ```text 186 /// top 187 /// / / \ \ 188 /// all possible values of `T` 189 /// \ \ / / 190 /// bottom 191 /// ``` 192 /// 193 /// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram 194 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 195 pub enum FlatSet<T> { 196 Bottom, 197 Elem(T), 198 Top, 199 } 200 201 impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> { join(&mut self, other: &Self) -> bool202 fn join(&mut self, other: &Self) -> bool { 203 let result = match (&*self, other) { 204 (Self::Top, _) | (_, Self::Bottom) => return false, 205 (Self::Elem(a), Self::Elem(b)) if a == b => return false, 206 207 (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()), 208 209 _ => Self::Top, 210 }; 211 212 *self = result; 213 true 214 } 215 } 216 217 impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> { meet(&mut self, other: &Self) -> bool218 fn meet(&mut self, other: &Self) -> bool { 219 let result = match (&*self, other) { 220 (Self::Bottom, _) | (_, Self::Top) => return false, 221 (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false, 222 223 (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()), 224 225 _ => Self::Bottom, 226 }; 227 228 *self = result; 229 true 230 } 231 } 232