Skip to main content

logicaffeine_kernel/dag/
check.rs

1//! Admitting declarations: the kernel's only door into the environment.
2
3use super::env::{Const, Hint, Kind, QuotKind, Safety};
4use super::error::{KResult, KernelFailure};
5use super::expr::{ExprId, Node};
6use super::level::{LevelId, LevelsId};
7use super::name::NameId;
8use super::tc::{Engine, Kernel, Theory};
9use super::wellknown::QuotNames;
10use std::rc::Rc;
11
12#[derive(Clone, Debug)]
13pub struct Header {
14    pub name: NameId,
15    pub lparams: Rc<[NameId]>,
16    pub ty: ExprId,
17}
18
19#[derive(Clone, Debug)]
20pub struct DefnDecl {
21    pub header: Header,
22    pub value: ExprId,
23    pub hint: Hint,
24    pub safety: Safety,
25}
26
27#[derive(Clone, Debug)]
28pub enum Decl {
29    Axiom {
30        header: Header,
31        is_unsafe: bool,
32    },
33    Defn(DefnDecl),
34    Thm {
35        header: Header,
36        value: ExprId,
37    },
38    Opaque {
39        header: Header,
40        value: ExprId,
41        is_unsafe: bool,
42    },
43    Mutual(Vec<DefnDecl>),
44    Quot {
45        header: Header,
46        kind: QuotKind,
47    },
48}
49
50impl Decl {
51    /// The declared name (a mutual block is named by its first member).
52    pub fn name(&self) -> NameId {
53        match self {
54            Decl::Axiom { header, .. }
55            | Decl::Thm { header, .. }
56            | Decl::Opaque { header, .. }
57            | Decl::Quot { header, .. } => header.name,
58            Decl::Defn(d) => d.header.name,
59            Decl::Mutual(ds) => ds.first().map_or(NameId::ANON, |d| d.header.name),
60        }
61    }
62
63    /// The export's record kind for this declaration (`mutual` for a block held until complete).
64    pub fn kind_name(&self) -> &'static str {
65        match self {
66            Decl::Axiom { .. } => "axiom",
67            Decl::Defn(_) => "def",
68            Decl::Thm { .. } => "thm",
69            Decl::Opaque { .. } => "opaque",
70            Decl::Mutual(_) => "mutual",
71            Decl::Quot { .. } => "quot",
72        }
73    }
74}
75
76impl Kernel {
77    pub fn check_name(&self, n: NameId, allow_primitive: bool) -> KResult<()> {
78        if self.env.contains(n) {
79            return Err(KernelFailure::reject(format!(
80                "'{}' has already been declared",
81                self.s.names.display(n)
82            )));
83        }
84        if !allow_primitive && self.theory == Theory::Lean && self.wk.primitives().contains(&n) {
85            return Err(KernelFailure::reject(format!(
86                "unexpected declaration of the primitive '{}' in a non-canonical form",
87                self.s.names.display(n)
88            )));
89        }
90        Ok(())
91    }
92
93    pub fn check_duplicate_lparams(&self, ps: &[NameId]) -> KResult<()> {
94        for (i, p) in ps.iter().enumerate() {
95            if ps[i + 1..].contains(p) {
96                return Err(KernelFailure::reject(format!(
97                    "duplicate universe level parameter '{}'",
98                    self.s.names.display(*p)
99                )));
100            }
101        }
102        Ok(())
103    }
104
105    fn check_closed(&self, e: ExprId) -> KResult<()> {
106        if self.s.has_fvar(e) || self.s.has_loose_bvars(e) {
107            return Err(KernelFailure::reject(
108                "declaration contains free or loose bound variables",
109            ));
110        }
111        Ok(())
112    }
113
114    /// Checks a declaration's header; the universe level of its type.
115    fn check_constant_val(&mut self, h: &Header, allow_primitive: bool) -> KResult<LevelId> {
116        self.check_name(h.name, allow_primitive)?;
117        self.check_duplicate_lparams(&h.lparams)?;
118        self.check_closed(h.ty)?;
119        match self.engine {
120            Engine::Sem => self.with_sem(|sem| sem.check_type(h.ty)),
121            Engine::Classic | Engine::Dual => {
122                let sort = self.check(h.ty)?;
123                let sort = self.ensure_sort(sort)?;
124                Ok(self.sort_level(sort))
125            }
126        }
127    }
128
129    fn check_value(&mut self, h: &Header, value: ExprId) -> KResult<()> {
130        self.check_closed(value)?;
131        if self.engine == Engine::Sem {
132            return self.with_sem(|sem| sem.check_value(value, h.ty));
133        }
134        if self.theory == Theory::Logos && self.check_fix_against(value, h.ty)? {
135            return Ok(());
136        }
137        let vt = self.check(value)?;
138        if !self.conforms(vt, h.ty)? {
139            return Err(
140                KernelFailure::reject("declaration type mismatch").with_detail(format!(
141                    "the value has type\n    {}\n  but is declared with type\n    {}",
142                    self.show(vt),
143                    self.show(h.ty)
144                )),
145            );
146        }
147        Ok(())
148    }
149
150    /// Runs `f` as one declaration's check: fresh caches and local context, scratch memory
151    /// released afterwards whatever the outcome.
152    pub fn with_decl<T>(
153        &mut self,
154        lparams: Rc<[NameId]>,
155        safety: Safety,
156        f: impl FnOnce(&mut Kernel) -> KResult<T>,
157    ) -> KResult<T> {
158        self.begin_decl(lparams, safety);
159        let r = f(self);
160        self.end_decl();
161        r
162    }
163
164    /// Whether `d` may be admitted unchecked in a trusted-prefix run (see
165    /// [`super::export::Observe::trust_before`]): anything but a primitive or the quotient.
166    pub fn may_trust(&self, d: &Decl) -> bool {
167        !matches!(d, Decl::Quot { .. }) && !self.wk.primitives().contains(&d.name())
168    }
169
170    /// Whether an inductive block declaring `claimed` may be admitted unchecked: none of its
171    /// constants is a primitive.
172    pub fn may_trust_block(&self, claimed: &[Const]) -> bool {
173        let prims = self.wk.primitives();
174        claimed.iter().all(|c| !prims.contains(&c.name))
175    }
176
177    /// Admits `d` as the export states it, without checking it (trusted-prefix runs only).
178    pub fn admit_trusted(&mut self, d: Decl) {
179        self.admit(d);
180    }
181
182    /// Admits an inductive block's constants as the export states them, without checking them
183    /// (trusted-prefix runs only).
184    pub fn admit_trusted_block(&mut self, claimed: Vec<Const>) {
185        for c in claimed {
186            self.env.add(c);
187        }
188    }
189
190    /// Everything [`Kernel::add_decl`] checks, admitting nothing.
191    #[cfg(feature = "shadow")]
192    pub(crate) fn check_without_admitting(&mut self, decl: &Decl) -> KResult<()> {
193        let name = decl.name();
194        self.check_decl(decl)
195            .map_err(|e| e.in_declaration(self.s.names.display(name)))
196    }
197
198    pub fn add_decl(&mut self, decl: Decl) -> KResult<()> {
199        let name = decl.name();
200        self.add_decl_inner(decl)
201            .map_err(|e| e.in_declaration(self.s.names.display(name)))
202    }
203
204    fn add_decl_inner(&mut self, decl: Decl) -> KResult<()> {
205        if let Decl::Quot { header, kind } = decl {
206            return self.add_quot(header, kind, self.wk.eq);
207        }
208        if self.engine == Engine::Dual {
209            self.check_decl_by_both(&decl)?;
210        } else {
211            self.check_decl(&decl)?;
212        }
213        self.admit(decl);
214        Ok(())
215    }
216
217    /// Checks a declaration by BOTH engines, which must agree: the value engine first, then the
218    /// substitution engine in declaration scopes of its own. A disagreement is never resolved
219    /// towards acceptance.
220    fn check_decl_by_both(&mut self, decl: &Decl) -> KResult<()> {
221        self.engine = Engine::Sem;
222        let by_value = self.check_decl(decl);
223        self.engine = Engine::Classic;
224        let by_substitution = self.check_decl(decl);
225        self.engine = Engine::Dual;
226        let verdict = |r: &KResult<()>| match r {
227            Ok(()) => "accepts",
228            Err(e) if e.is_reject() => "rejects",
229            Err(_) => "declines",
230        };
231        match (by_value, by_substitution) {
232            (Ok(()), Ok(())) => Ok(()),
233            (Err(v), Err(s)) if v.is_reject() && s.is_reject() => Err(s),
234            (Err(e), _) | (_, Err(e)) if !e.is_reject() => Err(e),
235            (v, s) => Err(KernelFailure::resource(format!(
236                "the two engines disagree: evaluation {}, substitution {}",
237                verdict(&v),
238                verdict(&s)
239            ))
240            .with_detail(match (v, s) {
241                (Err(e), _) | (_, Err(e)) => e.to_string(),
242                _ => unreachable!(),
243            })),
244        }
245    }
246
247    /// Everything a declaration must satisfy, without admitting it (any provisional declarations
248    /// a check needs are withdrawn again before it returns).
249    fn check_decl(&mut self, decl: &Decl) -> KResult<()> {
250        match decl {
251            Decl::Axiom { header, is_unsafe } => {
252                let safety = if *is_unsafe {
253                    Safety::Unsafe
254                } else {
255                    Safety::Safe
256                };
257                self.with_decl(header.lparams.clone(), safety, |k| {
258                    k.check_constant_val(header, false)
259                })?;
260                Ok(())
261            }
262            Decl::Defn(d) => self.check_definition(d),
263            Decl::Thm { header, value } => {
264                self.with_decl(header.lparams.clone(), Safety::Safe, |k| {
265                    let level = k.check_constant_val(header, false)?;
266                    if !k.s.levels.is_always_zero(level) {
267                        return Err(KernelFailure::reject("theorem type is not a proposition")
268                            .with_detail(format!("its type is {}", k.show(header.ty))));
269                    }
270                    k.check_value(header, *value)
271                })
272            }
273            Decl::Opaque { header, value, .. } => {
274                self.with_decl(header.lparams.clone(), Safety::Safe, |k| {
275                    k.check_constant_val(header, false)?;
276                    k.check_value(header, *value)
277                })
278            }
279            Decl::Mutual(ds) => self.check_mutual(ds),
280            Decl::Quot { .. } => unreachable!("quotients are admitted by add_quot"),
281        }
282    }
283
284    fn admit(&mut self, decl: Decl) {
285        let defn = |d: DefnDecl| Const {
286            name: d.header.name,
287            lparams: d.header.lparams,
288            ty: d.header.ty,
289            kind: Kind::Defn {
290                value: d.value,
291                hint: d.hint,
292                safety: d.safety,
293            },
294        };
295        match decl {
296            Decl::Axiom { header, is_unsafe } => self.env.add(Const {
297                name: header.name,
298                lparams: header.lparams,
299                ty: header.ty,
300                kind: Kind::Axiom { is_unsafe },
301            }),
302            Decl::Defn(d) => self.env.add(defn(d)),
303            Decl::Thm { header, value } => self.env.add(Const {
304                name: header.name,
305                lparams: header.lparams,
306                ty: header.ty,
307                kind: Kind::Thm { value },
308            }),
309            Decl::Opaque {
310                header,
311                value,
312                is_unsafe,
313            } => self.env.add(Const {
314                name: header.name,
315                lparams: header.lparams,
316                ty: header.ty,
317                kind: Kind::Opaque { value, is_unsafe },
318            }),
319            Decl::Mutual(ds) => {
320                for d in ds {
321                    self.env.add(defn(d));
322                }
323            }
324            Decl::Quot { .. } => unreachable!("quotients are admitted by add_quot"),
325        }
326    }
327
328    fn check_definition(&mut self, d: &DefnDecl) -> KResult<()> {
329        let h = &d.header;
330        if d.safety == Safety::Unsafe {
331            self.with_decl(h.lparams.clone(), Safety::Unsafe, |k| {
332                k.check_constant_val(h, false)
333            })?;
334            self.env.add_provisional(Const {
335                name: h.name,
336                lparams: h.lparams.clone(),
337                ty: h.ty,
338                kind: Kind::Axiom { is_unsafe: true },
339            });
340            let r = self.with_decl(h.lparams.clone(), Safety::Unsafe, |k| {
341                k.check_value(h, d.value)
342            });
343            self.env.remove(h.name);
344            r
345        } else {
346            self.with_decl(h.lparams.clone(), Safety::Safe, |k| {
347                let allow = k.check_primitive_def(d)?;
348                k.check_constant_val(h, allow)?;
349                k.check_value(h, d.value)
350            })
351        }
352    }
353
354    fn check_mutual(&mut self, ds: &[DefnDecl]) -> KResult<()> {
355        let Some(first) = ds.first() else {
356            return Err(KernelFailure::reject("empty mutual definition"));
357        };
358        if first.safety == Safety::Safe {
359            return Err(KernelFailure::reject(
360                "invalid mutual definition, declaration is not tagged as unsafe/partial",
361            ));
362        }
363        let safety = first.safety;
364        let lparams = first.header.lparams.clone();
365        self.with_decl(lparams.clone(), safety, |k| {
366            let mut seen = Vec::new();
367            for d in ds {
368                if d.safety != safety {
369                    return Err(KernelFailure::reject(
370                        "mutual declarations must have the same safety annotation",
371                    ));
372                }
373                if d.header.lparams != lparams {
374                    return Err(KernelFailure::reject(
375                        "mutual declarations must have the same universe parameters",
376                    ));
377                }
378                if seen.contains(&d.header.name) {
379                    return Err(KernelFailure::reject(
380                        "duplicate declaration name in mutual block",
381                    ));
382                }
383                seen.push(d.header.name);
384                k.check_constant_val(&d.header, false)?;
385            }
386            Ok(())
387        })?;
388        for d in ds {
389            self.env.add_provisional(Const {
390                name: d.header.name,
391                lparams: d.header.lparams.clone(),
392                ty: d.header.ty,
393                kind: Kind::Axiom {
394                    is_unsafe: safety == Safety::Unsafe,
395                },
396            });
397        }
398        let r = self.with_decl(lparams, safety, |k| {
399            for d in ds {
400                k.check_value(&d.header, d.value)?;
401            }
402            Ok(())
403        });
404        for d in ds {
405            self.env.remove(d.header.name);
406        }
407        r
408    }
409
410    // ---- quotients ----
411
412    /// `eq` is the canonical equality: one inductive `Π (α : Sort u), α → α → Prop` with one
413    /// constructor `Π (α : Sort u) (a : α), eq α a a`.
414    fn check_eq_type(&mut self, eq: NameId) -> KResult<()> {
415        let fail =
416            |m: &str| KernelFailure::reject(format!("failed to initialize quot module, {m}"));
417        let c = self
418            .env
419            .get(eq)
420            .cloned()
421            .ok_or_else(|| fail("environment does not have 'Eq' type"))?;
422        let Kind::Induct(info) = &c.kind else {
423            return Err(fail("'Eq' is not an inductive type"));
424        };
425        if c.lparams.len() != 1 {
426            return Err(fail("unexpected number of universe params at 'Eq' type"));
427        }
428        if info.ctors.len() != 1 {
429            return Err(fail("unexpected number of constructors for 'Eq' type"));
430        }
431        let u = self.s.levels.param(c.lparams[0]);
432        let sort_u = self.s.sort(u);
433        let alpha = self.push_local(sort_u, None);
434        let prop = self.s.prop();
435        let inner = self.s.pi(alpha, prop);
436        let arrow = self.s.pi(alpha, inner);
437        let expected = self.mk_pi(&[alpha], arrow);
438        if c.ty != expected {
439            return Err(fail("'Eq' has an unexpected type"));
440        }
441        let refl = self.get_const(info.ctors[0])?;
442        if refl.lparams.len() != 1 {
443            return Err(fail(
444                "unexpected number of universe params at 'Eq' type constructor",
445            ));
446        }
447        let u = self.s.levels.param(refl.lparams[0]);
448        let sort_u = self.s.sort(u);
449        let alpha = self.push_local(sort_u, None);
450        let a = self.push_local(alpha, None);
451        let eq_c = self.s.cnst_levels(eq, &[u]);
452        let body = self.s.mk_app_n(eq_c, &[alpha, a, a]);
453        let expected = self.mk_pi(&[alpha, a], body);
454        if refl.ty != expected {
455            return Err(fail("unexpected type for 'Eq' type constructor"));
456        }
457        Ok(())
458    }
459
460    /// The canonical type of each quotient constant, over level parameters `ps`. Built over
461    /// free variables, so a non-dependent arrow is a plain `Pi` whose body is closed.
462    fn quot_canonical(
463        &mut self,
464        kind: QuotKind,
465        ps: &[NameId],
466        set: QuotNames,
467        eq: NameId,
468    ) -> KResult<ExprId> {
469        let need = if kind == QuotKind::Lift { 2 } else { 1 };
470        if ps.len() != need {
471            return Err(KernelFailure::reject(
472                "unexpected number of universe parameters for a quotient constant",
473            ));
474        }
475        let u = self.s.levels.param(ps[0]);
476        let sort_u = self.s.sort(u);
477        let prop = self.s.prop();
478        let alpha = self.push_local(sort_u, None);
479        let a_prop = self.s.pi(alpha, prop);
480        let rel_ty = self.s.pi(alpha, a_prop);
481        let ul = self.s.levels.list(&[u]);
482        let quot = self.s.cnst(set.ty, ul);
483        Ok(match kind {
484            QuotKind::Type => {
485                let r = self.push_local(rel_ty, None);
486                self.mk_pi(&[alpha, r], sort_u)
487            }
488            QuotKind::Ctor => {
489                let r = self.push_local(rel_ty, None);
490                let a = self.push_local(alpha, None);
491                let qr = self.s.mk_app_n(quot, &[alpha, r]);
492                self.mk_pi(&[alpha, r, a], qr)
493            }
494            QuotKind::Lift => {
495                let v = self.s.levels.param(ps[1]);
496                let sort_v = self.s.sort(v);
497                let r = self.push_local(rel_ty, None);
498                let beta = self.push_local(sort_v, None);
499                let f_ty = self.s.pi(alpha, beta);
500                let f = self.push_local(f_ty, None);
501                let a = self.push_local(alpha, None);
502                let b = self.push_local(alpha, None);
503                let rab = self.s.mk_app_n(r, &[a, b]);
504                let vl = self.s.levels.list(&[v]);
505                let eqv = self.s.cnst(eq, vl);
506                let fa = self.s.app(f, a);
507                let fb = self.s.app(f, b);
508                let feq = self.s.mk_app_n(eqv, &[beta, fa, fb]);
509                let rab_to = self.s.pi(rab, feq);
510                let sanity = self.mk_pi(&[a, b], rab_to);
511                let qr = self.s.mk_app_n(quot, &[alpha, r]);
512                let qr_to_beta = self.s.pi(qr, beta);
513                let body = self.s.pi(sanity, qr_to_beta);
514                self.mk_pi(&[alpha, r, beta, f], body)
515            }
516            QuotKind::Ind => {
517                let r = self.push_local(rel_ty, None);
518                let qr = self.s.mk_app_n(quot, &[alpha, r]);
519                let qr_prop = self.s.pi(qr, prop);
520                let beta = self.push_local(qr_prop, None);
521                let a = self.push_local(alpha, None);
522                let mk = self.s.cnst(set.mk, ul);
523                let mk_a = self.s.mk_app_n(mk, &[alpha, r, a]);
524                let b_mk = self.s.app(beta, mk_a);
525                let all_quot = self.mk_pi(&[a], b_mk);
526                let q = self.push_local(qr, None);
527                let bq = self.s.app(beta, q);
528                let concl = self.mk_pi(&[q], bq);
529                let body = self.s.pi(all_quot, concl);
530                self.mk_pi(&[alpha, r, beta], body)
531            }
532        })
533    }
534
535    /// A quotient constant whose lift states its respect hypothesis with the equality `eq`: the
536    /// theory's own quotient, or under LOGOS's theory the one a kernel installs.
537    pub(crate) fn add_quotient(&mut self, h: Header, kind: QuotKind, eq: NameId) -> KResult<()> {
538        let name = h.name;
539        self.add_quot(h, kind, eq)
540            .map_err(|e| e.in_declaration(self.s.names.display(name)))
541    }
542
543    fn add_quot(&mut self, h: Header, kind: QuotKind, eq: NameId) -> KResult<()> {
544        let named = |set: &QuotNames| match kind {
545            QuotKind::Type => set.ty,
546            QuotKind::Ctor => set.mk,
547            QuotKind::Lift => set.lift,
548            QuotKind::Ind => set.ind,
549        };
550        let set = [Some(self.wk.quot_names()), self.wk.installed_quot]
551            .into_iter()
552            .flatten()
553            .find(|set| named(set) == h.name)
554            .ok_or_else(|| {
555                KernelFailure::reject("quotient declaration with a non-canonical name")
556            })?;
557        if kind != QuotKind::Type && !self.env.contains(set.ty) {
558            return Err(KernelFailure::reject(format!(
559                "quotient constant declared before '{}'",
560                self.s.names.display(set.ty)
561            )));
562        }
563        self.with_decl(h.lparams.clone(), Safety::Safe, |k| {
564            k.check_name(h.name, false)?;
565            k.check_duplicate_lparams(&h.lparams)?;
566            k.check_eq_type(eq)?;
567            let canonical = k.quot_canonical(kind, &h.lparams, set, eq)?;
568            if canonical != h.ty {
569                return Err(
570                    KernelFailure::reject("quotient constant has a non-canonical type")
571                        .with_detail(format!(
572                            "expected {}\n  found {}",
573                            k.show(canonical),
574                            k.show(h.ty)
575                        )),
576                );
577            }
578            Ok(())
579        })?;
580        self.env.add(Const {
581            name: h.name,
582            lparams: h.lparams,
583            ty: h.ty,
584            kind: Kind::Quot(kind),
585        });
586        if [set.ty, set.mk, set.lift, set.ind]
587            .iter()
588            .all(|n| self.env.contains(*n))
589            && !self.env.quotients.contains(&set)
590        {
591            self.env.quotients.push(set);
592        }
593        self.env.quot_init = !self.env.quotients.is_empty();
594        Ok(())
595    }
596
597    pub fn const_levels_of(&mut self, ps: &[NameId]) -> LevelsId {
598        let ls: Vec<LevelId> = ps.iter().map(|p| self.s.levels.param(*p)).collect();
599        self.s.levels.list(&ls)
600    }
601
602    pub fn is_sort(&self, e: ExprId) -> bool {
603        matches!(self.s.node(e), Node::Sort(_))
604    }
605}