Skip to main content

oxilean_kernel/hash_cons/
key.rs

1//! `ExprKey` — a hashable, owning representation of an `Expr` node.
2//!
3//! `Expr` is already `Hash + Eq` (derived), so `ExprKey` is simply a
4//! newtype wrapper that makes the intent explicit: it identifies a node's
5//! *structural content*, not its arena slot.
6//!
7//! Using a newtype keeps the cache `HashMap` key type distinct from `Expr`
8//! so callers cannot accidentally conflate "a key for lookup" with "a live
9//! expression value".
10
11use crate::Node;
12use crate::{BinderInfo, Expr, FVarId, Level, Literal, Name};
13use std::hash::{Hash, Hasher};
14use std::rc::Rc;
15
16/// Structural key derived from the content of one `Expr` node.
17///
18/// Two `ExprKey` values are equal if and only if the corresponding `Expr`
19/// values are structurally identical (same variant, same fields). This is
20/// precisely the condition under which hash-consing must return the same
21/// `Idx<Expr>`.
22///
23/// Internally the key clones the `Expr` so that the `HashMap<ExprKey, …>`
24/// owns its keys independently of any arena.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct ExprKey(pub Expr);
27
28impl Hash for ExprKey {
29    fn hash<H: Hasher>(&self, state: &mut H) {
30        // Delegate to `Expr`'s derived `Hash` implementation which performs
31        // a full structural walk.
32        self.0.hash(state);
33    }
34}
35
36impl ExprKey {
37    /// Construct an `ExprKey` from an owned `Expr`.
38    #[inline]
39    pub fn new(expr: Expr) -> Self {
40        Self(expr)
41    }
42
43    /// Construct an `ExprKey` from a reference, cloning the expression.
44    #[inline]
45    pub fn from_ref(expr: &Expr) -> Self {
46        Self(expr.clone())
47    }
48
49    /// Return the inner `Expr`.
50    #[inline]
51    pub fn into_inner(self) -> Expr {
52        self.0
53    }
54
55    /// Borrow the inner `Expr`.
56    #[inline]
57    pub fn inner(&self) -> &Expr {
58        &self.0
59    }
60}
61
62// ---------------------------------------------------------------------------
63// Constructors that build ExprKey values directly from field values.
64// These mirror the `mk_*` constructors on `HashConsArena` so the cache layer
65// can construct a lookup key without first allocating an `Expr`.
66// ---------------------------------------------------------------------------
67
68impl ExprKey {
69    /// Key for `Expr::Sort(level)`.
70    pub fn sort(level: Level) -> Self {
71        Self::new(Expr::Sort(level))
72    }
73
74    /// Key for `Expr::BVar(idx)`.
75    pub fn bvar(idx: u32) -> Self {
76        Self::new(Expr::BVar(idx))
77    }
78
79    /// Key for `Expr::FVar(id)`.
80    pub fn fvar(id: FVarId) -> Self {
81        Self::new(Expr::FVar(id))
82    }
83
84    /// Key for `Expr::Const(name, levels)`.
85    pub fn const_(name: Name, levels: Vec<Level>) -> Self {
86        Self::new(Expr::Const(name, levels))
87    }
88
89    /// Key for `Expr::App(f, a)`.
90    pub fn app(f: Expr, a: Expr) -> Self {
91        Self::new(Expr::App(Node::new(f), Node::new(a)))
92    }
93
94    /// Key for `Expr::Lam(bi, name, dom, body)`.
95    pub fn lam(bi: BinderInfo, name: Name, dom: Expr, body: Expr) -> Self {
96        Self::new(Expr::Lam(bi, name, Node::new(dom), Node::new(body)))
97    }
98
99    /// Key for `Expr::Pi(bi, name, dom, cod)`.
100    pub fn pi(bi: BinderInfo, name: Name, dom: Expr, cod: Expr) -> Self {
101        Self::new(Expr::Pi(bi, name, Node::new(dom), Node::new(cod)))
102    }
103
104    /// Key for `Expr::Let(name, ty, val, body)`.
105    pub fn let_(name: Name, ty: Expr, val: Expr, body: Expr) -> Self {
106        Self::new(Expr::Let(
107            name,
108            Node::new(ty),
109            Node::new(val),
110            Node::new(body),
111        ))
112    }
113
114    /// Key for `Expr::Lit(lit)`.
115    pub fn lit(lit: Literal) -> Self {
116        Self::new(Expr::Lit(lit))
117    }
118
119    /// Key for `Expr::Proj(name, idx, struct_expr)`.
120    pub fn proj(name: Name, idx: u32, struct_expr: Expr) -> Self {
121        Self::new(Expr::Proj(name, idx, Node::new(struct_expr)))
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_expr_key_eq_same_bvar() {
131        let k1 = ExprKey::bvar(0);
132        let k2 = ExprKey::bvar(0);
133        assert_eq!(k1, k2);
134    }
135
136    #[test]
137    fn test_expr_key_ne_different_bvar() {
138        let k1 = ExprKey::bvar(0);
139        let k2 = ExprKey::bvar(1);
140        assert_ne!(k1, k2);
141    }
142
143    #[test]
144    fn test_expr_key_sort_zero_equals() {
145        let k1 = ExprKey::sort(Level::zero());
146        let k2 = ExprKey::sort(Level::zero());
147        assert_eq!(k1, k2);
148    }
149
150    #[test]
151    fn test_expr_key_fvar_equals() {
152        let id = FVarId::new(42);
153        let k1 = ExprKey::fvar(id);
154        let k2 = ExprKey::fvar(id);
155        assert_eq!(k1, k2);
156    }
157
158    #[test]
159    fn test_expr_key_hash_consistent() {
160        use std::collections::hash_map::DefaultHasher;
161        let k1 = ExprKey::bvar(5);
162        let k2 = ExprKey::bvar(5);
163        let mut h1 = DefaultHasher::new();
164        let mut h2 = DefaultHasher::new();
165        k1.hash(&mut h1);
166        k2.hash(&mut h2);
167        assert_eq!(h1.finish(), h2.finish());
168    }
169
170    #[test]
171    fn test_expr_key_into_inner() {
172        let k = ExprKey::bvar(3);
173        let expr = k.into_inner();
174        assert_eq!(expr, Expr::BVar(3));
175    }
176
177    #[test]
178    fn test_expr_key_from_ref() {
179        let expr = Expr::BVar(7);
180        let k1 = ExprKey::from_ref(&expr);
181        let k2 = ExprKey::bvar(7);
182        assert_eq!(k1, k2);
183    }
184
185    #[test]
186    fn test_expr_key_const_equals() {
187        let name = Name::str("Nat");
188        let k1 = ExprKey::const_(name.clone(), vec![]);
189        let k2 = ExprKey::const_(name, vec![]);
190        assert_eq!(k1, k2);
191    }
192
193    #[test]
194    fn test_expr_key_app_equals() {
195        let f = Expr::BVar(0);
196        let a = Expr::BVar(1);
197        let k1 = ExprKey::app(f.clone(), a.clone());
198        let k2 = ExprKey::app(f, a);
199        assert_eq!(k1, k2);
200    }
201
202    #[test]
203    fn test_expr_key_lam_equals() {
204        let name = Name::str("x");
205        let dom = Expr::Sort(Level::zero());
206        let body = Expr::BVar(0);
207        let k1 = ExprKey::lam(BinderInfo::Default, name.clone(), dom.clone(), body.clone());
208        let k2 = ExprKey::lam(BinderInfo::Default, name, dom, body);
209        assert_eq!(k1, k2);
210    }
211
212    #[test]
213    fn test_expr_key_pi_equals() {
214        let name = Name::str("A");
215        let dom = Expr::Sort(Level::zero());
216        let cod = Expr::BVar(0);
217        let k1 = ExprKey::pi(BinderInfo::Default, name.clone(), dom.clone(), cod.clone());
218        let k2 = ExprKey::pi(BinderInfo::Default, name, dom, cod);
219        assert_eq!(k1, k2);
220    }
221
222    #[test]
223    fn test_expr_key_let_equals() {
224        let name = Name::str("n");
225        let ty = Expr::Sort(Level::zero());
226        let val = Expr::BVar(0);
227        let body = Expr::BVar(1);
228        let k1 = ExprKey::let_(name.clone(), ty.clone(), val.clone(), body.clone());
229        let k2 = ExprKey::let_(name, ty, val, body);
230        assert_eq!(k1, k2);
231    }
232
233    #[test]
234    fn test_expr_key_lit_nat_equals() {
235        let k1 = ExprKey::lit(Literal::nat(42));
236        let k2 = ExprKey::lit(Literal::nat(42));
237        assert_eq!(k1, k2);
238    }
239
240    #[test]
241    fn test_expr_key_proj_equals() {
242        let name = Name::str("fst");
243        let se = Expr::BVar(0);
244        let k1 = ExprKey::proj(name.clone(), 0, se.clone());
245        let k2 = ExprKey::proj(name, 0, se);
246        assert_eq!(k1, k2);
247    }
248
249    #[test]
250    fn test_expr_key_usable_as_hashmap_key() {
251        use std::collections::HashMap;
252        let mut map: HashMap<ExprKey, u32> = HashMap::new();
253        map.insert(ExprKey::bvar(0), 100);
254        map.insert(ExprKey::bvar(1), 200);
255        assert_eq!(map.get(&ExprKey::bvar(0)), Some(&100));
256        assert_eq!(map.get(&ExprKey::bvar(1)), Some(&200));
257        assert_eq!(map.get(&ExprKey::bvar(2)), None);
258    }
259}