Skip to main content

logicaffeine_kernel/dag/
logos_env.rs

1//! A LOGOS context replayed into the core: every declaration, in dependency order, admitted by
2//! [`Theory::Logos`](super::tc::Theory::Logos)'s checks, with the core's verdict for each.
3//!
4//! A [`Context`] keeps its declarations in maps with no order, so the order is the dependency
5//! order of the globals each declaration names: the strongly connected components of that graph,
6//! dependencies first. A component of several inductive types is one mutual block; any other
7//! cycle is reported, never broken.
8//!
9//! Inductive types are admitted LOGOS's way, without generated recursors (`match` eliminates), and
10//! with Lean's checks on each constructor: the arity is a type ending in a sort; a constructor's
11//! type is a type whose parameter binders are the family's, whose fields live in sorts at most the
12//! family's unless the family is a proposition, and whose result is the family applied to exactly
13//! its parameters and then indices that do not mention the block; it takes the family's universe
14//! parameters in the family's order; and strict positivity holds (the `Term` kernel's own check,
15//! `positivity::check_positivity_mutual_resolved`). The block's recursive and reflexive shape and
16//! its irrelevant universe parameters are recorded. Definitions, theorems and axioms go through
17//! the core's ordinary declaration checks; the certified quotient is installed whole.
18
19use super::check::{Decl, DefnDecl, Header};
20use super::env::{Const, CtorInfo, Hint, InductiveInfo, Kind, QuotKind, Safety};
21use super::error::{KResult, KernelFailure};
22use super::expr::{ExprId, Node};
23use super::level::LevelId;
24use super::logos::{explicit_levels, lower};
25use super::name::NameId;
26use super::tc::Kernel;
27use crate::context::{Context, Registration};
28use crate::quotient::PreludeKind;
29use crate::term::{Literal, Term};
30use std::collections::{BTreeMap, BTreeSet};
31use std::rc::Rc;
32
33/// One declaration's verdict in the replay.
34#[derive(Clone, Debug)]
35pub struct Replayed {
36    pub name: String,
37    pub kind: &'static str,
38    pub verdict: Result<(), String>,
39}
40
41/// A declaration as the core admits it, in the `Term` forms a [`Context`] registers. `params` are
42/// its universe parameters, in the order its constants take levels.
43#[derive(Clone, Debug)]
44pub enum Declaration {
45    /// One inductive family of a block: its arity (parameters included), how many of the arity's
46    /// binders are parameters, and each constructor's full type.
47    Inductive {
48        name: String,
49        params: Vec<String>,
50        arity: Term,
51        num_params: usize,
52        ctors: Vec<(String, Term)>,
53    },
54    Definition {
55        name: String,
56        params: Vec<String>,
57        ty: Term,
58        body: Term,
59    },
60    Axiom {
61        name: String,
62        params: Vec<String>,
63        ty: Term,
64    },
65    /// The quotient: its four primitives in the order the core admits them, installed together
66    /// as Lean's `init_quot` installs them, so a declaration that uses any of them sees the whole
67    /// quotient and its computation rule. Each is checked against its canonical signature.
68    Quotient {
69        primitives: Vec<(QuotKind, String, Vec<String>, Term)>,
70        equality: String,
71    },
72    /// The certified observational equality's constants (`crate::observational`), axioms to the
73    /// core and computing by `logos_obs`, admitted together after `after`: every name the
74    /// certificate rests on, which its rules' answers are built from.
75    Observational {
76        constants: Vec<(String, Vec<String>, Term)>,
77        after: Vec<String>,
78    },
79}
80
81impl Declaration {
82    fn name(&self) -> &str {
83        match self {
84            Declaration::Inductive { name, .. }
85            | Declaration::Definition { name, .. }
86            | Declaration::Axiom { name, .. } => name,
87            Declaration::Quotient { primitives, .. } => &primitives[0].1,
88            Declaration::Observational { constants, .. } => &constants[0].0,
89        }
90    }
91
92    /// Every global the item declares, constructors and quotient primitives included.
93    pub(crate) fn names(&self) -> Vec<&str> {
94        match self {
95            Declaration::Inductive { name, ctors, .. } => std::iter::once(name.as_str())
96                .chain(ctors.iter().map(|(c, _)| c.as_str()))
97                .collect(),
98            Declaration::Quotient { primitives, .. } => {
99                primitives.iter().map(|p| p.1.as_str()).collect()
100            }
101            Declaration::Observational { constants, .. } => {
102                constants.iter().map(|c| c.0.as_str()).collect()
103            }
104            _ => vec![self.name()],
105        }
106    }
107
108    fn kind(&self) -> &'static str {
109        match self {
110            Declaration::Inductive { .. } => "inductive",
111            Declaration::Definition { .. } => "definition",
112            Declaration::Axiom { .. } => "axiom",
113            Declaration::Quotient { .. } => "quotient",
114            Declaration::Observational { .. } => "observational",
115        }
116    }
117
118    pub(crate) fn terms(&self) -> Vec<&Term> {
119        match self {
120            Declaration::Inductive { arity, ctors, .. } => std::iter::once(arity)
121                .chain(ctors.iter().map(|(_, t)| t))
122                .collect(),
123            Declaration::Definition { ty, body, .. } => vec![ty, body],
124            Declaration::Axiom { ty, .. } => vec![ty],
125            Declaration::Quotient { primitives, .. } => primitives.iter().map(|p| &p.3).collect(),
126            Declaration::Observational { constants, .. } => {
127                constants.iter().map(|c| &c.2).collect()
128            }
129        }
130    }
131}
132
133/// Replays every declaration of `ctx` into a [`Kernel::logos`] kernel.
134pub fn replay(ctx: &Context) -> (Kernel, Vec<Replayed>) {
135    let (k, _, verdicts) = replay_items(ctx);
136    (k, verdicts)
137}
138
139/// [`replay`], with the declarations it read.
140pub(crate) fn replay_items(ctx: &Context) -> (Kernel, Vec<Declaration>, Vec<Replayed>) {
141    let items = collect(ctx);
142    let mut k = Kernel::logos(ctx);
143    let verdicts = admit_items(&mut k, ctx, &items);
144    (k, items, verdicts)
145}
146
147/// Admits `items` into `k` in dependency order (each may depend on what `k` holds already): the
148/// core's verdict on each declared name, a family by its own name, the quotient by each primitive.
149pub(crate) fn admit_items(k: &mut Kernel, ctx: &Context, items: &[Declaration]) -> Vec<Replayed> {
150    let mut out = Vec::new();
151    for component in order(ctx, items) {
152        let members: Vec<&Declaration> = component.iter().map(|i| &items[*i]).collect();
153        let verdict = admit_block(k, ctx, &members).map_err(|e| e.to_string());
154        for m in &members {
155            let names = match m {
156                Declaration::Quotient { .. } | Declaration::Observational { .. } => m.names(),
157                _ => vec![m.name()],
158            };
159            for name in names {
160                out.push(Replayed {
161                    name: name.to_string(),
162                    kind: m.kind(),
163                    verdict: verdict.clone(),
164                });
165            }
166        }
167    }
168    out
169}
170
171/// Admits one declaration, or one mutual block of inductive families, into a kernel built by
172/// [`Kernel::logos`] over `ctx`: the core's verdict on it.
173pub fn admit(k: &mut Kernel, ctx: &Context, block: &[Declaration]) -> Result<(), String> {
174    let members: Vec<&Declaration> = block.iter().collect();
175    admit_block(k, ctx, &members).map_err(|e| e.to_string())
176}
177
178fn admit_block(k: &mut Kernel, ctx: &Context, members: &[&Declaration]) -> KResult<()> {
179    if members.is_empty() {
180        return Ok(());
181    }
182    if members.len() > 1
183        && !members
184            .iter()
185            .all(|m| matches!(m, Declaration::Inductive { .. }))
186    {
187        return Err(KernelFailure::reject(
188            "part of a dependency cycle that is not a mutual inductive block",
189        ));
190    }
191    match members[0] {
192        Declaration::Inductive { .. } => admit_inductives(k, ctx, members),
193        Declaration::Definition {
194            name,
195            params,
196            ty,
197            body,
198        } => admit_definition(k, ctx, name, params, ty, body),
199        Declaration::Axiom { name, params, ty } => admit_axiom(k, ctx, name, params, ty),
200        Declaration::Quotient {
201            primitives,
202            equality,
203        } => admit_quotient(k, ctx, primitives, equality),
204        Declaration::Observational { constants, .. } => constants
205            .iter()
206            .try_for_each(|(name, params, ty)| admit_axiom(k, ctx, name, params, ty)),
207    }
208}
209
210/// A declaration's universe parameters: its explicit level telescope, or else the level
211/// variables its type mentions, which the `Term` kernel reads as implicitly bound by it
212/// (`type_checker::lookup_const` returns such a type with its level variables free).
213fn level_params(ctx: &Context, name: &str, ty: &Term) -> Vec<String> {
214    match super::logos::level_telescope(ctx, name) {
215        Some((ps, _)) => ps,
216        None => crate::term::free_level_vars(ty),
217    }
218}
219
220fn collect(ctx: &Context) -> Vec<Declaration> {
221    let names: BTreeSet<&str> = ctx
222        .iter_inductives()
223        .map(|(n, _)| n)
224        .chain(ctx.iter_definitions().map(|(n, _, _)| n))
225        .chain(ctx.iter_universe_polys().map(|(n, ..)| n))
226        .chain(ctx.iter_declarations().map(|(n, _)| n))
227        .collect();
228    let mut items: BTreeMap<String, Declaration> = names
229        .into_iter()
230        .filter_map(|n| Some((n.to_string(), item(ctx, n)?)))
231        .collect();
232    group_quotient(ctx, &mut items);
233    group_observational(ctx, &mut items);
234    items.into_values().collect()
235}
236
237/// The declaration `name` is registered as in `ctx` ([`Context::registration`]).
238pub(crate) fn item(ctx: &Context, name: &str) -> Option<Declaration> {
239    Some(match ctx.registration(name)? {
240        Registration::Inductive(arity) => Declaration::Inductive {
241            name: name.to_string(),
242            params: level_params(ctx, name, arity),
243            arity: arity.clone(),
244            num_params: ctx.inductive_num_params(name),
245            ctors: ctx
246                .get_constructors(name)
247                .into_iter()
248                .map(|(c, t)| (c.to_string(), t.clone()))
249                .collect(),
250        },
251        Registration::Definition(ty, body) => Declaration::Definition {
252            name: name.to_string(),
253            params: level_params(ctx, name, ty),
254            ty: ty.clone(),
255            body: body.clone(),
256        },
257        Registration::UniversePoly(params, ty, body) => Declaration::Definition {
258            name: name.to_string(),
259            params: params.to_vec(),
260            ty: ty.clone(),
261            body: body.clone(),
262        },
263        Registration::Axiom(ty) => Declaration::Axiom {
264            name: name.to_string(),
265            params: level_params(ctx, name, ty),
266            ty: ty.clone(),
267        },
268    })
269}
270
271/// The certified quotient's primitives, by name, with the kind each is certified as.
272const QUOTIENT: [(&str, PreludeKind, QuotKind); 4] = [
273    ("Quot", PreludeKind::Type, QuotKind::Type),
274    ("Quot_mk", PreludeKind::Constructor, QuotKind::Ctor),
275    ("Quot_lift", PreludeKind::Lift, QuotKind::Lift),
276    ("Quot_ind", PreludeKind::Induction, QuotKind::Ind),
277];
278
279/// The quotient a kernel installs for content imported from Lean, by the names `quotient::Kind`
280/// certifies.
281const INSTALLED_QUOTIENT: [(crate::quotient::Kind, QuotKind); 4] = [
282    (crate::quotient::Kind::Type, QuotKind::Type),
283    (crate::quotient::Kind::Constructor, QuotKind::Ctor),
284    (crate::quotient::Kind::Lift, QuotKind::Lift),
285    (crate::quotient::Kind::Induction, QuotKind::Ind),
286];
287
288/// Each certified quotient's four primitives, when all of them are among `items` as axioms,
289/// become one [`Declaration::Quotient`]: the standard library's, and the one a kernel installs.
290fn group_quotient(ctx: &Context, items: &mut BTreeMap<String, Declaration>) {
291    let prelude: Vec<(&str, QuotKind)> = QUOTIENT
292        .iter()
293        .filter(|(name, kind, _)| crate::quotient::prelude_kind(ctx, name) == Some(*kind))
294        .map(|(name, _, kind)| (*name, *kind))
295        .collect();
296    let installed_equality = ctx
297        .quotient_primitives
298        .get(crate::quotient::Kind::Lift.name())
299        .and_then(|c| c.equality.clone());
300    let installed: Vec<(&str, QuotKind)> = INSTALLED_QUOTIENT
301        .iter()
302        .filter(|(kind, _)| {
303            ctx.quotient_primitives
304                .get(kind.name())
305                .is_some_and(|c| c.kind == *kind)
306        })
307        .map(|(kind, quot)| (kind.name(), *quot))
308        .collect();
309    for (set, equality) in [
310        (prelude, Some("Eq".to_string())),
311        (installed, installed_equality),
312    ] {
313        let whole = set.len() == 4
314            && set
315                .iter()
316                .all(|(name, _)| matches!(items.get(*name), Some(Declaration::Axiom { .. })));
317        let Some(equality) = equality.filter(|_| whole) else {
318            continue;
319        };
320        let primitives = set
321            .iter()
322            .map(|(name, kind)| match items.remove(*name) {
323                Some(Declaration::Axiom { name, params, ty }) => (*kind, name, params, ty),
324                _ => unreachable!("certified above"),
325            })
326            .collect();
327        items.insert(
328            set[0].0.to_string(),
329            Declaration::Quotient {
330                primitives,
331                equality,
332            },
333        );
334    }
335}
336
337/// The certified observational constants, when all of them are among `items` as axioms, become
338/// one [`Declaration::Observational`].
339fn group_observational(ctx: &Context, items: &mut BTreeMap<String, Declaration>) {
340    let Some(certificate) = ctx.observational.as_ref() else {
341        return;
342    };
343    if !certificate
344        .names()
345        .all(|n| matches!(items.get(n), Some(Declaration::Axiom { .. })))
346    {
347        return;
348    }
349    let constants: Vec<(String, Vec<String>, Term)> = certificate
350        .names()
351        .map(|n| match items.remove(n) {
352            Some(Declaration::Axiom { name, params, ty }) => (name, params, ty),
353            _ => unreachable!("an axiom above"),
354        })
355        .collect();
356    let after = certificate
357        .dependencies
358        .iter()
359        .filter(|d| !constants.iter().any(|c| &c.0 == *d))
360        .cloned()
361        .collect();
362    items.insert(
363        constants[0].0.clone(),
364        Declaration::Observational { constants, after },
365    );
366}
367
368/// The items registered under `names` in `ctx`, the certified quotient and observational
369/// equality each grouped as one.
370pub(crate) fn items_named(ctx: &Context, names: &BTreeSet<String>) -> Vec<Declaration> {
371    let mut items: BTreeMap<String, Declaration> = names
372        .iter()
373        .filter_map(|n| Some((n.clone(), item(ctx, n)?)))
374        .collect();
375    group_quotient(ctx, &mut items);
376    group_observational(ctx, &mut items);
377    items.into_values().collect()
378}
379
380fn globals(t: &Term, out: &mut BTreeSet<String>) {
381    match t {
382        Term::Global(n) | Term::Const { name: n, .. } => {
383            out.insert(n.clone());
384        }
385        Term::Pi {
386            param_type,
387            body_type: body,
388            ..
389        }
390        | Term::Lambda {
391            param_type, body, ..
392        } => {
393            globals(param_type, out);
394            globals(body, out);
395        }
396        Term::App(f, a) => {
397            globals(f, out);
398            globals(a, out);
399        }
400        Term::Match {
401            discriminant,
402            motive,
403            cases,
404        } => {
405            globals(discriminant, out);
406            globals(motive, out);
407            cases.iter().for_each(|c| globals(c, out));
408        }
409        Term::Fix { body, .. } => globals(body, out),
410        Term::MutualFix { defs, .. } => defs.iter().for_each(|(_, b)| globals(b, out)),
411        Term::Let {
412            ty, value, body, ..
413        } => {
414            globals(ty, out);
415            globals(value, out);
416            globals(body, out);
417        }
418        Term::Annotated { term, .. } => globals(term, out),
419        Term::Lit(lit) => {
420            out.insert(carrier(lit).to_string());
421        }
422        Term::Sort(_) | Term::Var(_) | Term::Hole => {}
423    }
424}
425
426/// The family a literal's type is (`logos_tc`'s typing of literals): a declaration holding the
427/// literal depends on it.
428fn carrier(lit: &Literal) -> &'static str {
429    match lit {
430        Literal::Int(_) | Literal::BigInt(_) => "Int",
431        Literal::Nat(_) => "Nat",
432        Literal::Float(_) => "Float",
433        Literal::Text(_) => "Text",
434        Literal::Duration(_) => "Duration",
435        Literal::Date(_) => "Date",
436        Literal::Moment(_) => "Moment",
437    }
438}
439
440/// Every global `item` names, a literal naming its carrier, and for the observational constants
441/// every name their rules' answers are built from.
442pub(crate) fn item_globals(item: &Declaration) -> BTreeSet<String> {
443    let mut names = BTreeSet::new();
444    item.terms()
445        .into_iter()
446        .for_each(|t| globals(t, &mut names));
447    if let Declaration::Observational { after, .. } = item {
448        names.extend(after.iter().cloned());
449    }
450    names
451}
452
453/// The items' strongly connected components, each listed after every component it depends on.
454fn order(ctx: &Context, items: &[Declaration]) -> Vec<Vec<usize>> {
455    let mut owner: BTreeMap<&str, usize> = BTreeMap::new();
456    for (i, item) in items.iter().enumerate() {
457        for name in item.names() {
458            owner.insert(name, i);
459        }
460    }
461    let globals: Vec<BTreeSet<String>> = items.iter().map(item_globals).collect();
462    let mut edges: Vec<Vec<usize>> = globals
463        .iter()
464        .enumerate()
465        .map(|(i, names)| {
466            names
467                .iter()
468                .filter_map(|n| owner.get(n.as_str()).copied())
469                .filter(|j| *j != i)
470                .collect()
471        })
472        .collect();
473    // An item that names a registered structure's constructor also waits for the structure's
474    // projections, which structure η (`Kernel::logos_eta_struct`) reads to compare a constructor
475    // application with a value of the structure. A projection that already depends on the item
476    // gets no edge back.
477    for (i, names) in globals.iter().enumerate() {
478        let mut projections: Vec<usize> = names
479            .iter()
480            .filter_map(|n| ctx.struct_of_constructor(n))
481            .flat_map(|(_, info)| info.projections.iter())
482            .filter_map(|p| owner.get(p.as_str()).copied())
483            .filter(|j| *j != i)
484            .collect();
485        projections.sort_unstable();
486        projections.dedup();
487        for j in projections {
488            if !edges[i].contains(&j) && !reaches(&edges, j, i) {
489                edges[i].push(j);
490            }
491        }
492    }
493    tarjan(&edges)
494}
495
496/// Whether `to` is reachable from `from` along `edges`.
497fn reaches(edges: &[Vec<usize>], from: usize, to: usize) -> bool {
498    let mut seen = vec![false; edges.len()];
499    let mut work = vec![from];
500    while let Some(i) = work.pop() {
501        if i == to {
502            return true;
503        }
504        if !std::mem::replace(&mut seen[i], true) {
505            work.extend(edges[i].iter().copied());
506        }
507    }
508    false
509}
510
511/// Tarjan's algorithm, iterative: components in reverse topological order of the condensation,
512/// which with edges pointing at dependencies is dependencies first.
513fn tarjan(edges: &[Vec<usize>]) -> Vec<Vec<usize>> {
514    let n = edges.len();
515    let (mut index, mut low, mut on) = (vec![usize::MAX; n], vec![0; n], vec![false; n]);
516    let (mut stack, mut out, mut next) = (Vec::new(), Vec::new(), 0);
517    for root in 0..n {
518        if index[root] != usize::MAX {
519            continue;
520        }
521        let mut work: Vec<(usize, usize)> = vec![(root, 0)];
522        while let Some(&mut (v, ref mut e)) = work.last_mut() {
523            if *e == 0 && index[v] == usize::MAX {
524                index[v] = next;
525                low[v] = next;
526                next += 1;
527                stack.push(v);
528                on[v] = true;
529            }
530            if *e < edges[v].len() {
531                let w = edges[v][*e];
532                *e += 1;
533                if index[w] == usize::MAX {
534                    work.push((w, 0));
535                } else if on[w] {
536                    low[v] = low[v].min(index[w]);
537                }
538                continue;
539            }
540            work.pop();
541            if let Some(&(parent, _)) = work.last() {
542                low[parent] = low[parent].min(low[v]);
543            }
544            if low[v] == index[v] {
545                let mut component = Vec::new();
546                while let Some(w) = stack.pop() {
547                    on[w] = false;
548                    component.push(w);
549                    if w == v {
550                        break;
551                    }
552                }
553                component.sort_unstable();
554                out.push(component);
555            }
556        }
557    }
558    out
559}
560
561/// The core's verdict on `block`, a candidate declaration, in `k`, admitting nothing: a definition or
562/// an axiom gets every check its admission makes; an inductive block is admitted into a clone of
563/// `k`, discarded after.
564#[cfg(feature = "shadow")]
565pub(crate) fn judge(k: &mut Kernel, ctx: &Context, block: &[Declaration]) -> Result<(), String> {
566    let verdict = match block {
567        [Declaration::Definition {
568            name,
569            params,
570            ty,
571            body,
572        }] => judge_definition(k, ctx, name, params, ty, body),
573        [Declaration::Axiom { name, params, ty }] => judge_axiom(k, ctx, name, params, ty),
574        _ => admit_block(&mut k.clone(), ctx, &block.iter().collect::<Vec<_>>()),
575    };
576    verdict.map_err(|e| e.to_string())
577}
578
579#[cfg(feature = "shadow")]
580fn judge_definition(
581    k: &mut Kernel,
582    ctx: &Context,
583    name: &str,
584    params: &[String],
585    ty: &Term,
586    body: &Term,
587) -> KResult<()> {
588    let header = Header {
589        name: name_id(k, name),
590        lparams: lparams(k, params),
591        ty: lowered(k, ctx, ty)?,
592    };
593    let value = lowered(k, ctx, body)?;
594    let height = definition_height(k, value);
595    k.check_without_admitting(&Decl::Defn(DefnDecl {
596        header,
597        value,
598        hint: Hint::Regular(height),
599        safety: Safety::Safe,
600    }))
601}
602
603#[cfg(feature = "shadow")]
604fn judge_axiom(
605    k: &mut Kernel,
606    ctx: &Context,
607    name: &str,
608    params: &[String],
609    ty: &Term,
610) -> KResult<()> {
611    let header = Header {
612        name: name_id(k, name),
613        lparams: lparams(k, params),
614        ty: lowered(k, ctx, ty)?,
615    };
616    k.check_without_admitting(&Decl::Axiom {
617        header,
618        is_unsafe: false,
619    })
620}
621
622/// The definition a door of `ctx` puts to the core: its terms as `ctx` stores them
623/// (`Context::stored_term`) and the telescope the door gives it, the stated one or the level
624/// variables of the stored type and then of the body (`term`'s rules 1 and 4).
625pub fn candidate_definition(
626    ctx: &Context,
627    name: &str,
628    levels: Option<&[String]>,
629    ty: &Term,
630    body: &Term,
631) -> Declaration {
632    let (ty, body) = (ctx.stored_term(ty.clone()), ctx.stored_term(body.clone()));
633    let params = match levels {
634        Some(levels) => levels.to_vec(),
635        None => crate::term::definition_telescope(&ty, &body)
636            .unwrap_or_else(|| crate::term::free_level_vars(&ty)),
637    };
638    Declaration::Definition {
639        name: name.to_string(),
640        params,
641        ty,
642        body,
643    }
644}
645
646/// The axiom a door of `ctx` puts to the core, its type stored and its levels read as
647/// [`candidate_definition`] reads them.
648pub fn candidate_axiom(
649    ctx: &Context,
650    name: &str,
651    levels: Option<&[String]>,
652    ty: &Term,
653) -> Declaration {
654    let ty = ctx.stored_term(ty.clone());
655    Declaration::Axiom {
656        name: name.to_string(),
657        params: levels.map_or_else(|| crate::term::free_level_vars(&ty), <[String]>::to_vec),
658        ty,
659    }
660}
661
662/// The block of inductive families a door of `ctx` puts to the core. Each arity is stored as
663/// `ctx` stores it, and each member takes the stated telescope or its stored arity's level
664/// variables. A constructor's bare reference to a member stands at that member's telescope, and
665/// one to anything else at the level `ctx` gives it.
666pub fn candidate_block(
667    ctx: &Context,
668    levels: Option<&[String]>,
669    block: &[crate::context::MutualInductive],
670) -> Vec<Declaration> {
671    let arities: Vec<Term> = block
672        .iter()
673        .map(|m| ctx.stored_term(m.sort.clone()))
674        .collect();
675    let telescopes: Vec<Vec<String>> = arities
676        .iter()
677        .map(|arity| levels.map_or_else(|| crate::term::free_level_vars(arity), <[String]>::to_vec))
678        .collect();
679    let own_levels = |name: &str| match block.iter().position(|m| m.name == name) {
680        Some(i) => (!telescopes[i].is_empty()).then(|| telescopes[i].clone()),
681        None => ctx.own_levels(name),
682    };
683    block
684        .iter()
685        .zip(arities)
686        .zip(&telescopes)
687        .map(|((m, arity), params)| Declaration::Inductive {
688            name: m.name.clone(),
689            params: params.clone(),
690            arity,
691            num_params: m.num_params,
692            ctors: m
693                .constructors
694                .iter()
695                .map(|(c, t)| {
696                    let mut t = crate::annotation::stored(t.clone());
697                    crate::term::bare_references_at_own_levels(&mut t, &own_levels);
698                    (c.clone(), t)
699                })
700                .collect(),
701        })
702        .collect()
703}
704
705fn lowered(k: &mut Kernel, ctx: &Context, t: &Term) -> KResult<ExprId> {
706    lower(&mut k.s, &explicit_levels(ctx, t))
707        .map_err(|e| KernelFailure::reject(format!("no core term: {e:?}")))
708}
709
710fn lparams(k: &mut Kernel, params: &[String]) -> Rc<[NameId]> {
711    params
712        .iter()
713        .map(|p| k.s.names.str(NameId::ANON, p))
714        .collect::<Vec<_>>()
715        .into()
716}
717
718fn name_id(k: &mut Kernel, name: &str) -> NameId {
719    k.s.names.str(NameId::ANON, name)
720}
721
722fn admit_axiom(
723    k: &mut Kernel,
724    ctx: &Context,
725    name: &str,
726    params: &[String],
727    ty: &Term,
728) -> KResult<()> {
729    let header = Header {
730        name: name_id(k, name),
731        lparams: lparams(k, params),
732        ty: lowered(k, ctx, ty)?,
733    };
734    k.add_decl(Decl::Axiom {
735        header,
736        is_unsafe: false,
737    })
738}
739
740/// The certified quotient, admitted as the core's own: each primitive checked against its
741/// canonical signature, and the four installed before anything that uses one of them.
742fn admit_quotient(
743    k: &mut Kernel,
744    ctx: &Context,
745    primitives: &[(QuotKind, String, Vec<String>, Term)],
746    equality: &str,
747) -> KResult<()> {
748    let eq = name_id(k, equality);
749    for (kind, name, params, ty) in primitives {
750        let header = Header {
751            name: name_id(k, name),
752            lparams: lparams(k, params),
753            ty: lowered(k, ctx, ty)?,
754        };
755        k.add_quotient(header, *kind, eq)?;
756    }
757    Ok(())
758}
759
760fn admit_definition(
761    k: &mut Kernel,
762    ctx: &Context,
763    name: &str,
764    params: &[String],
765    ty: &Term,
766    body: &Term,
767) -> KResult<()> {
768    let header = Header {
769        name: name_id(k, name),
770        lparams: lparams(k, params),
771        ty: lowered(k, ctx, ty)?,
772    };
773    let value = lowered(k, ctx, body)?;
774    let height = definition_height(k, value);
775    k.add_decl(Decl::Defn(DefnDecl {
776        header,
777        value,
778        hint: Hint::Regular(height),
779        safety: Safety::Safe,
780    }))
781}
782
783/// One more than the tallest definition `value` names (Lean's definitional height).
784fn definition_height(k: &Kernel, value: ExprId) -> u32 {
785    let mut h = 0;
786    k.s.any_node(value, &mut |s, x| {
787        if let Node::Const(n, _) = s.node(x) {
788            if let Some(c) = k.env.get(n) {
789                if let Hint::Regular(d) = c.hint() {
790                    h = h.max(d);
791                }
792            }
793        }
794        false
795    });
796    h + 1
797}
798
799/// A type's Π-telescope opened onto fresh locals, and the type it ends in.
800fn telescope(k: &mut Kernel, mut ty: ExprId) -> KResult<(Vec<ExprId>, ExprId)> {
801    let mut locals = Vec::new();
802    loop {
803        let w = k.whnf(ty)?;
804        match k.s.node(w) {
805            Node::Pi(a, b) => {
806                let x = k.push_local(a, None);
807                locals.push(x);
808                ty = k.s.instantiate1(b, x);
809            }
810            _ => return Ok((locals, w)),
811        }
812    }
813}
814
815fn admit_inductives(k: &mut Kernel, ctx: &Context, block: &[&Declaration]) -> KResult<()> {
816    let names: Vec<&str> = block.iter().map(|i| i.name()).collect();
817    let mut types = Vec::new();
818    for item in block {
819        let Declaration::Inductive {
820            name,
821            params,
822            arity,
823            num_params,
824            ctors,
825        } = item
826        else {
827            unreachable!()
828        };
829        let n = name_id(k, name);
830        let lps = lparams(k, params);
831        k.check_name(n, true)?;
832        let ty = lowered(k, ctx, arity)?;
833        let level = k.with_decl(lps.clone(), Safety::Safe, |k| {
834            let s = k.check(ty)?;
835            k.ensure_sort(s)?;
836            let (locals, end) = telescope(k, ty)?;
837            match k.s.node(end) {
838                Node::Sort(l) => Ok((l, locals.len() as u32)),
839                _ => Err(KernelFailure::reject(format!(
840                    "the arity of '{name}' does not end in a sort"
841                ))),
842            }
843        })?;
844        types.push((
845            n,
846            lps,
847            ty,
848            level,
849            *num_params as u32,
850            ctors.clone(),
851            params.clone(),
852        ));
853    }
854    for (n, lps, ty, (_, arity_len), num_params, ..) in &types {
855        let info = InductiveInfo {
856            num_params: *num_params,
857            num_indices: arity_len - num_params,
858            all: types.iter().map(|t| t.0).collect::<Vec<_>>().into(),
859            ctors: Vec::new().into(),
860            num_nested: 0,
861            is_rec: false,
862            is_reflexive: false,
863            is_unsafe: false,
864            rec: k.rec_name(*n),
865        };
866        k.env.add_provisional(Const {
867            name: *n,
868            lparams: lps.clone(),
869            ty: *ty,
870            kind: Kind::Induct(info),
871        });
872    }
873    let members: BTreeSet<NameId> = types.iter().map(|t| t.0).collect();
874    let mut finals = Vec::new();
875    let result = (|| -> KResult<()> {
876        for (n, lps, ind_ty, (level, arity_len), num_params, ctors, family_levels) in &types {
877            let mut ctor_ids = Vec::new();
878            let (mut is_rec, mut is_reflexive) = (false, false);
879            for (cidx, (cname, cty)) in ctors.iter().enumerate() {
880                let c = name_id(k, cname);
881                k.check_name(c, true)?;
882                // A constructor the context registers takes the telescope it was registered with; a
883                // candidate's constructor, not in the context yet, takes its block's.
884                let own_levels = super::logos::level_telescope(ctx, cname)
885                    .map_or_else(|| family_levels.clone(), |(ps, _)| ps);
886                if own_levels != *family_levels {
887                    return Err(KernelFailure::reject(format!(
888                        "constructor '{cname}' takes the universe parameters {own_levels:?}, its family {family_levels:?}"
889                    )));
890                }
891                crate::positivity::check_positivity_mutual_resolved(ctx, &names, cname, cty)
892                    .map_err(|e| KernelFailure::reject(e.to_string()))?;
893                let ty = lowered(k, ctx, cty)?;
894                let shape = k.with_decl(lps.clone(), Safety::Safe, |k| {
895                    check_constructor(
896                        k,
897                        cname,
898                        *n,
899                        lps,
900                        *ind_ty,
901                        *level,
902                        *num_params,
903                        *arity_len,
904                        ty,
905                        &members,
906                    )
907                })?;
908                is_rec |= shape.recursive;
909                is_reflexive |= shape.reflexive;
910                ctor_ids.push(c);
911                finals.push(Const {
912                    name: c,
913                    lparams: lps.clone(),
914                    ty,
915                    kind: Kind::Ctor(CtorInfo {
916                        induct: *n,
917                        cidx: cidx as u32,
918                        num_params: *num_params,
919                        num_fields: shape.fields,
920                        is_unsafe: false,
921                    }),
922                });
923            }
924            let provisional = k.env.get(*n).expect("provisional").clone();
925            let Kind::Induct(mut info) = provisional.kind.clone() else {
926                unreachable!()
927            };
928            info.ctors = ctor_ids.into();
929            info.is_rec = is_rec;
930            info.is_reflexive = is_reflexive;
931            finals.push(Const {
932                name: *n,
933                lparams: lps.clone(),
934                ty: provisional.ty,
935                kind: Kind::Induct(info),
936            });
937        }
938        Ok(())
939    })();
940    for (n, ..) in &types {
941        k.env.remove(*n);
942    }
943    result?;
944    let irrelevant = irrelevant_levels(k, &finals);
945    for c in finals {
946        if let Some(mask) = irrelevant.get(&c.name) {
947            k.record_irrelevant_levels(c.name, *mask);
948        }
949        k.env.add(c);
950    }
951    Ok(())
952}
953
954/// What a constructor's check found: its field count, and whether a field mentions the block
955/// (recursive) or does so as a function's result (reflexive).
956struct CtorShape {
957    fields: u32,
958    recursive: bool,
959    reflexive: bool,
960}
961
962/// Lean's constructor check, over LOGOS's universes: the constructor's parameter binders are the
963/// family's, each field lives in a sort at most the family's unless the family is a proposition,
964/// and the result is the family applied to exactly its parameters and then its indices.
965#[allow(clippy::too_many_arguments)]
966fn check_constructor(
967    k: &mut Kernel,
968    cname: &str,
969    family: NameId,
970    lps: &Rc<[NameId]>,
971    family_ty: ExprId,
972    level: LevelId,
973    num_params: u32,
974    arity_len: u32,
975    ty: ExprId,
976    members: &BTreeSet<NameId>,
977) -> KResult<CtorShape> {
978    let reject = |what: String| KernelFailure::reject(format!("constructor '{cname}' {what}"));
979    let s = k.check(ty)?;
980    k.ensure_sort(s)?;
981    let mut arity = family_ty;
982    let mut params = Vec::with_capacity(num_params as usize);
983    let mut cur = ty;
984    for i in 0..num_params {
985        let aw = k.whnf(arity)?;
986        let Node::Pi(param_ty, arity_body) = k.s.node(aw) else {
987            return Err(reject("outruns its family's parameters".into()));
988        };
989        let cw = k.whnf(cur)?;
990        let Node::Pi(dom, body) = k.s.node(cw) else {
991            return Err(reject(format!(
992                "has fewer than its family's {num_params} parameters"
993            )));
994        };
995        if !k.is_def_eq(dom, param_ty)? {
996            return Err(reject(format!(
997                "takes its parameter {i} at a type other than its family's"
998            )));
999        }
1000        let p = k.push_local(param_ty, None);
1001        params.push(p);
1002        arity = k.s.instantiate1(arity_body, p);
1003        cur = k.s.instantiate1(body, p);
1004    }
1005    let mentions_block = |k: &Kernel, e: ExprId| {
1006        k.s.any_node(
1007            e,
1008            &mut |s, x| matches!(s.node(x), Node::Const(m, _) if members.contains(&m)),
1009        )
1010    };
1011    let mut shape = CtorShape {
1012        fields: 0,
1013        recursive: false,
1014        reflexive: false,
1015    };
1016    loop {
1017        let cw = k.whnf(cur)?;
1018        let Node::Pi(dom, body) = k.s.node(cw) else {
1019            cur = cw;
1020            break;
1021        };
1022        shape.fields += 1;
1023        if mentions_block(k, dom) {
1024            shape.recursive = true;
1025            let dw = k.whnf(dom)?;
1026            shape.reflexive |= matches!(k.s.node(dw), Node::Pi(..));
1027        }
1028        if level != LevelId::ZERO && !k.s.levels.is_sprop(level) {
1029            let fs = k.infer(dom, true)?;
1030            let fs = k.ensure_sort(fs)?;
1031            let fl = k.sort_level(fs);
1032            if !k.s.levels.leq(fl, level) {
1033                return Err(KernelFailure::reject(format!(
1034                    "universe inconsistency: constructor '{cname}' stores an argument in a sort above its inductive's"
1035                )));
1036            }
1037        }
1038        let x = k.push_local(dom, None);
1039        cur = k.s.instantiate1(body, x);
1040    }
1041    let (head, args) = k.s.app_args(cur);
1042    let identity = k.const_levels_of(lps);
1043    let returns_family =
1044        matches!(k.s.node(head), Node::Const(m, ls) if m == family && ls == identity);
1045    if !returns_family || args.len() != arity_len as usize || args[..params.len()] != params[..] {
1046        return Err(reject(format!(
1047            "must return its family applied to its parameters and then {} indices, not {}",
1048            arity_len - num_params,
1049            k.show(cur)
1050        )));
1051    }
1052    for index in &args[params.len()..] {
1053        if mentions_block(k, *index) {
1054            return Err(reject(
1055                "mentions its family in an index of its result".into(),
1056            ));
1057        }
1058    }
1059    Ok(shape)
1060}
1061
1062/// The universe parameters of a block's inductives that occur nowhere past their parameters —
1063/// neither in an arity's indices or sort nor in a constructor's fields or result indices — as a
1064/// mask over each constant's level list. The block's own recursive references at their declared
1065/// instance do not count. Such a parameter is irrelevant in the sense of cumulative inductive
1066/// types (Timany–Sozeau): with the parameters applied, the instances it ranges over denote one
1067/// family.
1068fn irrelevant_levels(k: &mut Kernel, block: &[Const]) -> BTreeMap<NameId, u64> {
1069    let members: BTreeSet<NameId> = block
1070        .iter()
1071        .filter(|c| matches!(c.kind, Kind::Induct(_)))
1072        .map(|c| c.name)
1073        .collect();
1074    let mut declared = BTreeMap::new();
1075    for c in block.iter().filter(|c| members.contains(&c.name)) {
1076        let ls = k.const_levels_of(&c.lparams);
1077        declared.insert(c.name, ls);
1078    }
1079    let mut mentioned: BTreeSet<NameId> = BTreeSet::new();
1080    for c in block {
1081        let num_params = match &c.kind {
1082            Kind::Induct(info) => info.num_params,
1083            Kind::Ctor(ci) => ci.num_params,
1084            _ => continue,
1085        };
1086        let mut rest = c.ty;
1087        for _ in 0..num_params {
1088            match k.s.node(rest) {
1089                Node::Pi(_, b) => rest = b,
1090                _ => break,
1091            }
1092        }
1093        for p in c.lparams.iter() {
1094            let s = &k.s;
1095            if s.any_node(rest, &mut |s, x| match s.node(x) {
1096                Node::Sort(l) => s.levels.mentions_param(l, *p),
1097                Node::Const(n, ls) if declared.get(&n) != Some(&ls) => s
1098                    .levels
1099                    .get_list(ls)
1100                    .iter()
1101                    .any(|l| s.levels.mentions_param(*l, *p)),
1102                _ => false,
1103            }) {
1104                mentioned.insert(*p);
1105            }
1106        }
1107    }
1108    block
1109        .iter()
1110        .map(|c| {
1111            let mask = c
1112                .lparams
1113                .iter()
1114                .enumerate()
1115                .filter(|(i, p)| *i < 64 && !mentioned.contains(p))
1116                .fold(0u64, |m, (i, _)| m | 1 << i);
1117            (c.name, mask)
1118        })
1119        .collect()
1120}