1
1
//! Traits used to represent [lattices] for use as the domain of a dataflow analysis.
2
2
//!
3
- //! ## Implementation Notes
3
+ //! # Overview
4
4
//!
5
- //! Given that they represent partially ordered sets, you may be surprised that [`MeetSemiLattice`]
6
- //! and [`JoinSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This
7
- //! is because most standard library types use lexicographic ordering instead of [set inclusion]
8
- //! for their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
9
- //! dataflow analysis, there's no need for a hypothetical `SetInclusion` newtype with a custom
10
- //! `PartialOrd` impl. The only benefit would be the ability to check (in debug mode) that the
11
- //! least upper (or greatest lower) bound returned by the lattice join (or meet) operator was in
12
- //! fact greater (or lower) than the inputs.
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.
13
35
//!
14
36
//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
15
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
16
40
17
41
use rustc_index:: bit_set:: BitSet ;
18
42
use rustc_index:: vec:: { Idx , IndexVec } ;
@@ -47,7 +71,13 @@ pub trait MeetSemiLattice: Eq {
47
71
fn meet ( & mut self , other : & Self ) -> bool ;
48
72
}
49
73
50
- /// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom.
74
+ /// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
75
+ ///
76
+ /// ```text
77
+ /// true
78
+ /// |
79
+ /// false
80
+ /// ```
51
81
impl JoinSemiLattice for bool {
52
82
fn join ( & mut self , other : & Self ) -> bool {
53
83
if let ( false , true ) = ( * self , * other) {
@@ -70,8 +100,11 @@ impl MeetSemiLattice for bool {
70
100
}
71
101
}
72
102
73
- /// A tuple or list of lattices is itself a lattice whose least upper bound is the concatenation of
74
- /// the least upper bounds of each element of the tuple or list.
103
+ /// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
104
+ /// of the least upper bounds of each element of the tuple (or list).
105
+ ///
106
+ /// In other words:
107
+ /// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ)
75
108
impl < I : Idx , T : JoinSemiLattice > JoinSemiLattice for IndexVec < I , T > {
76
109
fn join ( & mut self , other : & Self ) -> bool {
77
110
assert_eq ! ( self . len( ) , other. len( ) ) ;
@@ -96,9 +129,9 @@ impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> {
96
129
}
97
130
}
98
131
99
- /// A `BitSet` is an efficent way to store a tuple of "two-point" lattices. Equivalently, it is the
100
- /// lattice corresponding to the powerset of the set of all possibe values of the index type `T`
101
- /// ordered by inclusion .
132
+ /// A `BitSet` represents the lattice formed by the powerset of all possible values of
133
+ /// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
134
+ /// one for each possible value of `T` .
102
135
impl < T : Idx > JoinSemiLattice for BitSet < T > {
103
136
fn join ( & mut self , other : & Self ) -> bool {
104
137
self . union ( other)
@@ -146,8 +179,7 @@ impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
146
179
}
147
180
148
181
/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no
149
- /// value of `T` is comparable with any other. A flat set has the following [Hasse
150
- /// diagram](https://en.wikipedia.org/wiki/Hasse_diagram):
182
+ /// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]:
151
183
///
152
184
/// ```text
153
185
/// top
@@ -156,6 +188,8 @@ impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
156
188
/// \ \ / /
157
189
/// bottom
158
190
/// ```
191
+ ///
192
+ /// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
159
193
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
160
194
pub enum FlatSet < T > {
161
195
Bottom ,
0 commit comments