Skip to main content

logicaffeine_kernel/dag/
primitive.rs

1//! Primitive validation: the literal extension is only as sound as the definitions it replaces.
2//!
3//! Lean's kernel evaluates `Nat.add 2 3` on machine numbers instead of unfolding `Nat.add`. That
4//! is sound only if the environment's `Nat.add` really is addition. Before a primitive name is
5//! admitted, its definition is checked against its defining equations, stated over a free
6//! variable so they hold for every argument (the scheme of `lean4lean`'s `Primitive.lean`). A
7//! primitive whose equations are proven is accelerated; one whose form is wrong is rejected.
8
9use super::check::DefnDecl;
10use super::env::Safety;
11use super::error::{KResult, KernelFailure};
12use super::expr::ExprId;
13use super::level::LevelsId;
14use super::name::NameId;
15use super::tc::Kernel;
16
17impl Kernel {
18    fn prim_fail(&self, n: NameId) -> KernelFailure {
19        KernelFailure::reject(format!(
20            "invalid form for primitive definition '{}'",
21            self.s.names.display(n)
22        ))
23    }
24
25    fn c0(&mut self, n: NameId) -> ExprId {
26        self.s.cnst(n, LevelsId::EMPTY)
27    }
28
29    /// `Nat → a ≡ Nat → b`: equality under one bound natural number (`BVar(0)`).
30    fn defeq1(&mut self, a: ExprId, b: ExprId) -> KResult<bool> {
31        let nat = self.c0(self.wk.nat);
32        let pa = self.s.pi(nat, a);
33        let pb = self.s.pi(nat, b);
34        self.is_def_eq(pa, pb)
35    }
36
37    /// Equality under two bound natural numbers (`BVar(1)`, `BVar(0)`).
38    fn defeq2(&mut self, a: ExprId, b: ExprId) -> KResult<bool> {
39        let nat = self.c0(self.wk.nat);
40        let pa = self.s.pi(nat, a);
41        let pb = self.s.pi(nat, b);
42        self.defeq1(pa, pb)
43    }
44
45    fn arrow(&mut self, a: ExprId, b: ExprId) -> ExprId {
46        let b = self.s.lift_loose(b, 0, 1);
47        self.s.pi(a, b)
48    }
49
50    fn app2(&mut self, f: ExprId, a: ExprId, b: ExprId) -> ExprId {
51        self.s.mk_app_n(f, &[a, b])
52    }
53
54    /// Returns whether `d` may take a primitive name (Lean's `checkPrimitiveDef`), and records a
55    /// primitive whose equations check as computed natively (`validated_primitives`).
56    ///
57    /// In LOGOS's theory a primitive's name refuses nothing: a definition that does not check as
58    /// the primitive is an ordinary one, computed by unfolding, and its literal rules read `Zero`
59    /// and `Succ` only while they are `Nat`'s constructors (`Kernel::reads_nat_literal`). So a
60    /// definition named `Succ` is admitted, and an impostor `Nat.div` is not computed natively.
61    pub fn check_primitive_def(&mut self, d: &DefnDecl) -> KResult<bool> {
62        let checked = self.check_primitive_form(d);
63        if self.theory == super::tc::Theory::Logos {
64            return Ok(matches!(checked, Ok(true)));
65        }
66        checked
67    }
68
69    fn check_primitive_form(&mut self, d: &DefnDecl) -> KResult<bool> {
70        let n = d.header.name;
71        if d.safety != Safety::Safe || !self.wk.primitives().contains(&n) {
72            return Ok(false);
73        }
74        if !d.header.lparams.is_empty() {
75            return Err(self.prim_fail(n));
76        }
77        let w = WellKnownIds::of(self);
78        let nat = self.c0(w.nat);
79        let bool_ = self.c0(w.bool_);
80        let tru = self.c0(w.tru);
81        let fal = self.c0(w.fal);
82        let zero = self.c0(w.zero);
83        let succ_c = self.c0(w.succ);
84        let x = self.s.bvar(0);
85        let y = self.s.bvar(1);
86        let succ = |k: &mut Kernel, e: ExprId| k.s.app(succ_c, e);
87        let nn = self.arrow(nat, nat);
88        let nnn = self.arrow(nat, nn);
89        let nb = self.arrow(nat, bool_);
90        let nnb = self.arrow(nat, nb);
91        let v = d.value;
92        let ty = d.header.ty;
93        let ok = if n == w.add {
94            self.env.contains(w.nat)
95                && self.is_def_eq(ty, nnn)?
96                && {
97                    let lhs = self.app2(v, x, zero);
98                    self.defeq1(lhs, x)?
99                }
100                && {
101                    let sx = succ(self, x);
102                    let lhs = self.app2(v, y, sx);
103                    let r = self.app2(v, y, x);
104                    let rhs = succ(self, r);
105                    self.defeq2(lhs, rhs)?
106                }
107        } else if n == w.pred {
108            self.env.contains(w.nat)
109                && self.is_def_eq(ty, nn)?
110                && {
111                    let lhs = self.s.app(v, zero);
112                    self.is_def_eq(lhs, zero)?
113                }
114                && {
115                    let sx = succ(self, x);
116                    let lhs = self.s.app(v, sx);
117                    self.defeq1(lhs, x)?
118                }
119        } else if n == w.sub {
120            self.env.contains(w.pred)
121                && self.is_def_eq(ty, nnn)?
122                && {
123                    let lhs = self.app2(v, x, zero);
124                    self.defeq1(lhs, x)?
125                }
126                && {
127                    let pred = self.c0(w.pred);
128                    let sx = succ(self, x);
129                    let lhs = self.app2(v, y, sx);
130                    let r = self.app2(v, y, x);
131                    let rhs = self.s.app(pred, r);
132                    self.defeq2(lhs, rhs)?
133                }
134        } else if n == w.mul {
135            self.env.contains(w.add)
136                && self.is_def_eq(ty, nnn)?
137                && {
138                    let lhs = self.app2(v, x, zero);
139                    self.defeq1(lhs, zero)?
140                }
141                && {
142                    let add = self.c0(w.add);
143                    let sx = succ(self, x);
144                    let lhs = self.app2(v, y, sx);
145                    let r = self.app2(v, y, x);
146                    let rhs = self.app2(add, r, y);
147                    self.defeq2(lhs, rhs)?
148                }
149        } else if n == w.pow {
150            self.env.contains(w.mul)
151                && self.is_def_eq(ty, nnn)?
152                && {
153                    let one = succ(self, zero);
154                    let lhs = self.app2(v, x, zero);
155                    self.defeq1(lhs, one)?
156                }
157                && {
158                    let mul = self.c0(w.mul);
159                    let sx = succ(self, x);
160                    let lhs = self.app2(v, y, sx);
161                    let r = self.app2(v, y, x);
162                    let rhs = self.app2(mul, r, y);
163                    self.defeq2(lhs, rhs)?
164                }
165        } else if n == w.beq || n == w.ble {
166            let is_beq = n == w.beq;
167            self.env.contains(w.nat)
168                && self.env.contains(w.bool_)
169                && self.is_def_eq(ty, nnb)?
170                && {
171                    let lhs = self.app2(v, zero, zero);
172                    self.is_def_eq(lhs, tru)?
173                }
174                && {
175                    let sx = succ(self, x);
176                    let lhs = self.app2(v, zero, sx);
177                    let expect = if is_beq { fal } else { tru };
178                    self.defeq1(lhs, expect)?
179                }
180                && {
181                    let sx = succ(self, x);
182                    let lhs = self.app2(v, sx, zero);
183                    self.defeq1(lhs, fal)?
184                }
185                && {
186                    let sx = succ(self, x);
187                    let sy = succ(self, y);
188                    let lhs = self.app2(v, sy, sx);
189                    let rhs = self.app2(v, y, x);
190                    self.defeq2(lhs, rhs)?
191                }
192        } else if n == w.shl {
193            self.env.contains(w.mul)
194                && self.is_def_eq(ty, nnn)?
195                && {
196                    let lhs = self.app2(v, x, zero);
197                    self.defeq1(lhs, x)?
198                }
199                && {
200                    let mul = self.c0(w.mul);
201                    let one = succ(self, zero);
202                    let two = succ(self, one);
203                    let sy = succ(self, y);
204                    let lhs = self.app2(v, x, sy);
205                    let tx = self.app2(mul, two, x);
206                    let rhs = self.app2(v, tx, y);
207                    self.defeq2(lhs, rhs)?
208                }
209        } else if n == w.shr {
210            self.env.contains(w.div)
211                && self.is_def_eq(ty, nnn)?
212                && {
213                    let lhs = self.app2(v, x, zero);
214                    self.defeq1(lhs, x)?
215                }
216                && {
217                    let div = self.c0(w.div);
218                    let one = succ(self, zero);
219                    let two = succ(self, one);
220                    let sy = succ(self, y);
221                    let lhs = self.app2(v, x, sy);
222                    let r = self.app2(v, x, y);
223                    let rhs = self.app2(div, r, two);
224                    self.defeq2(lhs, rhs)?
225                }
226        } else if n == w.land || n == w.lor || n == w.xor {
227            self.env.contains(w.bitwise)
228                && self.is_def_eq(ty, nnn)?
229                && self.check_bitwise_op(n, v, &w)?
230        } else if n == w.char_of_nat {
231            let ch = self.c0(w.char_);
232            self.env.contains(w.nat) && self.env.contains(w.char_) && {
233                let t = self.arrow(nat, ch);
234                self.is_def_eq(ty, t)?
235            }
236        } else if n == w.string_of_list {
237            self.check_string_of_list(ty)?
238        } else if n == w.mod_ || n == w.div || n == w.gcd || n == w.bitwise {
239            let admitted = self.env.contains(w.nat) && {
240                let expect = if n == w.bitwise {
241                    let bb = self.arrow(bool_, bool_);
242                    let bbb = self.arrow(bool_, bb);
243                    self.arrow(bbb, nnn)
244                } else {
245                    nnn
246                };
247                self.is_def_eq(ty, expect)?
248            };
249            if admitted && self.check_wf_primitive(d)? && self.accelerable_after_validation(n) {
250                self.validated_primitives.insert(n);
251            }
252            if !admitted {
253                return Err(self.prim_fail(n));
254            }
255            return Ok(true);
256        } else {
257            return Ok(false);
258        };
259        if !ok {
260            return Err(self.prim_fail(n));
261        }
262        if self.accelerable_after_validation(n) {
263            self.validated_primitives.insert(n);
264        }
265        Ok(true)
266    }
267
268    /// Whether a primitive whose equations just checked may be computed natively. Its equations
269    /// are stated in terms of other primitives (`Nat.mul` in terms of `Nat.add`, `Nat.land` as
270    /// `Nat.bitwise and`), so they pin its meaning only once those are pinned too. A
271    /// well-founded primitive whose unfolding could not be validated is still admitted by its
272    /// type, and is then computed by unfolding rather than natively.
273    fn accelerable_after_validation(&self, n: NameId) -> bool {
274        let w = &self.wk;
275        let deps: &[NameId] = if n == w.nat_mod || n == w.nat_div {
276            &[w.nat_sub]
277        } else if n == w.nat_gcd {
278            &[w.nat_mod]
279        } else if n == w.nat_bitwise {
280            &[w.nat_div, w.nat_mod, w.nat_add]
281        } else if n == w.nat_sub {
282            &[w.nat_pred]
283        } else if n == w.nat_mul || n == w.nat_shift_left {
284            &[w.nat_add, w.nat_mul]
285        } else if n == w.nat_pow {
286            &[w.nat_add, w.nat_mul]
287        } else if n == w.nat_shift_right {
288            &[w.nat_div]
289        } else if [w.nat_land, w.nat_lor, w.nat_xor].contains(&n) {
290            &[w.nat_bitwise]
291        } else {
292            &[]
293        };
294        deps.iter()
295            .all(|d| *d == n || self.validated_primitives.contains(d))
296    }
297
298    fn check_bitwise_op(&mut self, n: NameId, v: ExprId, w: &WellKnownIds) -> KResult<bool> {
299        let super::expr::Node::App(f, op) = self.s.node(v) else {
300            return Ok(false);
301        };
302        if f != self.c0(w.bitwise) {
303            return Ok(false);
304        }
305        let tru = self.c0(w.tru);
306        let fal = self.c0(w.fal);
307        let x = self.s.bvar(0);
308        let bool_ = self.c0(w.bool_);
309        let under_bool = |k: &mut Kernel, a: ExprId, b: ExprId| -> KResult<bool> {
310            let pa = k.s.pi(bool_, a);
311            let pb = k.s.pi(bool_, b);
312            k.is_def_eq(pa, pb)
313        };
314        let app = |k: &mut Kernel, a: ExprId, b: ExprId| k.s.mk_app_n(op, &[a, b]);
315        Ok(if n == w.land {
316            let a = app(self, fal, x);
317            let b = app(self, tru, x);
318            under_bool(self, a, fal)? && under_bool(self, b, x)?
319        } else if n == w.lor {
320            let a = app(self, fal, x);
321            let b = app(self, tru, x);
322            under_bool(self, a, x)? && under_bool(self, b, tru)?
323        } else {
324            let cases = [
325                (fal, fal, fal),
326                (tru, fal, tru),
327                (fal, tru, tru),
328                (tru, tru, fal),
329            ];
330            let mut all = true;
331            for (a, b, r) in cases {
332                let e = app(self, a, b);
333                if !self.is_def_eq(e, r)? {
334                    all = false;
335                    break;
336                }
337            }
338            all
339        })
340    }
341
342    fn check_string_of_list(&mut self, ty: ExprId) -> KResult<bool> {
343        let w = WellKnownIds::of(self);
344        for needed in [w.char_, w.list, w.list_nil, w.list_cons, w.string] {
345            if !self.env.contains(needed) {
346                return Ok(false);
347            }
348        }
349        let zero = self.s.levels.list(&[super::level::LevelId::ZERO]);
350        let ch = self.c0(w.char_);
351        let list_c = self.s.cnst(w.list, zero);
352        let list_char = self.s.app(list_c, ch);
353        let string = self.c0(w.string);
354        let expect = self.arrow(list_char, string);
355        let nil_c = self.s.cnst(w.list_nil, zero);
356        let nil = self.s.app(nil_c, ch);
357        let nil_t = self.check(nil)?;
358        let cons_c = self.s.cnst(w.list_cons, zero);
359        let cons = self.s.app(cons_c, ch);
360        let cons_t = self.check(cons)?;
361        let lc_lc = self.arrow(list_char, list_char);
362        let cons_expect = self.arrow(ch, lc_lc);
363        Ok(self.is_def_eq(nil_t, list_char)?
364            && self.is_def_eq(cons_t, cons_expect)?
365            && self.is_def_eq(ty, expect)?)
366    }
367}
368
369/// The primitive names, copied out so validation can borrow the kernel mutably.
370pub(crate) struct WellKnownIds {
371    nat: NameId,
372    zero: NameId,
373    succ: NameId,
374    add: NameId,
375    pred: NameId,
376    sub: NameId,
377    mul: NameId,
378    pow: NameId,
379    div: NameId,
380    mod_: NameId,
381    gcd: NameId,
382    beq: NameId,
383    ble: NameId,
384    shl: NameId,
385    shr: NameId,
386    land: NameId,
387    lor: NameId,
388    xor: NameId,
389    bitwise: NameId,
390    bool_: NameId,
391    tru: NameId,
392    fal: NameId,
393    char_: NameId,
394    char_of_nat: NameId,
395    string: NameId,
396    string_of_list: NameId,
397    list: NameId,
398    list_nil: NameId,
399    list_cons: NameId,
400}
401
402impl WellKnownIds {
403    fn of(k: &Kernel) -> WellKnownIds {
404        let w = &k.wk;
405        WellKnownIds {
406            nat: w.nat,
407            zero: w.nat_zero,
408            succ: w.nat_succ,
409            add: w.nat_add,
410            pred: w.nat_pred,
411            sub: w.nat_sub,
412            mul: w.nat_mul,
413            pow: w.nat_pow,
414            div: w.nat_div,
415            mod_: w.nat_mod,
416            gcd: w.nat_gcd,
417            beq: w.nat_beq,
418            ble: w.nat_ble,
419            shl: w.nat_shift_left,
420            shr: w.nat_shift_right,
421            land: w.nat_land,
422            lor: w.nat_lor,
423            xor: w.nat_xor,
424            bitwise: w.nat_bitwise,
425            bool_: w.bool_,
426            tru: w.bool_true,
427            fal: w.bool_false,
428            char_: w.char_,
429            char_of_nat: w.char_of_nat,
430            string: w.string,
431            string_of_list: w.string_of_list,
432            list: w.list,
433            list_nil: w.list_nil,
434            list_cons: w.list_cons,
435        }
436    }
437}