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}