Skip to main content

logicaffeine_kernel/
context.rs

1//! Typing context for the kernel.
2//!
3//! A context maps variable names to their types.
4//! Used during type checking to track what variables are in scope.
5
6use crate::term::{Term, Universe};
7use std::collections::HashMap;
8use std::sync::Arc;
9
10/// Typing context: maps variable names to their types.
11///
12/// The context is immutable-by-default: `extend` creates a new context
13/// with the additional binding, preserving the original.
14///
15/// Also stores global definitions:
16/// - Inductive types (e.g., Nat : Type 0)
17/// - Constructors (e.g., Zero : Nat, Succ : Nat -> Nat)
18/// - Declarations (e.g., hypotheses like h1 : P -> Q)
19#[derive(Debug, Clone, Default)]
20pub struct Context {
21    /// Local variable bindings (from λ and Π) — the only part that grows during type
22    /// inference (one entry per enclosing binder). The context is `extend`ed (cloned) at
23    /// every binder, so the binding TYPES are shared behind `Arc`: cloning the map copies
24    /// pointers, not whole proposition types.
25    bindings: HashMap<String, Arc<Term>>,
26
27    /// The global environment below is FIXED during inference but the context is
28    /// `extend`ed (cloned) at every λ/Π. Sharing it behind `Rc` makes that clone O(1)
29    /// instead of deep-copying every premise type — the difference between linear and
30    /// quadratic checking of a large certified proof.
31    ///
32    /// Inductive type definitions: name -> sort (e.g., "Nat" -> Type 0)
33    inductives: Arc<HashMap<String, Term>>,
34
35    /// Constructor definitions: name -> (inductive_name, type)
36    constructors: Arc<HashMap<String, (String, Term)>>,
37
38    /// How many LEADING arguments of an inductive's arity are uniform PARAMETERS (as
39    /// opposed to INDICES that vary per constructor). `List (A:Type)` has 1 parameter and
40    /// 0 indices; `Eq (A) (x) : A → Prop` has 2 parameters and 1 index; `Vector (A) : Nat
41    /// → Type` has 1 parameter and 1 index. Absence means "all of the arity is
42    /// parameters" (0 indices) — so every non-indexed inductive behaves exactly as before
43    /// this map existed, and indexed elimination is a strict extension.
44    inductive_params: Arc<HashMap<String, usize>>,
45
46    /// Order of constructor registration per inductive.
47    /// HashMap doesn't preserve insertion order, so we track it explicitly.
48    constructor_order: Arc<HashMap<String, Vec<String>>>,
49
50    /// Declaration bindings (axioms/hypotheses): name -> type
51    /// Used for certifying proofs where hypotheses are assumed.
52    declarations: Arc<HashMap<String, Term>>,
53
54    /// Definition bodies: name -> (type, body)
55    /// Definitions are transparent - they unfold during normalization.
56    /// Distinguished from declarations (axioms) which have no body.
57    definitions: Arc<HashMap<String, (Term, Term)>>,
58
59    /// Hint database: theorem names marked as hints for auto tactic.
60    /// When auto fails with decision procedures, it tries to apply these hints.
61    hints: Arc<Vec<String>>,
62
63    /// Universe-polymorphic definitions (R3): name -> (universe params, type, body). A
64    /// `Term::Const { name, levels }` reference instantiates these params with `levels`.
65    universe_polys: Arc<HashMap<String, (Vec<String>, Term, Term)>>,
66
67    /// Typeclass instance database (R4): each `(type, value)` is an instance the
68    /// elaborator may resolve for an instance-implicit argument — e.g.
69    /// `(Inhabited Nat, mk Nat Zero)`. Searched by unifying `type` against the required
70    /// class type.
71    instances: Arc<Vec<(Term, Term)>>,
72
73    /// Registered COERCIONS (E1): each `(from, to, coe)` is a function `coe : from → to` the
74    /// elaborator may insert when an argument of type `from` is supplied where `to` is
75    /// expected — Lean's `Coe`/`↑`. Searched by unifying `from`/`to` against the mismatch.
76    coercions: Arc<Vec<(Term, Term, Term)>>,
77
78    /// PER-BINDER implicitness (E2): a global's parameter kinds in order — implicit,
79    /// explicit, and instance may INTERLEAVE (Lean's `BinderInfo`). Absent means the legacy
80    /// "`implicit_args` leading implicits, rest explicit" model.
81    binder_kinds: Arc<HashMap<String, Vec<crate::elaborate::ParamKind>>>,
82
83    /// How many LEADING parameters of a global are implicit (declared with `{…}` in the
84    /// surface). The surface elaborator inserts and infers that many arguments at each
85    /// application of the global, so the user writes `id 0` for `id Int 0`.
86    implicit_args: Arc<HashMap<String, usize>>,
87
88    /// Registered STRUCTURES (Rung 0c): a structure type name → its metadata. Only
89    /// these one-constructor inductives get DEFINITIONAL ETA (`p ≡ ⟨p.1, …, p.n⟩`),
90    /// keyed here so the rule is local and testable, never inferred for arbitrary
91    /// one-constructor inductives.
92    structures: Arc<HashMap<String, StructInfo>>,
93
94    /// MUTUAL inductive blocks (K3): each member name → the full ordered list of the
95    /// block's members. `Even`/`Odd`, `Tree`/`Forest` — an inductive registered
96    /// alone maps to nothing (its recursor recurses only on itself). The mutual
97    /// recursor derivation reads this to give each member a motive and to route a
98    /// recursive occurrence of a SIBLING to the sibling's fixpoint.
99    mutual_blocks: Arc<HashMap<String, Vec<String>>>,
100}
101
102/// One member of a MUTUAL inductive block: its name, arity sort, uniform parameter
103/// count, and constructors (name + full type). Constructor types may reference ANY
104/// member of the block — that is the whole point of a mutual declaration.
105#[derive(Clone, Debug)]
106pub struct MutualInductive {
107    /// The inductive's name (e.g. `Even`).
108    pub name: String,
109    /// Its arity sort (e.g. `Nat → Prop`).
110    pub sort: Term,
111    /// How many leading arity arguments are uniform parameters (the rest are indices).
112    pub num_params: usize,
113    /// Constructors: `(name, full type)`, types possibly mentioning sibling members.
114    pub constructors: Vec<(String, Term)>,
115}
116
117/// Metadata for a registered structure (record): its single constructor, how many
118/// leading type PARAMETERS it takes, and the projection function names in field order.
119#[derive(Clone, Debug)]
120pub struct StructInfo {
121    /// The constructor name (e.g. `Prod_mk`).
122    pub mk: String,
123    /// Number of leading type parameters (e.g. 2 for `Prod A B`).
124    pub num_params: usize,
125    /// Projection definition names, in field order (e.g. `[Prod_fst, Prod_snd]`).
126    pub projections: Vec<String>,
127}
128
129impl Context {
130    /// Create an empty context.
131    pub fn new() -> Self {
132        Context {
133            bindings: HashMap::new(),
134            inductives: Arc::new(HashMap::new()),
135            constructors: Arc::new(HashMap::new()),
136            inductive_params: Arc::new(HashMap::new()),
137            constructor_order: Arc::new(HashMap::new()),
138            declarations: Arc::new(HashMap::new()),
139            definitions: Arc::new(HashMap::new()),
140            hints: Arc::new(Vec::new()),
141            universe_polys: Arc::new(HashMap::new()),
142            instances: Arc::new(Vec::new()),
143            coercions: Arc::new(Vec::new()),
144            binder_kinds: Arc::new(HashMap::new()),
145            implicit_args: Arc::new(HashMap::new()),
146            structures: Arc::new(HashMap::new()),
147            mutual_blocks: Arc::new(HashMap::new()),
148        }
149    }
150
151    /// Record structure metadata (used by [`Context::add_structure`]).
152    pub fn register_struct_info(&mut self, name: &str, info: StructInfo) {
153        Arc::make_mut(&mut self.structures).insert(name.to_string(), info);
154    }
155
156    /// The structure metadata for an inductive type name, if it is a registered
157    /// structure (record). `None` for ordinary inductives — eta never fires for them.
158    pub fn struct_info(&self, name: &str) -> Option<&StructInfo> {
159        self.structures.get(name)
160    }
161
162    /// If `ctor` is the constructor of a registered structure, return `(structure
163    /// name, its info)`. Used to detect an η-expandable constructor head.
164    pub fn struct_of_constructor(&self, ctor: &str) -> Option<(&str, &StructInfo)> {
165        let ind = self.constructor_inductive(ctor)?;
166        let info = self.structures.get(ind)?;
167        Some((ind, info))
168    }
169
170    /// Record that the global `name` has `count` leading implicit parameters, so the
171    /// surface elaborator inserts that many inferred arguments at each application.
172    pub fn set_implicit_args(&mut self, name: &str, count: usize) {
173        Arc::make_mut(&mut self.implicit_args).insert(name.to_string(), count);
174    }
175
176    /// How many leading parameters of `name` are implicit (0 if none/unknown).
177    pub fn implicit_args(&self, name: &str) -> usize {
178        self.implicit_args.get(name).copied().unwrap_or(0)
179    }
180
181    /// Register a typeclass instance: a `value` of type `ty` (e.g. `mk Nat Zero` of type
182    /// `Inhabited Nat`). The elaborator resolves an instance-implicit argument by
183    /// searching these for a `ty` that unifies with the required class type.
184    pub fn add_instance(&mut self, ty: Term, value: Term) {
185        Arc::make_mut(&mut self.instances).push((ty, value));
186    }
187
188    /// Register a coercion `coe : from → to` — the elaborator may insert it when an
189    /// argument of type `from` appears where `to` is expected.
190    pub fn add_coercion(&mut self, from: Term, to: Term, coe: Term) {
191        Arc::make_mut(&mut self.coercions).push((from, to, coe));
192    }
193
194    /// All registered coercions, as `(from, to, coe)` triples.
195    pub fn coercions(&self) -> &[(Term, Term, Term)] {
196        &self.coercions
197    }
198
199    /// Record a global's per-parameter kinds (implicit/explicit/instance, in order), so the
200    /// elaborator can insert implicit and instance arguments at their real positions.
201    pub fn set_binder_kinds(&mut self, name: &str, kinds: Vec<crate::elaborate::ParamKind>) {
202        Arc::make_mut(&mut self.binder_kinds).insert(name.to_string(), kinds);
203    }
204
205    /// A global's per-parameter kinds, if recorded.
206    pub fn binder_kinds(&self, name: &str) -> Option<&[crate::elaborate::ParamKind]> {
207        self.binder_kinds.get(name).map(|v| v.as_slice())
208    }
209
210    /// All registered typeclass instances, as `(type, value)` pairs.
211    pub fn instances(&self) -> &[(Term, Term)] {
212        &self.instances
213    }
214
215    /// Register a universe-polymorphic definition `name.{params} : ty := body`. A
216    /// `Term::Const { name, levels }` reference later instantiates `params` with `levels`
217    /// (the `.{ℓ…}` syntax), so one definition is reused at every level.
218    pub fn add_universe_poly(&mut self, name: &str, params: Vec<String>, ty: Term, body: Term) {
219        Arc::make_mut(&mut self.universe_polys).insert(name.to_string(), (params, ty, body));
220    }
221
222    /// Look up a universe-polymorphic definition: `(universe params, type, body)`.
223    pub fn get_universe_poly(&self, name: &str) -> Option<&(Vec<String>, Term, Term)> {
224        self.universe_polys.get(name)
225    }
226
227    /// Add a local binding to this context (mutates in place).
228    pub fn add(&mut self, name: &str, ty: Term) {
229        self.bindings.insert(name.to_string(), Arc::new(ty));
230    }
231
232    /// Look up a local variable's type in the context.
233    pub fn get(&self, name: &str) -> Option<&Term> {
234        self.bindings.get(name).map(|t| t.as_ref())
235    }
236
237    /// Create a new context extended with an additional local binding.
238    ///
239    /// Does not mutate the original context.
240    pub fn extend(&self, name: &str, ty: Term) -> Context {
241        let mut new_ctx = self.clone();
242        new_ctx.add(name, ty);
243        new_ctx
244    }
245
246    /// Register an inductive type.
247    ///
248    /// The `sort` is the type of the inductive (e.g., Type 0 for Nat).
249    ///
250    /// All of the arity is treated as uniform parameters (0 indices) unless
251    /// [`set_inductive_params`](Self::set_inductive_params) records a smaller parameter
252    /// count — see [`add_indexed_inductive`](Self::add_indexed_inductive).
253    pub fn add_inductive(&mut self, name: &str, sort: Term) {
254        Arc::make_mut(&mut self.inductives).insert(name.to_string(), sort);
255    }
256
257    /// Register a STRUCTURE (record) `{name} (params) := {name}_mk (fields)` — a
258    /// one-constructor inductive with auto-derived projections and definitional eta.
259    ///
260    /// `params` are the leading type parameters `(A : Type 0)`, `(B : Type 0)`, …;
261    /// `fields` are `(fst : A)`, `(snd : B)`, … where a field type may reference the
262    /// params and any EARLIER field (by name). Registers:
263    /// - the inductive `{name} : Π(params). Type 0`,
264    /// - the constructor `{name}_mk : Π(params). Π(fields). {name} params`,
265    /// - a projection `{name}_{fieldᵢ} : Π(params). Π(s:{name} params). Tᵢ` for each
266    ///   field (its body a `match` on `s`), and
267    /// - the [`StructInfo`] that gates the eta rule.
268    ///
269    /// The structure lives in `Type 0` (fields over `Type 0` carriers) — the common
270    /// case for the algebraic hierarchy.
271    pub fn add_structure(
272        &mut self,
273        name: &str,
274        params: &[(&str, Term)],
275        fields: &[(&str, Term)],
276    ) {
277        let g = |s: &str| Term::Global(s.to_string());
278        let var = |s: &str| Term::Var(s.to_string());
279        // Wrap `body` in a Π / λ telescope.
280        let pis = |tele: &[(&str, Term)], body: Term| {
281            tele.iter().rev().fold(body, |acc, (p, t)| Term::Pi {
282                param: p.to_string(),
283                param_type: Box::new(t.clone()),
284                body_type: Box::new(acc),
285            })
286        };
287        let lams = |tele: &[(&str, Term)], body: Term| {
288            tele.iter().rev().fold(body, |acc, (p, t)| Term::Lambda {
289                param: p.to_string(),
290                param_type: Box::new(t.clone()),
291                body: Box::new(acc),
292            })
293        };
294        // `{name} A B …` — the structure applied to its parameter variables.
295        let s_applied = params.iter().fold(g(name), |acc, (p, _)| {
296            Term::App(Box::new(acc), Box::new(var(p)))
297        });
298
299        // 1. The inductive.
300        let ind_type = pis(params, Term::Sort(Universe::Type(0)));
301        self.add_indexed_inductive(name, ind_type, params.len());
302
303        // 2. The constructor.
304        let mk = format!("{name}_mk");
305        let ctor_type = pis(params, pis(fields, s_applied.clone()));
306        self.add_constructor(&mk, name, ctor_type);
307
308        // 3. The projections.
309        let mut proj_names = Vec::new();
310        for (i, (fname, ftype)) in fields.iter().enumerate() {
311            let proj = format!("{name}_{fname}");
312            proj_names.push(proj.clone());
313
314            // Rewrite earlier field references `f_j` (j < i) to `proj_j params disc`,
315            // for a chosen discriminant term.
316            let field_of = |disc: &Term, ty: &Term| -> Term {
317                let mut out = ty.clone();
318                for (j, (fj, _)) in fields.iter().enumerate().take(i) {
319                    let proj_j_applied = params
320                        .iter()
321                        .fold(g(&format!("{name}_{fj}")), |acc, (p, _)| {
322                            Term::App(Box::new(acc), Box::new(var(p)))
323                        });
324                    let proj_j_applied = Term::App(Box::new(proj_j_applied), Box::new(disc.clone()));
325                    out = crate::type_checker::substitute(&out, fj, &proj_j_applied);
326                }
327                out
328            };
329
330            // Projection type: Π(params). Π(s : {name} params). Tᵢ[fⱼ := projⱼ params s].
331            let ret_ty = field_of(&var("s"), ftype);
332            let proj_type = pis(
333                params,
334                Term::Pi {
335                    param: "s".to_string(),
336                    param_type: Box::new(s_applied.clone()),
337                    body_type: Box::new(ret_ty),
338                },
339            );
340
341            // Body: λparams. λ(s). match s return (λ(s✧). Tᵢ[fⱼ := projⱼ params s✧])
342            //                       with | mk => λ(fields). fieldᵢ
343            let motive = Term::Lambda {
344                param: "s✧".to_string(),
345                param_type: Box::new(s_applied.clone()),
346                body: Box::new(field_of(&var("s✧"), ftype)),
347            };
348            let case = lams(fields, var(fname));
349            let match_term = Term::Match {
350                discriminant: Box::new(var("s")),
351                motive: Box::new(motive),
352                cases: vec![case],
353            };
354            let body = lams(
355                params,
356                Term::Lambda {
357                    param: "s".to_string(),
358                    param_type: Box::new(s_applied.clone()),
359                    body: Box::new(match_term),
360                },
361            );
362            self.add_definition(proj, proj_type, body);
363        }
364
365        self.register_struct_info(
366            name,
367            StructInfo { mk, num_params: params.len(), projections: proj_names },
368        );
369    }
370
371    /// Register an INDEXED inductive: `sort` is its full arity `Π(params). Π(indices).
372    /// Sort`, of which the first `num_params` leading arguments are uniform parameters and
373    /// the rest are indices that vary per constructor (e.g. `Eq` with `num_params == 2`).
374    pub fn add_indexed_inductive(&mut self, name: &str, sort: Term, num_params: usize) {
375        self.add_inductive(name, sort);
376        self.set_inductive_params(name, num_params);
377    }
378
379    /// Record how many leading arguments of `name`'s arity are uniform parameters.
380    pub fn set_inductive_params(&mut self, name: &str, num_params: usize) {
381        Arc::make_mut(&mut self.inductive_params).insert(name.to_string(), num_params);
382    }
383
384    /// The full arity of an inductive — the number of leading `Π`s in its sort (`Nat` →
385    /// 0, `TList : Type → Type` → 1, `Eq : Type → A → A → Prop` → 3). `0` for an unknown
386    /// name.
387    pub fn inductive_arity(&self, name: &str) -> usize {
388        self.inductives.get(name).map(count_leading_pis).unwrap_or(0)
389    }
390
391    /// The EXPLICITLY declared parameter count for `name`, or `None` if the inductive was
392    /// registered without one. Reduction uses this to skip exactly the parameters of an
393    /// indexed constructor (`refl A x` → 2), falling back to a syntactic heuristic for the
394    /// legacy inductives that never declared a split — so their ι-reduction is untouched.
395    pub fn inductive_declared_params(&self, name: &str) -> Option<usize> {
396        self.inductive_params.get(name).copied()
397    }
398
399    /// How many leading arguments of `name`'s arity are uniform PARAMETERS. Defaults to
400    /// the full arity (so a non-indexed inductive is all parameters, 0 indices).
401    pub fn inductive_num_params(&self, name: &str) -> usize {
402        self.inductive_params
403            .get(name)
404            .copied()
405            .unwrap_or_else(|| self.inductive_arity(name))
406    }
407
408    /// How many trailing arguments of `name`'s arity are INDICES (arity − parameters).
409    pub fn inductive_num_indices(&self, name: &str) -> usize {
410        self.inductive_arity(name).saturating_sub(self.inductive_num_params(name))
411    }
412
413    /// Register a constructor for an inductive type.
414    ///
415    /// The `ty` is the full type of the constructor
416    /// (e.g., `Nat` for Zero, `Nat -> Nat` for Succ).
417    ///
418    /// Constructors are tracked in registration order for match expressions.
419    pub fn add_constructor(&mut self, name: &str, inductive: &str, ty: Term) {
420        Arc::make_mut(&mut self.constructors)
421            .insert(name.to_string(), (inductive.to_string(), ty));
422
423        // Track constructor order for this inductive
424        Arc::make_mut(&mut self.constructor_order)
425            .entry(inductive.to_string())
426            .or_default()
427            .push(name.to_string());
428    }
429
430    /// Add a declaration (typed assumption/hypothesis).
431    ///
432    /// Used for proof certification where hypotheses are assumed.
433    /// Example: h1 : P -> Q
434    pub fn add_declaration(&mut self, name: &str, ty: Term) {
435        Arc::make_mut(&mut self.declarations).insert(name.to_string(), ty);
436    }
437
438    /// Register a definition: name : type := body
439    ///
440    /// Definitions are transparent and unfold during normalization (delta reduction).
441    /// This distinguishes them from declarations (axioms) which have no body.
442    pub fn add_definition(&mut self, name: String, ty: Term, body: Term) {
443        Arc::make_mut(&mut self.definitions).insert(name, (ty, body));
444    }
445
446    /// Look up a global definition (inductive, constructor, definition, or declaration).
447    ///
448    /// Returns the type of the global.
449    pub fn get_global(&self, name: &str) -> Option<&Term> {
450        // Check inductives first
451        if let Some(sort) = self.inductives.get(name) {
452            return Some(sort);
453        }
454        // Check constructors
455        if let Some((_, ty)) = self.constructors.get(name) {
456            return Some(ty);
457        }
458        // Check definitions (return type, not body)
459        if let Some((ty, _)) = self.definitions.get(name) {
460            return Some(ty);
461        }
462        // Check declarations (axioms)
463        self.declarations.get(name)
464    }
465
466    /// Check if a name is a definition (has a body that can be unfolded).
467    pub fn is_definition(&self, name: &str) -> bool {
468        self.definitions.contains_key(name)
469    }
470
471    /// Get the body of a definition, if it exists.
472    ///
473    /// Returns None for axioms, constructors, and inductives (only definitions have bodies).
474    pub fn get_definition_body(&self, name: &str) -> Option<&Term> {
475        self.definitions.get(name).map(|(_, body)| body)
476    }
477
478    /// Get the type of a definition, if it exists.
479    pub fn get_definition_type(&self, name: &str) -> Option<&Term> {
480        self.definitions.get(name).map(|(ty, _)| ty)
481    }
482
483    /// Check if a name is a constructor.
484    pub fn is_constructor(&self, name: &str) -> bool {
485        self.constructors.contains_key(name)
486    }
487
488    /// Get the inductive type a constructor belongs to.
489    pub fn constructor_inductive(&self, name: &str) -> Option<&str> {
490        self.constructors.get(name).map(|(ind, _)| ind.as_str())
491    }
492
493    /// Check if a name is an inductive type.
494    pub fn is_inductive(&self, name: &str) -> bool {
495        self.inductives.contains_key(name)
496    }
497
498    /// Get all constructors for an inductive type, in registration order.
499    ///
500    /// Returns a vector of (constructor_name, constructor_type) pairs.
501    pub fn get_constructors(&self, inductive: &str) -> Vec<(&str, &Term)> {
502        self.constructor_order
503            .get(inductive)
504            .map(|names| {
505                names
506                    .iter()
507                    .filter_map(|name| {
508                        self.constructors
509                            .get(name)
510                            .map(|(_, ty)| (name.as_str(), ty))
511                    })
512                    .collect()
513            })
514            .unwrap_or_default()
515    }
516
517    /// Iterate over all declarations (hypotheses).
518    ///
519    /// Used by the certifier to find hypothesis by type.
520    pub fn iter_declarations(&self) -> impl Iterator<Item = (&str, &Term)> {
521        self.declarations.iter().map(|(k, v)| (k.as_str(), v))
522    }
523
524    /// Iterate over all definitions.
525    ///
526    /// Used by the UI to display definitions.
527    pub fn iter_definitions(&self) -> impl Iterator<Item = (&str, &Term, &Term)> {
528        self.definitions.iter().map(|(k, (ty, body))| (k.as_str(), ty, body))
529    }
530
531    /// Iterate over all inductive types.
532    ///
533    /// Used by the UI to display inductive types.
534    pub fn iter_inductives(&self) -> impl Iterator<Item = (&str, &Term)> {
535        self.inductives.iter().map(|(k, v)| (k.as_str(), v))
536    }
537
538    /// Add a constructor with strict positivity checking.
539    ///
540    /// Returns an error if the inductive type appears negatively in the
541    /// constructor type. This prevents paradoxes like:
542    /// ```text
543    /// Inductive Bad := Cons : (Bad -> False) -> Bad
544    /// ```
545    pub fn add_constructor_checked(
546        &mut self,
547        name: &str,
548        inductive: &str,
549        ty: Term,
550    ) -> crate::error::KernelResult<()> {
551        // Check strict positivity first
552        crate::positivity::check_positivity(inductive, name, &ty)?;
553        // Then the CIC universe constraint (a `Type k` inductive cannot store a field of a
554        // larger sort — the Girard/Hurkens inconsistency).
555        crate::type_checker::check_constructor_universes(self, inductive, name, &ty)?;
556
557        // If it passes, add the constructor normally
558        self.add_constructor(name, inductive, ty);
559        Ok(())
560    }
561
562    /// Register a MUTUAL block of inductives whose constructors may reference one
563    /// another (`Even`/`Odd`, `Tree`/`Forest`). Strict positivity is checked over the
564    /// WHOLE block up front — a sibling occurrence is a recursive occurrence, a
565    /// sibling in a negative position is a cross-block paradox and rejected — and the
566    /// registration is TRANSACTIONAL: if any constructor violates positivity, nothing
567    /// is added. On success every member's header (with its parameter split), every
568    /// constructor, and the block-membership registry are populated, ready for the
569    /// auto-derived mutual recursors.
570    pub fn add_mutual_inductives(
571        &mut self,
572        block: &[MutualInductive],
573    ) -> crate::error::KernelResult<()> {
574        let names: Vec<&str> = block.iter().map(|m| m.name.as_str()).collect();
575        // 1. Positivity of every constructor against the whole block, BEFORE mutating.
576        for member in block {
577            for (cname, cty) in &member.constructors {
578                crate::positivity::check_positivity_mutual(&names, cname, cty)?;
579            }
580        }
581        // 1b. UNIVERSE CONSISTENCY of every constructor — checked in a TEMP env carrying the
582        // block's headers (so a recursive/sibling field resolves), before mutating `self`,
583        // keeping the whole registration transactional.
584        {
585            let mut temp = self.clone();
586            for member in block {
587                temp.add_indexed_inductive(&member.name, member.sort.clone(), member.num_params);
588            }
589            for member in block {
590                for (cname, cty) in &member.constructors {
591                    crate::type_checker::check_constructor_universes(&temp, &member.name, cname, cty)?;
592                }
593            }
594        }
595        // 2. All headers first (so each constructor's sibling references resolve).
596        for member in block {
597            self.add_indexed_inductive(&member.name, member.sort.clone(), member.num_params);
598        }
599        // 3. All constructors.
600        for member in block {
601            for (cname, cty) in &member.constructors {
602                self.add_constructor(cname, &member.name, cty.clone());
603            }
604        }
605        // 4. Record block membership (only for a genuine block of ≥ 2 members).
606        if block.len() > 1 {
607            let members: Vec<String> = block.iter().map(|m| m.name.clone()).collect();
608            let reg = Arc::make_mut(&mut self.mutual_blocks);
609            for member in block {
610                reg.insert(member.name.clone(), members.clone());
611            }
612        }
613        Ok(())
614    }
615
616    /// The mutual block `name` belongs to (the full ordered member list), or `None`
617    /// if `name` is a standalone inductive. Used by the recursor derivation to give
618    /// every block member a motive and route sibling recursion.
619    pub fn mutual_block_of(&self, name: &str) -> Option<&[String]> {
620        self.mutual_blocks.get(name).map(|v| v.as_slice())
621    }
622
623    /// Register a NESTED inductive (`RTree := rnode : TList RTree → RTree`) by compiling
624    /// it — via the UNTRUSTED [`inductive_compile`](crate::inductive_compile) front-end —
625    /// to a mutual block plus conversion isos, then registering the block and CHECKING
626    /// every iso through the trusted kernel. A mis-compiled sibling is caught by mutual
627    /// positivity; a mis-typed iso is caught here (its inferred type must match its
628    /// declared conversion type). Soundness rests entirely on those trusted checks — the
629    /// compiler adds no trusted code, exactly as Lean lowers nested inductives to mutual.
630    pub fn add_nested_inductive(
631        &mut self,
632        decl: &crate::inductive_compile::NestedDecl,
633    ) -> crate::error::KernelResult<crate::inductive_compile::NestedInfo> {
634        let compiled = crate::inductive_compile::compile_nested(self, decl)?;
635        // The mutual block is checked by the trusted mutual machinery (block positivity).
636        self.add_mutual_inductives(&compiled.block)?;
637        // Each iso is KERNEL-CHECKED before it is trusted: infer its type and require it
638        // be the declared conversion type. A wrong iso is rejected here.
639        for (name, ty, body) in &compiled.isos {
640            let inferred = crate::infer_type(self, body)?;
641            if !crate::is_subtype(self, &inferred, ty) || !crate::is_subtype(self, ty, &inferred) {
642                return Err(crate::error::KernelError::CertificationError(format!(
643                    "nested-compile: iso '{name}' inferred type {inferred} ≠ declared {ty}"
644                )));
645            }
646            self.add_definition(name.clone(), ty.clone(), body.clone());
647        }
648        Ok(crate::inductive_compile::NestedInfo {
649            siblings: compiled.siblings,
650            isos: compiled.iso_names,
651        })
652    }
653
654    /// Register a theorem as a hint for the auto tactic.
655    ///
656    /// Hints are theorems that auto will try to apply when decision
657    /// procedures fail. This allows auto to "learn" from proven theorems.
658    pub fn add_hint(&mut self, name: &str) {
659        if !self.hints.contains(&name.to_string()) {
660            Arc::make_mut(&mut self.hints).push(name.to_string());
661        }
662    }
663
664    /// Get all registered hints.
665    ///
666    /// Returns the names of theorems registered as hints.
667    pub fn get_hints(&self) -> &[String] {
668        &self.hints
669    }
670
671    /// Check if a theorem is registered as a hint.
672    pub fn is_hint(&self, name: &str) -> bool {
673        self.hints.contains(&name.to_string())
674    }
675}
676
677/// Count the leading `Π`s of a term — an inductive's arity, or a constructor's parameter
678/// count.
679fn count_leading_pis(t: &Term) -> usize {
680    let mut n = 0;
681    let mut cur = t;
682    while let Term::Pi { body_type, .. } = cur {
683        n += 1;
684        cur = body_type;
685    }
686    n
687}