Skip to main content

logicaffeine_kernel/dag/sem/
eval.rs

1//! Evaluation: terms to values, application, and the values of constants.
2//!
3//! Evaluation is call-by-value and performs β only. An application of a variable, a constant or
4//! a stuck projection becomes a neutral; δ and ι are left to weak-head reduction. So evaluating a
5//! term costs time proportional to the part of it outside every binder — a lambda is a closure
6//! whose body is not looked at until the closure is applied.
7//!
8//! An expression is evaluated in its environment *pruned* to the entries it can see (the others
9//! are replaced by a placeholder), so that two environments agreeing on those entries give the
10//! same key for the evaluation cache and the same closure, and a closure is open exactly when
11//! something it can see is.
12//!
13//! A universe-polymorphic constant's type, body and recursor rules are evaluated from its own
14//! terms under the substitution of one use's levels for its parameters, which the environment
15//! carries: sorts and constants take their levels through it as evaluation reaches them, and
16//! nothing is instantiated in advance. A term that mentions no universe parameter is read under
17//! the identity, so its value is shared by every instantiation.
18
19use super::value::{EnvId, Head, Kind, Val, V};
20use super::{reject, Sem};
21use crate::dag::env::Kind as CKind;
22use crate::dag::error::KResult;
23use crate::dag::expr::{ExprId, Node};
24use crate::dag::level::{LevelId, LevelsId, SubstId};
25use crate::dag::name::NameId;
26
27impl Sem<'_> {
28    /// `env` pruned for `e`: only the entries `e` can observe (its loose variables). A closed
29    /// term is read under its environment's substitution only if it mentions a universe
30    /// parameter, so its value is shared by every instantiation; an open term keeps the
31    /// substitution (re-rooting its entries under the identity costs more than it shares).
32    #[inline]
33    pub(crate) fn env_for(&mut self, env: EnvId, e: ExprId) -> EnvId {
34        let (n, mask, lparams) = self.k.s.scope_of(e);
35        let node = self.st.arena.env(env);
36        if n == 0 {
37            return self.st.arena.lsub_base(if lparams {
38                node.lsub
39            } else {
40                SubstId::IDENTITY
41            });
42        }
43        let lsub = node.lsub;
44        let Some(mask) = mask else {
45            return self.prune_wide(env, e, lsub);
46        };
47        if n == node.len && node.live == mask {
48            return env;
49        }
50        if let Some(r) = self.st.arena.last_prune(env, mask) {
51            return r;
52        }
53        self.prune(env, n, mask)
54    }
55
56    /// The pruning `env_for` could not answer from the environment itself.
57    #[cold]
58    #[inline(never)]
59    fn prune(&mut self, env: EnvId, n: u32, mask: u64) -> EnvId {
60        debug_assert!(n <= 64 && mask != 0 && 64 - mask.leading_zeros() == n);
61        let r = match self.st.prune_cache.get(&(env.0, mask)) {
62            Some(e) => EnvId(e),
63            None => {
64                let mut vals: smallvec::SmallVec<[V; 16]> = smallvec::SmallVec::new();
65                self.st.arena.select(env, mask, &mut vals);
66                let lsub = self.st.arena.lsub_of(env);
67                let r = self.st.arena.frame(n, mask, &vals, lsub);
68                self.st.prune_cache.insert((env.0, mask), r.0);
69                r
70            }
71        };
72        self.st.arena.note_prune(env, mask, r);
73        r
74    }
75
76    /// `env` pruned for an expression whose loose variables are not all below 64 (or whose mask
77    /// is not exact): by its exact set of loose variables, so that closures over deeply nested
78    /// terms still capture only what they can see and stay one value per meaning. An environment
79    /// the expression sees entirely — no entry dead, every index loose — is its own pruning, as
80    /// in the narrow case; copying it would cost its length at every level of a deep nest.
81    #[cold]
82    #[inline(never)]
83    fn prune_wide(&mut self, env: EnvId, e: ExprId, lsub: SubstId) -> EnvId {
84        let set = self.loose_set(e);
85        let node = self.st.arena.env(env);
86        if node.dense && set.len() == node.len as usize && node.lsub == lsub {
87            return env;
88        }
89        let key = (env.0, e.0);
90        if let Some(r) = self.st.wide_prune_cache.get(&key) {
91            return EnvId(r);
92        }
93        let Some(&last) = set.last() else {
94            return self.st.arena.lsub_base(lsub);
95        };
96        let mut vals: smallvec::SmallVec<[V; 16]> = smallvec::SmallVec::new();
97        self.st.arena.select_indices(env, &set, &mut vals);
98        let r = if last < 64 {
99            let mask = set.iter().fold(0u64, |m, &i| m | (1 << i));
100            self.st.arena.frame(last + 1, mask, &vals, lsub)
101        } else {
102            self.st.arena.wide_frame(last + 1, &set, &vals, lsub)
103        };
104        self.st.wide_prune_cache.insert(key, r.0);
105        r
106    }
107
108    /// The loose de Bruijn indices of `e`, exactly, ascending (for expressions the store's
109    /// 64-bit mask cannot describe). Computed once per persistent expression.
110    pub(crate) fn loose_set(&mut self, e: ExprId) -> std::rc::Rc<[u32]> {
111        if let (n, Some(mask)) = self.k.s.loose_bvars(e) {
112            debug_assert!(n <= 64);
113            let mut v = Vec::with_capacity(mask.count_ones() as usize);
114            let mut m = mask;
115            while m != 0 {
116                v.push(m.trailing_zeros());
117                m &= m - 1;
118            }
119            return v.into();
120        }
121        if let Some(r) = self.st.loose_sets.get(&e) {
122            return r.clone();
123        }
124        let r: std::rc::Rc<[u32]> = match self.k.s.node(e) {
125            Node::BVar(i) => vec![i].into(),
126            Node::App(f, a) => {
127                let (x, y) = (self.loose_set(f), self.loose_set(a));
128                union_shifted(&x, &y, 0)
129            }
130            Node::Lam(d, b) | Node::Pi(d, b) => {
131                let (x, y) = (self.loose_set(d), self.loose_set(b));
132                union_shifted(&x, &y, 1)
133            }
134            Node::Let(t, v, b) => {
135                let (x, y, z) = (self.loose_set(t), self.loose_set(v), self.loose_set(b));
136                union_shifted(&union_shifted(&x, &y, 0), &z, 1)
137            }
138            Node::Proj(_, _, x) => self.loose_set(x),
139            _ => Vec::new().into(),
140        };
141        if !e.is_scratch() {
142            self.st.loose_sets.insert(e, r.clone());
143        }
144        r
145    }
146
147    /// How a constant behaves as a head.
148    fn const_kind(&mut self, n: NameId, ls: LevelsId) -> KResult<Kind> {
149        let Some(c) = self.k.env.get(n) else {
150            return Err(reject(format!(
151                "unknown constant '{}'",
152                self.k.s.names.display(n)
153            )));
154        };
155        let kind = match &c.kind {
156            CKind::Defn { .. } | CKind::Thm { .. } => {
157                if self.k.s.levels.get_list(ls).len() == c.lparams.len() {
158                    Kind::Delta
159                } else {
160                    Kind::Opaque
161                }
162            }
163            CKind::Ctor(_) => Kind::Ctor,
164            CKind::Rec(_) => Kind::Rec,
165            CKind::Quot(_) => Kind::Quot,
166            CKind::Axiom { .. } | CKind::Opaque { .. } | CKind::Induct(_) => Kind::Opaque,
167        };
168        if !self.k.env.is_settled(n) {
169            self.st.scratch_seen = true;
170            self.st.counters.scratch_unsettled += 1;
171        }
172        Ok(kind)
173    }
174
175    /// The value of a constant (with no arguments).
176    pub(crate) fn const_value(&mut self, n: NameId, ls: LevelsId) -> KResult<V> {
177        let h = Head::Const {
178            name: n,
179            levels: ls,
180        };
181        if let Some(v) = self.st.arena.find_head(&h) {
182            return Ok(v);
183        }
184        let kind = self.const_kind(n, ls)?;
185        Ok(self.st.arena.head_value(kind, h))
186    }
187
188    pub(crate) fn const0(&mut self, n: NameId) -> KResult<V> {
189        self.const_value(n, LevelsId::EMPTY)
190    }
191
192    /// The environment constant `c`'s own terms are read under at the universe levels `ls`: the
193    /// base of the substitution of `ls` for its parameters. A constant that is only provisionally
194    /// declared may be withdrawn with its declaration, so the session must end with it.
195    pub(crate) fn instance_base(&mut self, c: NameId, ls: LevelsId) -> KResult<EnvId> {
196        let Some(k) = self.k.env.get(c) else {
197            return Err(reject(format!(
198                "unknown constant '{}'",
199                self.k.s.names.display(c)
200            )));
201        };
202        let ps = k.lparams.clone();
203        if !self.k.env.is_settled(c) {
204            self.st.scratch_seen = true;
205            self.st.counters.scratch_instance += 1;
206        }
207        let s = self.k.s.levels.substitution(&ps, ls);
208        Ok(self.st.arena.lsub_base(s))
209    }
210
211    /// The type of constant `n` at universe levels `ls`.
212    pub fn const_type(&mut self, n: NameId, ls: LevelsId) -> KResult<V> {
213        if let Some(v) = self.st.const_type.get(&(n.0, ls.0)) {
214            return Ok(V(v));
215        }
216        let base = self.instance_base(n, ls)?;
217        let ty = self.k.env.get(n).expect("a known constant").ty;
218        let v = self.eval(base, ty)?;
219        self.st.const_type.insert((n.0, ls.0), v.0);
220        Ok(v)
221    }
222
223    /// Level `l` of a term read in `env`.
224    #[inline]
225    pub(crate) fn level_in(&mut self, env: EnvId, l: LevelId) -> LevelId {
226        let s = self.st.arena.lsub_of(env);
227        if s == SubstId::IDENTITY {
228            l
229        } else {
230            self.k.s.levels.subst(l, s)
231        }
232    }
233
234    /// The universe levels `ls` of a term read in `env`.
235    #[inline]
236    pub(crate) fn levels_in(&mut self, env: EnvId, ls: LevelsId) -> LevelsId {
237        let s = self.st.arena.lsub_of(env);
238        if s == SubstId::IDENTITY {
239            ls
240        } else {
241            self.k.s.levels.subst_list(ls, s)
242        }
243    }
244
245    /// Evaluates `e` in `env`.
246    pub fn eval(&mut self, env: EnvId, e: ExprId) -> KResult<V> {
247        match self.k.s.node(e) {
248            Node::BVar(i) => self
249                .st
250                .arena
251                .lookup(env, i)
252                .ok_or_else(|| reject("evaluation met a loose bound variable")),
253            Node::Sort(l) => {
254                let s = self.atom_subst(env, e);
255                if let Some(v) = self.st.atom_memo(e, s) {
256                    return Ok(v);
257                }
258                let l = self.k.s.levels.subst(l, s);
259                let v = self.st.arena.sort(l);
260                self.st.note_atom(e, s, v);
261                Ok(v)
262            }
263            Node::Const(n, ls) => {
264                let s = self.atom_subst(env, e);
265                if let Some(v) = self.st.atom_memo(e, s) {
266                    return Ok(v);
267                }
268                let ls = self.k.s.levels.subst_list(ls, s);
269                let v = self.const_value(n, ls)?;
270                self.st.note_atom(e, s, v);
271                Ok(v)
272            }
273            Node::NatLit(n) => Ok(self.st.arena.nat(n)),
274            Node::StrLit(s) => Ok(self.st.arena.str(s)),
275            Node::FVar(_) => Err(reject("the value engine met a free variable")),
276            Node::Lam(dom, body) => {
277                self.note_expr(e);
278                let env = self.env_for(env, e);
279                Ok(self.st.arena.lam(dom, env, body))
280            }
281            _ => {
282                self.note_expr(e);
283                let env = self.env_for(env, e);
284                if let Some(v) = self.st.eval_cache.get(&(env.0, e.0)) {
285                    return Ok(V(v));
286                }
287                self.enter()?;
288                let r = self.eval_node(env, e);
289                self.leave();
290                let v = r?;
291                self.st.eval_cache.insert((env.0, e.0), v.0);
292                Ok(v)
293            }
294        }
295    }
296
297    /// The substitution a sort or constant `e` is read under in `env`: the identity unless it
298    /// mentions a universe parameter.
299    #[inline]
300    fn atom_subst(&self, env: EnvId, e: ExprId) -> SubstId {
301        let s = self.st.arena.lsub_of(env);
302        if s != SubstId::IDENTITY && self.k.s.has_lparam(e) {
303            s
304        } else {
305            SubstId::IDENTITY
306        }
307    }
308
309    /// A term of the current declaration's scratch region may not key anything that outlives it.
310    #[inline]
311    pub(crate) fn note_expr(&mut self, e: ExprId) {
312        if e.is_scratch() {
313            self.st.scratch_seen = true;
314            self.st.counters.scratch_expr += 1;
315        }
316    }
317
318    fn eval_node(&mut self, env: EnvId, e: ExprId) -> KResult<V> {
319        match self.k.s.node(e) {
320            Node::App(..) => {
321                let (f, args) = self.k.s.app_args(e);
322                let mut fv = self.eval(env, f)?;
323                let mut i = 0;
324                while i < args.len() {
325                    if let Val::Lam {
326                        env: cenv, body, ..
327                    } = self.st.arena.get(fv)
328                    {
329                        let a = self.eval(env, args[i])?;
330                        let mut benv = self.st.arena.extend(cenv, a);
331                        let mut b = body;
332                        i += 1;
333                        while i < args.len() {
334                            let Node::Lam(_, inner) = self.k.s.node(b) else {
335                                break;
336                            };
337                            let a = self.eval(env, args[i])?;
338                            benv = self.st.arena.extend(benv, a);
339                            b = inner;
340                            i += 1;
341                        }
342                        fv = self.eval(benv, b)?;
343                    } else {
344                        let a = self.eval(env, args[i])?;
345                        fv = self.apply(fv, a)?;
346                        i += 1;
347                    }
348                }
349                Ok(fv)
350            }
351            Node::Pi(dom, body) => {
352                let d = self.eval(env, dom)?;
353                Ok(self.st.arena.pi(d, env, body))
354            }
355            Node::Let(_, val, body) => {
356                let v = self.eval(env, val)?;
357                let env2 = self.st.arena.extend(env, v);
358                self.eval(env2, body)
359            }
360            Node::Proj(s, idx, x) => {
361                let xv = self.eval(env, x)?;
362                self.proj(s, idx, xv)
363            }
364            _ => unreachable!("atoms are evaluated directly"),
365        }
366    }
367
368    /// `f a`.
369    pub fn apply(&mut self, f: V, a: V) -> KResult<V> {
370        match self.st.arena.get(f) {
371            Val::Lam { env, body, .. } => {
372                let env2 = self.st.arena.extend(env, a);
373                self.eval(env2, body)
374            }
375            Val::Neu { .. } => Ok(self.st.arena.neutral_app(f, a)),
376            _ => Err(reject("application of a value that is not a function")),
377        }
378    }
379
380    pub(crate) fn apply_all(&mut self, mut f: V, args: &[V]) -> KResult<V> {
381        let mut i = 0;
382        while i < args.len() {
383            if let Val::Lam { env, body, .. } = self.st.arena.get(f) {
384                let mut benv = self.st.arena.extend(env, args[i]);
385                let mut b = body;
386                i += 1;
387                while i < args.len() {
388                    let Node::Lam(_, inner) = self.k.s.node(b) else {
389                        break;
390                    };
391                    benv = self.st.arena.extend(benv, args[i]);
392                    b = inner;
393                    i += 1;
394                }
395                f = self.eval(benv, b)?;
396            } else {
397                f = self.apply(f, args[i])?;
398                i += 1;
399            }
400        }
401        Ok(f)
402    }
403
404    /// The domain of a lambda value.
405    pub(crate) fn lam_domain(&mut self, v: V) -> KResult<V> {
406        match self.st.arena.get(v) {
407            Val::Lam { dom, env, .. } => {
408                if let Some(d) = self.st.arena.dom_memo(v) {
409                    return Ok(d);
410                }
411                let d = self.eval(env, dom)?;
412                self.st.arena.set_dom_memo(v, d);
413                Ok(d)
414            }
415            Val::Pi { dom, .. } | Val::PiLam { dom, .. } => Ok(dom),
416            _ => Err(reject("expected a function")),
417        }
418    }
419
420    /// The body of a closure applied to `x`: a lambda's body, a Π's codomain.
421    pub fn instantiate(&mut self, clo: V, x: V) -> KResult<V> {
422        match self.st.arena.get(clo) {
423            Val::Lam { env, body, .. } | Val::Pi { env, body, .. } => {
424                let env2 = self.st.arena.extend(env, x);
425                self.eval(env2, body)
426            }
427            Val::PiLam {
428                dom,
429                env,
430                ctx,
431                body,
432            } => {
433                let env2 = self.st.arena.extend(env, x);
434                let ctx2 = self.st.arena.ctx_extend(ctx, dom);
435                self.infer(env2, ctx2, body, false)
436            }
437            _ => Err(reject("expected a function or function type")),
438        }
439    }
440}
441
442/// `a ∪ { i - shift | i ∈ b, i ≥ shift }`, both sets ascending, in one walk. A union that adds
443/// nothing to `a` is `a` itself, its allocation shared: down a deep nest of binders the set of a
444/// body is usually its domain's plus nothing, or its body's.
445fn union_shifted(
446    a: &std::rc::Rc<[u32]>,
447    whole: &std::rc::Rc<[u32]>,
448    shift: u32,
449) -> std::rc::Rc<[u32]> {
450    let b = &whole[whole.partition_point(|&i| i < shift)..];
451    if b.is_empty() {
452        return a.clone();
453    }
454    if a.is_empty() && shift == 0 {
455        return whole.clone();
456    }
457    let mut out = Vec::with_capacity(a.len() + b.len());
458    let (mut i, mut j) = (0, 0);
459    while i < a.len() && j < b.len() {
460        let (x, y) = (a[i], b[j] - shift);
461        match x.cmp(&y) {
462            std::cmp::Ordering::Less => {
463                out.push(x);
464                i += 1;
465            }
466            std::cmp::Ordering::Greater => {
467                out.push(y);
468                j += 1;
469            }
470            std::cmp::Ordering::Equal => {
471                out.push(x);
472                i += 1;
473                j += 1;
474            }
475        }
476    }
477    out.extend_from_slice(&a[i..]);
478    out.extend(b[j..].iter().map(|&y| y - shift));
479    if out.len() == a.len() {
480        return a.clone();
481    }
482    out.into()
483}
484
485#[cfg(test)]
486#[path = "../../../../logicaffeine_tests/unit/logicaffeine_kernel/dag/sem/eval/union_controls.rs"]
487mod union_controls;