1use crate::declaration::{
41 ConstantInfo, ConstantVal, ConstructorVal, InductiveVal, RecursorRule, RecursorVal,
42};
43use crate::expr_util::{get_app_fn, get_app_fn_args, mk_app};
44use crate::infer::LocalDecl;
45use crate::instantiate::instantiate_type_lparams;
46use crate::level::{is_equivalent, is_geq, normalize as normalize_level};
47use crate::subst::instantiate;
48use crate::Node;
49use crate::{
50 BinderInfo, Environment, Expr, FVarId, KernelError, Level, LevelView, Name, TypeChecker,
51};
52use std::rc::Rc;
53
54#[derive(Clone, Debug)]
56pub struct InductiveSpec {
57 pub name: Name,
59 pub ty: Expr,
61 pub ctors: Vec<(Name, Expr)>,
64 pub rec_name: Option<Name>,
66}
67
68impl InductiveSpec {
69 pub fn new(name: Name, ty: Expr, ctors: Vec<(Name, Expr)>) -> Self {
71 Self {
72 name,
73 ty,
74 ctors,
75 rec_name: None,
76 }
77 }
78 pub fn with_rec_name(mut self, rec_name: Name) -> Self {
80 self.rec_name = Some(rec_name);
81 self
82 }
83 fn effective_rec_name(&self) -> Name {
84 self.rec_name
85 .clone()
86 .unwrap_or_else(|| Name::mk_str(self.name.clone(), "rec"))
87 }
88}
89
90#[derive(Clone, Debug)]
92pub struct DerivedFamily {
93 pub inductives: Vec<ConstantInfo>,
95 pub constructors: Vec<ConstantInfo>,
97 pub recursors: Vec<ConstantInfo>,
99}
100
101impl DerivedFamily {
102 pub fn into_constant_infos(self) -> Vec<ConstantInfo> {
104 let mut out = self.inductives;
105 out.extend(self.constructors);
106 out.extend(self.recursors);
107 out
108 }
109}
110
111const DERIVE_FVAR_BASE: u64 = 1 << 40;
118
119#[derive(Clone, Debug)]
120struct Binder {
121 fvar: FVarId,
122 bi: BinderInfo,
123 name: Name,
124 ty: Expr,
125}
126
127fn fv(b: &Binder) -> Expr {
128 Expr::FVar(b.fvar)
129}
130
131struct DeriveCtx<'e> {
136 tc: Option<TypeChecker<'e>>,
137 next: u64,
138}
139
140impl<'e> DeriveCtx<'e> {
141 fn checked(env: &'e Environment) -> Self {
142 Self {
143 tc: Some(TypeChecker::new(env)),
144 next: 0,
145 }
146 }
147 fn unchecked() -> Self {
148 Self { tc: None, next: 0 }
149 }
150 fn is_checked(&self) -> bool {
151 self.tc.is_some()
152 }
153 fn fresh(&mut self, bi: BinderInfo, name: Name, ty: Expr) -> Binder {
154 let id = FVarId(DERIVE_FVAR_BASE + self.next);
155 self.next += 1;
156 if let Some(tc) = self.tc.as_mut() {
157 tc.push_local(LocalDecl {
158 fvar: id,
159 name: name.clone(),
160 ty: ty.clone(),
161 val: None,
162 });
163 }
164 Binder {
165 fvar: id,
166 bi,
167 name,
168 ty,
169 }
170 }
171 fn whnf(&mut self, e: &Expr) -> Expr {
172 match self.tc.as_mut() {
173 Some(tc) => tc.whnf(e),
174 None => e.clone(),
175 }
176 }
177 fn is_def_eq(&mut self, a: &Expr, b: &Expr) -> bool {
178 match self.tc.as_mut() {
179 Some(tc) => tc.is_def_eq(a, b),
180 None => a == b,
181 }
182 }
183 #[allow(clippy::result_large_err)]
184 fn ensure_sort(&mut self, e: &Expr) -> Result<Option<Level>, KernelError> {
185 match self.tc.as_mut() {
186 Some(tc) => tc.ensure_sort(e).map(Some),
187 None => Ok(None),
188 }
189 }
190}
191
192fn abstract_fvar(expr: &Expr, fvar: FVarId, depth: u32) -> Expr {
201 match expr {
202 Expr::FVar(id) if *id == fvar => Expr::BVar(depth),
203 Expr::BVar(_) | Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => {
204 expr.clone()
205 }
206 Expr::App(f, a) => Expr::App(
207 Node::new(abstract_fvar(f, fvar, depth)),
208 Node::new(abstract_fvar(a, fvar, depth)),
209 ),
210 Expr::Lam(bi, n, ty, body) => Expr::Lam(
211 *bi,
212 n.clone(),
213 Node::new(abstract_fvar(ty, fvar, depth)),
214 Node::new(abstract_fvar(body, fvar, depth + 1)),
215 ),
216 Expr::Pi(bi, n, ty, body) => Expr::Pi(
217 *bi,
218 n.clone(),
219 Node::new(abstract_fvar(ty, fvar, depth)),
220 Node::new(abstract_fvar(body, fvar, depth + 1)),
221 ),
222 Expr::Let(n, ty, val, body) => Expr::Let(
223 n.clone(),
224 Node::new(abstract_fvar(ty, fvar, depth)),
225 Node::new(abstract_fvar(val, fvar, depth)),
226 Node::new(abstract_fvar(body, fvar, depth + 1)),
227 ),
228 Expr::Proj(n, i, e) => Expr::Proj(n.clone(), *i, Node::new(abstract_fvar(e, fvar, depth))),
229 }
230}
231
232fn close(binders: &[Binder], body: &Expr, lam: bool, bi_override: Option<BinderInfo>) -> Expr {
237 let mut r = body.clone();
238 for b in binders.iter().rev() {
239 let inner = abstract_fvar(&r, b.fvar, 0);
240 let bi = bi_override.unwrap_or(b.bi);
241 r = if lam {
242 Expr::Lam(
243 bi,
244 b.name.clone(),
245 Node::new(b.ty.clone()),
246 Node::new(inner),
247 )
248 } else {
249 Expr::Pi(
250 bi,
251 b.name.clone(),
252 Node::new(b.ty.clone()),
253 Node::new(inner),
254 )
255 };
256 }
257 r
258}
259
260pub(crate) fn has_const_occ(e: &Expr, names: &[Name]) -> bool {
265 match e {
266 Expr::Const(n, _) => names.contains(n),
267 Expr::BVar(_) | Expr::FVar(_) | Expr::Sort(_) | Expr::Lit(_) => false,
268 Expr::App(f, a) => has_const_occ(f, names) || has_const_occ(a, names),
269 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
270 has_const_occ(ty, names) || has_const_occ(body, names)
271 }
272 Expr::Let(_, ty, val, body) => {
273 has_const_occ(ty, names) || has_const_occ(val, names) || has_const_occ(body, names)
274 }
275 Expr::Proj(_, _, e) => has_const_occ(e, names),
276 }
277}
278
279fn collect_level_param_names(l: &Level, out: &mut Vec<Name>) {
280 match l.view() {
281 LevelView::Zero => {}
282 LevelView::Succ(a) => collect_level_param_names(a, out),
283 LevelView::Max(a, b) | LevelView::IMax(a, b) => {
284 collect_level_param_names(a, out);
285 collect_level_param_names(b, out);
286 }
287 LevelView::Param(n) => {
288 if !out.contains(n) {
289 out.push(n.clone());
290 }
291 }
292 LevelView::MVar(_) => {}
293 }
294}
295
296fn collect_expr_level_params(e: &Expr, out: &mut Vec<Name>) {
297 match e {
298 Expr::Sort(l) => collect_level_param_names(l, out),
299 Expr::Const(_, ls) => {
300 for l in ls {
301 collect_level_param_names(l, out);
302 }
303 }
304 Expr::BVar(_) | Expr::FVar(_) | Expr::Lit(_) => {}
305 Expr::App(f, a) => {
306 collect_expr_level_params(f, out);
307 collect_expr_level_params(a, out);
308 }
309 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
310 collect_expr_level_params(ty, out);
311 collect_expr_level_params(body, out);
312 }
313 Expr::Let(_, ty, val, body) => {
314 collect_expr_level_params(ty, out);
315 collect_expr_level_params(val, out);
316 collect_expr_level_params(body, out);
317 }
318 Expr::Proj(_, _, e) => collect_expr_level_params(e, out),
319 }
320}
321
322#[allow(clippy::result_large_err)]
323fn check_level_params_declared(
324 e: &Expr,
325 lparams: &[Name],
326 what: &str,
327 name: &Name,
328) -> Result<(), KernelError> {
329 let mut used = Vec::new();
330 collect_expr_level_params(e, &mut used);
331 for u in used {
332 if !lparams.contains(&u) {
333 return Err(KernelError::InvalidInductive(format!(
334 "{} '{}' uses undeclared universe parameter '{}'",
335 what, name, u
336 )));
337 }
338 }
339 Ok(())
340}
341
342fn peel_all(ctx: &mut DeriveCtx<'_>, ty: &Expr) -> (Vec<Binder>, Expr) {
349 let mut out = Vec::new();
350 let mut cur = ty.clone();
351 loop {
352 let curw = if matches!(cur, Expr::Pi(_, _, _, _)) {
353 cur
354 } else {
355 let w = ctx.whnf(&cur);
356 if !matches!(w, Expr::Pi(_, _, _, _)) {
357 return (out, w);
358 }
359 w
360 };
361 if let Expr::Pi(bi, name, dom, body) = curw {
362 let b = ctx.fresh(bi, name, (*dom).clone());
363 cur = instantiate(&body, &fv(&b));
364 out.push(b);
365 } else {
366 return (out, curw);
367 }
368 }
369}
370
371#[allow(clippy::result_large_err)]
375fn peel_against_params(
376 ctx: &mut DeriveCtx<'_>,
377 ty: &Expr,
378 params: &[Binder],
379 what: &str,
380 name: &Name,
381) -> Result<Expr, KernelError> {
382 let mut cur = ty.clone();
383 for (i, p) in params.iter().enumerate() {
384 let curw = if matches!(cur, Expr::Pi(_, _, _, _)) {
385 cur
386 } else {
387 ctx.whnf(&cur)
388 };
389 match curw {
390 Expr::Pi(_, _, dom, body) => {
391 if ctx.is_checked() && !ctx.is_def_eq(&dom, &p.ty) {
392 return Err(KernelError::InvalidInductive(format!(
393 "{} '{}': parameter #{} does not match the family parameter type",
394 what,
395 name,
396 i + 1
397 )));
398 }
399 cur = instantiate(&body, &fv(p));
400 }
401 _ => {
402 return Err(KernelError::InvalidInductive(format!(
403 "{} '{}' does not bind the {} family parameter(s)",
404 what,
405 name,
406 params.len()
407 )));
408 }
409 }
410 }
411 Ok(cur)
412}
413
414struct FamilyPre {
419 lparams: Vec<Name>,
420 ind_levels: Vec<Level>,
421 np: usize,
422 params: Vec<Binder>,
423 names: Vec<Name>,
424 indices: Vec<Vec<Binder>>,
427 ind_level: Level,
429}
430
431#[allow(clippy::result_large_err)]
432fn build_family_pre(
433 ctx: &mut DeriveCtx<'_>,
434 lparams: &[Name],
435 num_params: u32,
436 types: &[(Name, Expr)],
437) -> Result<FamilyPre, KernelError> {
438 if types.is_empty() {
439 return Err(KernelError::InvalidInductive(
440 "inductive family must contain at least one type".to_string(),
441 ));
442 }
443 for (i, lp) in lparams.iter().enumerate() {
444 if lparams[..i].contains(lp) {
445 return Err(KernelError::InvalidInductive(format!(
446 "duplicate universe parameter '{}'",
447 lp
448 )));
449 }
450 }
451 for (i, (n, _)) in types.iter().enumerate() {
452 if types[..i].iter().any(|(m, _)| m == n) {
453 return Err(KernelError::InvalidInductive(format!(
454 "duplicate inductive type name '{}'",
455 n
456 )));
457 }
458 }
459 let np = num_params as usize;
460 let ind_levels: Vec<Level> = lparams.iter().cloned().map(Level::param).collect();
461
462 for (n, ty) in types {
463 check_level_params_declared(ty, lparams, "inductive type", n)?;
464 if ctx.is_checked() {
465 ctx.ensure_sort(ty)?;
466 }
467 }
468
469 let mut params = Vec::new();
471 let mut cur = types[0].1.clone();
472 for i in 0..np {
473 let curw = if matches!(cur, Expr::Pi(_, _, _, _)) {
474 cur
475 } else {
476 ctx.whnf(&cur)
477 };
478 match curw {
479 Expr::Pi(bi, name, dom, body) => {
480 let b = ctx.fresh(bi, name, (*dom).clone());
481 cur = instantiate(&body, &fv(&b));
482 params.push(b);
483 }
484 _ => {
485 return Err(KernelError::InvalidInductive(format!(
486 "inductive type '{}' has fewer than {} parameter binders (got {})",
487 types[0].0, np, i
488 )));
489 }
490 }
491 }
492
493 let mut names = Vec::new();
494 let mut indices = Vec::new();
495 let mut ind_level: Option<Level> = None;
496 for (j, (n, ty)) in types.iter().enumerate() {
497 let rest = if j == 0 {
498 cur.clone()
499 } else {
500 peel_against_params(ctx, ty, ¶ms, "inductive type", n)?
501 };
502 let (idx_binders, cod) = peel_all(ctx, &rest);
503 let codw = ctx.whnf(&cod);
504 let level = match codw {
505 Expr::Sort(l) => l,
506 other => {
507 return Err(KernelError::InvalidInductive(format!(
508 "inductive type '{}' must end in a sort, found '{}'",
509 n, other
510 )));
511 }
512 };
513 match &ind_level {
514 None => ind_level = Some(level),
515 Some(l0) => {
516 if !is_equivalent(l0, &level) {
517 return Err(KernelError::InvalidInductive(format!(
518 "mutually inductive type '{}' lives in a different universe than '{}'",
519 n, types[0].0
520 )));
521 }
522 }
523 }
524 names.push(n.clone());
525 indices.push(idx_binders);
526 }
527 let ind_level = normalize_level(&ind_level.unwrap_or_else(Level::zero));
529 Ok(FamilyPre {
530 lparams: lparams.to_vec(),
531 ind_levels,
532 np,
533 params,
534 names,
535 indices,
536 ind_level,
537 })
538}
539
540fn as_valid_ind_app(pre: &FamilyPre, e: &Expr, strict: bool) -> Option<(usize, Vec<Expr>)> {
543 let (head, args) = get_app_fn_args(e);
544 let (n, levels) = match head {
545 Expr::Const(n, levels) => (n, levels),
546 _ => return None,
547 };
548 let j = pre.names.iter().position(|x| x == n)?;
549 if strict {
550 if levels.len() != pre.ind_levels.len()
551 || !levels
552 .iter()
553 .zip(&pre.ind_levels)
554 .all(|(a, b)| is_equivalent(a, b))
555 {
556 return None;
557 }
558 if args.len() != pre.np + pre.indices[j].len() {
559 return None;
560 }
561 for (arg, p) in args.iter().zip(&pre.params) {
562 if **arg != Expr::FVar(p.fvar) {
563 return None;
564 }
565 }
566 }
567 let idx = args.iter().skip(pre.np).map(|e| (*e).clone()).collect();
568 Some((j, idx))
569}
570
571struct RecFieldSig {
576 ds: Vec<Binder>,
578 type_idx: usize,
580 index_args: Vec<Expr>,
582}
583
584struct FieldSig {
585 binder: Binder,
586 sort_level: Option<Level>,
588 rec: Option<RecFieldSig>,
589}
590
591struct CtorSig {
592 name: Name,
593 ty: Expr,
594 fields: Vec<FieldSig>,
595 ret_index_args: Vec<Expr>,
597}
598
599#[allow(clippy::result_large_err)]
604fn check_positivity(
605 ctx: &mut DeriveCtx<'_>,
606 pre: &FamilyPre,
607 ty: &Expr,
608 ctor_name: &Name,
609 decl_name: &Name,
610) -> Result<(), KernelError> {
611 let t = ctx.whnf(ty);
612 if !has_const_occ(&t, &pre.names) {
613 return Ok(());
614 }
615 match t {
616 Expr::Pi(bi, name, dom, body) => {
617 if has_const_occ(&dom, &pre.names) {
618 return Err(KernelError::InvalidInductive(format!(
619 "non-strictly-positive occurrence of '{}' in constructor '{}'",
620 decl_name, ctor_name
621 )));
622 }
623 let b = ctx.fresh(bi, name, (*dom).clone());
624 check_positivity(ctx, pre, &instantiate(&body, &fv(&b)), ctor_name, decl_name)
625 }
626 _ => {
627 if as_valid_ind_app(pre, &t, true).is_some() {
628 return Ok(());
629 }
630 match get_app_fn(&t) {
631 Expr::Const(c, _) if !pre.names.contains(c) => {
632 Err(KernelError::UnsupportedNestedInductive(decl_name.clone()))
633 }
634 _ => Err(KernelError::InvalidInductive(format!(
635 "invalid occurrence of '{}' in constructor '{}' (recursive occurrences \
636 must be applied to exactly the family parameters)",
637 decl_name, ctor_name
638 ))),
639 }
640 }
641 }
642}
643
644fn classify_rec_field(
647 ctx: &mut DeriveCtx<'_>,
648 pre: &FamilyPre,
649 field_ty: &Expr,
650) -> Option<RecFieldSig> {
651 if !has_const_occ(field_ty, &pre.names) {
652 return None;
653 }
654 let (ds, cod) = peel_all(ctx, field_ty);
655 let codw = ctx.whnf(&cod);
656 let strict = ctx.is_checked();
657 let (type_idx, index_args) = as_valid_ind_app(pre, &codw, strict)?;
658 Some(RecFieldSig {
659 ds,
660 type_idx,
661 index_args,
662 })
663}
664
665#[allow(clippy::result_large_err)]
666fn check_ctor(
667 ctx: &mut DeriveCtx<'_>,
668 pre: &FamilyPre,
669 type_idx: usize,
670 cname: &Name,
671 cty: &Expr,
672) -> Result<CtorSig, KernelError> {
673 check_level_params_declared(cty, &pre.lparams, "constructor", cname)?;
674 if ctx.is_checked() {
675 ctx.ensure_sort(cty)?;
677 }
678 let decl_name = &pre.names[type_idx];
679 let rest = peel_against_params(ctx, cty, &pre.params, "constructor", cname)?;
680 let (field_binders, cod) = peel_all(ctx, &rest);
681
682 let mut fields = Vec::with_capacity(field_binders.len());
683 for b in field_binders {
684 let sort_level = ctx.ensure_sort(&b.ty)?.map(|l| normalize_level(&l));
687 if let Some(sl) = &sort_level {
688 if !pre.ind_level.is_zero() && !is_geq(&pre.ind_level, sl) {
691 return Err(KernelError::InvalidInductive(format!(
692 "universe level of field '{}' in constructor '{}' is too big \
693 for inductive type '{}'",
694 b.name, cname, decl_name
695 )));
696 }
697 }
698 if ctx.is_checked() {
699 check_positivity(ctx, pre, &b.ty, cname, decl_name)?;
700 }
701 let rec = classify_rec_field(ctx, pre, &b.ty);
702 fields.push(FieldSig {
703 binder: b,
704 sort_level,
705 rec,
706 });
707 }
708
709 let codw = ctx.whnf(&cod);
710 let ret_index_args = if ctx.is_checked() {
711 match as_valid_ind_app(pre, &codw, true) {
712 Some((j, idx)) if j == type_idx => idx,
713 _ => {
714 return Err(KernelError::InvalidInductive(format!(
715 "constructor '{}' must return '{}' applied to exactly the family \
716 parameters followed by index terms",
717 cname, decl_name
718 )));
719 }
720 }
721 } else {
722 match as_valid_ind_app(pre, &codw, false) {
723 Some((j, idx)) if j == type_idx => idx,
724 _ => {
725 return Err(KernelError::InvalidInductive(format!(
726 "constructor '{}' does not return inductive type '{}'",
727 cname, decl_name
728 )));
729 }
730 }
731 };
732
733 Ok(CtorSig {
734 name: cname.clone(),
735 ty: cty.clone(),
736 fields,
737 ret_index_args,
738 })
739}
740
741fn is_large_eliminating(pre: &FamilyPre, ctors: &[Vec<CtorSig>]) -> bool {
749 if pre.ind_level.is_not_zero() {
750 return true;
752 }
753 if pre.names.len() > 1 {
754 return false;
755 }
756 let cs = &ctors[0];
757 match cs.len() {
758 0 => true, 1 => {
760 let c = &cs[0];
763 c.fields.iter().all(|f| {
764 let field_is_prop = matches!(&f.sort_level, Some(l) if l.is_zero());
765 field_is_prop || c.ret_index_args.contains(&Expr::FVar(f.binder.fvar))
766 })
767 }
768 _ => false,
769 }
770}
771
772fn compute_k_target(pre: &FamilyPre, ctors: &[Vec<CtorSig>]) -> bool {
776 pre.names.len() == 1
777 && pre.ind_level.is_zero()
778 && ctors[0].len() == 1
779 && ctors[0][0].fields.is_empty()
780}
781
782fn mk_elim_level_name(lparams: &[Name]) -> Name {
784 let mut cand = Name::str("u");
785 let mut i = 1u32;
786 while lparams.contains(&cand) {
787 cand = Name::str(format!("u_{}", i));
788 i += 1;
789 }
790 cand
791}
792
793fn last_component_name(n: &Name, fallback: &str) -> Name {
798 Name::str(n.last_str().unwrap_or(fallback).to_string())
799}
800
801#[allow(clippy::result_large_err)]
802fn derive_family_core(
803 ctx: &mut DeriveCtx<'_>,
804 pre: &FamilyPre,
805 specs: &[InductiveSpec],
806 ctors: Vec<Vec<CtorSig>>,
807) -> Result<DerivedFamily, KernelError> {
808 let k = pre.names.len();
809 let total_ctors: usize = ctors.iter().map(|c| c.len()).sum();
810 let rec_names: Vec<Name> = specs.iter().map(|s| s.effective_rec_name()).collect();
811
812 let large_elim = is_large_eliminating(pre, &ctors);
813 let k_target = compute_k_target(pre, &ctors);
814 let (rec_lparams, elim_level) = if large_elim {
815 let fresh = mk_elim_level_name(&pre.lparams);
816 let mut lp = Vec::with_capacity(pre.lparams.len() + 1);
817 lp.push(fresh.clone());
818 lp.extend(pre.lparams.iter().cloned());
819 (lp, Level::param(fresh))
820 } else {
821 (pre.lparams.clone(), Level::zero())
822 };
823 let rec_levels: Vec<Level> = rec_lparams.iter().cloned().map(Level::param).collect();
824 let param_exprs: Vec<Expr> = pre.params.iter().map(fv).collect();
825
826 let mut motives: Vec<Binder> = Vec::with_capacity(k);
828 for j in 0..k {
829 let mut t_args = param_exprs.clone();
830 t_args.extend(pre.indices[j].iter().map(fv));
831 let t_ty = mk_app(
832 Expr::Const(pre.names[j].clone(), pre.ind_levels.clone()),
833 &t_args,
834 );
835 let x = ctx.fresh(BinderInfo::Default, Name::str("t"), t_ty);
836 let mut dom = pre.indices[j].clone();
837 dom.push(x);
838 let motive_ty = close(
839 &dom,
840 &Expr::Sort(elim_level.clone()),
841 false,
842 Some(BinderInfo::Default),
843 );
844 let mname = if k == 1 {
845 Name::str("motive")
846 } else {
847 Name::str(format!("motive_{}", j + 1))
848 };
849 motives.push(ctx.fresh(BinderInfo::Implicit, mname, motive_ty));
850 }
851 let motive_exprs: Vec<Expr> = motives.iter().map(fv).collect();
852
853 struct MinorPlan {
857 type_idx: usize,
858 ctor_idx: usize,
859 minor: Binder,
860 }
861 let mut minors: Vec<MinorPlan> = Vec::with_capacity(total_ctors);
862 for (j, cs) in ctors.iter().enumerate() {
863 for (ci, c) in cs.iter().enumerate() {
864 let mut ih_binders: Vec<Binder> = Vec::new();
865 for f in &c.fields {
866 if let Some(rf) = &f.rec {
867 let ds_exprs: Vec<Expr> = rf.ds.iter().map(fv).collect();
868 let applied_field = mk_app(fv(&f.binder), &ds_exprs);
869 let mut m_args = rf.index_args.clone();
870 m_args.push(applied_field);
871 let ih_body = mk_app(fv(&motives[rf.type_idx]), &m_args);
872 let ih_ty = close(&rf.ds, &ih_body, false, None);
873 let ih_name =
874 Name::str(format!("{}_ih", f.binder.name.last_str().unwrap_or("a")));
875 ih_binders.push(ctx.fresh(BinderInfo::Default, ih_name, ih_ty));
876 }
877 }
878 let mut ctor_args = param_exprs.clone();
879 ctor_args.extend(c.fields.iter().map(|f| fv(&f.binder)));
880 let ctor_app = mk_app(
881 Expr::Const(c.name.clone(), pre.ind_levels.clone()),
882 &ctor_args,
883 );
884 let mut concl_args = c.ret_index_args.clone();
885 concl_args.push(ctor_app);
886 let concl = mk_app(fv(&motives[j]), &concl_args);
887 let mut tele: Vec<Binder> = c.fields.iter().map(|f| f.binder.clone()).collect();
888 tele.extend(ih_binders);
889 let minor_ty = close(&tele, &concl, false, None);
890 let minor = ctx.fresh(
891 BinderInfo::Default,
892 last_component_name(&c.name, "minor"),
893 minor_ty,
894 );
895 minors.push(MinorPlan {
896 type_idx: j,
897 ctor_idx: ci,
898 minor,
899 });
900 }
901 }
902 let minor_binders: Vec<Binder> = minors.iter().map(|m| m.minor.clone()).collect();
903 let minor_exprs: Vec<Expr> = minor_binders.iter().map(fv).collect();
904
905 let mut recursors = Vec::with_capacity(k);
907 for j in 0..k {
908 let mut t_args = param_exprs.clone();
910 t_args.extend(pre.indices[j].iter().map(fv));
911 let major_ty = mk_app(
912 Expr::Const(pre.names[j].clone(), pre.ind_levels.clone()),
913 &t_args,
914 );
915 let major = ctx.fresh(BinderInfo::Default, Name::str("t"), major_ty);
916 let mut concl_args: Vec<Expr> = pre.indices[j].iter().map(fv).collect();
917 concl_args.push(fv(&major));
918 let concl = mk_app(fv(&motives[j]), &concl_args);
919
920 let mut rec_ty = close(std::slice::from_ref(&major), &concl, false, None);
921 rec_ty = close(&pre.indices[j], &rec_ty, false, Some(BinderInfo::Implicit));
922 rec_ty = close(&minor_binders, &rec_ty, false, Some(BinderInfo::Default));
923 rec_ty = close(&motives, &rec_ty, false, Some(BinderInfo::Implicit));
924 rec_ty = close(&pre.params, &rec_ty, false, Some(BinderInfo::Implicit));
925
926 let mut rules = Vec::with_capacity(ctors[j].len());
929 for plan in minors.iter().filter(|m| m.type_idx == j) {
930 let c = &ctors[j][plan.ctor_idx];
931 let mut body_args: Vec<Expr> = c.fields.iter().map(|f| fv(&f.binder)).collect();
932 for f in &c.fields {
933 if let Some(rf) = &f.rec {
934 let ds_exprs: Vec<Expr> = rf.ds.iter().map(fv).collect();
935 let mut rec_args = param_exprs.clone();
936 rec_args.extend(motive_exprs.iter().cloned());
937 rec_args.extend(minor_exprs.iter().cloned());
938 rec_args.extend(rf.index_args.iter().cloned());
939 rec_args.push(mk_app(fv(&f.binder), &ds_exprs));
940 let rec_call = mk_app(
941 Expr::Const(rec_names[rf.type_idx].clone(), rec_levels.clone()),
942 &rec_args,
943 );
944 body_args.push(close(&rf.ds, &rec_call, true, Some(BinderInfo::Default)));
945 }
946 }
947 let body = mk_app(fv(&plan.minor), &body_args);
948 let field_binders: Vec<Binder> = c.fields.iter().map(|f| f.binder.clone()).collect();
949 let mut rhs = close(&field_binders, &body, true, Some(BinderInfo::Default));
950 rhs = close(&minor_binders, &rhs, true, Some(BinderInfo::Default));
951 rhs = close(&motives, &rhs, true, Some(BinderInfo::Default));
952 rhs = close(&pre.params, &rhs, true, Some(BinderInfo::Default));
953 rules.push(RecursorRule {
954 ctor: c.name.clone(),
955 nfields: c.fields.len() as u32,
956 rhs,
957 });
958 }
959
960 recursors.push(ConstantInfo::Recursor(RecursorVal {
961 common: ConstantVal {
962 name: rec_names[j].clone(),
963 level_params: rec_lparams.clone(),
964 ty: rec_ty,
965 },
966 all: pre.names.clone(),
967 num_params: pre.np as u32,
968 num_indices: pre.indices[j].len() as u32,
969 num_motives: k as u32,
970 num_minors: total_ctors as u32,
971 rules,
972 k: k_target,
973 is_unsafe: false,
974 }));
975 }
976
977 let is_rec_family = ctors
979 .iter()
980 .flatten()
981 .any(|c| c.fields.iter().any(|f| f.rec.is_some()));
982 let is_reflexive = ctors.iter().flatten().any(|c| {
983 c.fields
984 .iter()
985 .any(|f| matches!(&f.rec, Some(rf) if !rf.ds.is_empty()))
986 });
987 let is_prop = pre.ind_level.is_zero();
988
989 let mut inductives = Vec::with_capacity(k);
990 let mut constructors = Vec::with_capacity(total_ctors);
991 for (j, spec) in specs.iter().enumerate() {
992 inductives.push(ConstantInfo::Inductive(InductiveVal {
993 common: ConstantVal {
994 name: pre.names[j].clone(),
995 level_params: pre.lparams.clone(),
996 ty: spec.ty.clone(),
997 },
998 num_params: pre.np as u32,
999 num_indices: pre.indices[j].len() as u32,
1000 all: pre.names.clone(),
1001 ctors: ctors[j].iter().map(|c| c.name.clone()).collect(),
1002 num_nested: 0,
1003 is_rec: is_rec_family,
1004 is_unsafe: false,
1005 is_reflexive,
1006 is_prop,
1007 }));
1008 for (ci, c) in ctors[j].iter().enumerate() {
1009 constructors.push(ConstantInfo::Constructor(ConstructorVal {
1010 common: ConstantVal {
1011 name: c.name.clone(),
1012 level_params: pre.lparams.clone(),
1013 ty: c.ty.clone(),
1014 },
1015 induct: pre.names[j].clone(),
1016 cidx: ci as u32,
1017 num_params: pre.np as u32,
1018 num_fields: c.fields.len() as u32,
1019 is_unsafe: false,
1020 }));
1021 }
1022 }
1023
1024 Ok(DerivedFamily {
1025 inductives,
1026 constructors,
1027 recursors,
1028 })
1029}
1030
1031#[allow(clippy::result_large_err)]
1041pub(crate) fn check_specs_closed(specs: &[InductiveSpec]) -> Result<(), KernelError> {
1042 for spec in specs {
1043 crate::check::check_term_depth(&spec.name, &spec.ty)?;
1044 crate::check::check_closed_term(&spec.name, &spec.ty)?;
1045 for (ctor_name, ctor_ty) in &spec.ctors {
1046 crate::check::check_term_depth(ctor_name, ctor_ty)?;
1047 crate::check::check_closed_term(ctor_name, ctor_ty)?;
1048 }
1049 }
1050 Ok(())
1051}
1052
1053#[allow(clippy::result_large_err)]
1054fn check_distinct_ctor_names(specs: &[InductiveSpec]) -> Result<(), KernelError> {
1055 let mut seen: Vec<&Name> = Vec::new();
1056 for spec in specs {
1057 for (cname, _) in &spec.ctors {
1058 if seen.contains(&cname) {
1059 return Err(KernelError::InvalidInductive(format!(
1060 "duplicate constructor name '{}'",
1061 cname
1062 )));
1063 }
1064 seen.push(cname);
1065 }
1066 }
1067 Ok(())
1068}
1069
1070#[allow(clippy::result_large_err)]
1080pub fn check_and_derive_family(
1081 env: &Environment,
1082 lparams: &[Name],
1083 num_params: u32,
1084 specs: &[InductiveSpec],
1085) -> Result<DerivedFamily, KernelError> {
1086 check_distinct_ctor_names(specs)?;
1087 check_specs_closed(specs)?;
1088 let family_names: Vec<Name> = specs.iter().map(|s| s.name.clone()).collect();
1089
1090 let mut work = env.clone();
1092 for spec in specs {
1093 match work.find(&spec.name) {
1094 Some(ConstantInfo::Inductive(iv)) => {
1095 if iv.common.ty != spec.ty || iv.common.level_params != lparams {
1096 return Err(KernelError::InvalidInductive(format!(
1097 "inductive type '{}' is already declared with a different signature",
1098 spec.name
1099 )));
1100 }
1101 }
1102 Some(_) => {
1103 return Err(KernelError::InvalidInductive(format!(
1104 "'{}' is already declared and is not an inductive type",
1105 spec.name
1106 )));
1107 }
1108 None => {
1109 let stub = ConstantInfo::Inductive(InductiveVal {
1110 common: ConstantVal {
1111 name: spec.name.clone(),
1112 level_params: lparams.to_vec(),
1113 ty: spec.ty.clone(),
1114 },
1115 num_params,
1116 num_indices: 0,
1117 all: family_names.clone(),
1118 ctors: spec.ctors.iter().map(|(n, _)| n.clone()).collect(),
1119 num_nested: 0,
1120 is_rec: false,
1121 is_unsafe: false,
1122 is_reflexive: false,
1123 is_prop: false,
1124 });
1125 work.add_constant(stub)
1126 .map_err(|e| KernelError::Other(e.to_string()))?;
1127 }
1128 }
1129 }
1130
1131 let types: Vec<(Name, Expr)> = specs
1132 .iter()
1133 .map(|s| (s.name.clone(), s.ty.clone()))
1134 .collect();
1135 let mut ctx = DeriveCtx::checked(&work);
1136 let pre = build_family_pre(&mut ctx, lparams, num_params, &types)?;
1137 let mut ctor_sigs = Vec::with_capacity(specs.len());
1138 for (j, spec) in specs.iter().enumerate() {
1139 let mut cs = Vec::with_capacity(spec.ctors.len());
1140 for (cname, cty) in &spec.ctors {
1141 cs.push(check_ctor(&mut ctx, &pre, j, cname, cty)?);
1142 }
1143 ctor_sigs.push(cs);
1144 }
1145 derive_family_core(&mut ctx, &pre, specs, ctor_sigs)
1146}
1147
1148#[allow(clippy::result_large_err)]
1152pub fn add_inductive_family(
1153 env: &mut Environment,
1154 lparams: Vec<Name>,
1155 num_params: u32,
1156 specs: Vec<InductiveSpec>,
1157) -> Result<(), KernelError> {
1158 let fam = check_and_derive_family(env, &lparams, num_params, &specs)?;
1159 for ci in fam.into_constant_infos() {
1160 env.add_constant(ci)
1161 .map_err(|e| KernelError::Other(e.to_string()))?;
1162 }
1163 Ok(())
1164}
1165
1166#[allow(clippy::result_large_err)]
1175pub fn derive_family_unchecked(
1176 lparams: &[Name],
1177 num_params: u32,
1178 specs: &[InductiveSpec],
1179) -> Result<DerivedFamily, KernelError> {
1180 check_distinct_ctor_names(specs)?;
1181 let types: Vec<(Name, Expr)> = specs
1182 .iter()
1183 .map(|s| (s.name.clone(), s.ty.clone()))
1184 .collect();
1185 let mut ctx = DeriveCtx::unchecked();
1186 let pre = build_family_pre(&mut ctx, lparams, num_params, &types)?;
1187 let mut ctor_sigs = Vec::with_capacity(specs.len());
1188 for (j, spec) in specs.iter().enumerate() {
1189 let mut cs = Vec::with_capacity(spec.ctors.len());
1190 for (cname, cty) in &spec.ctors {
1191 cs.push(check_ctor(&mut ctx, &pre, j, cname, cty)?);
1192 }
1193 ctor_sigs.push(cs);
1194 }
1195 derive_family_core(&mut ctx, &pre, specs, ctor_sigs)
1196}
1197
1198#[allow(clippy::result_large_err)]
1208pub(crate) fn validate_inductive_val(
1209 env: &Environment,
1210 iv: &InductiveVal,
1211) -> Result<(), KernelError> {
1212 for (i, lp) in iv.common.level_params.iter().enumerate() {
1213 if iv.common.level_params[..i].contains(lp) {
1214 return Err(KernelError::InvalidInductive(format!(
1215 "duplicate universe parameter '{}' in inductive '{}'",
1216 lp, iv.common.name
1217 )));
1218 }
1219 }
1220 check_level_params_declared(
1221 &iv.common.ty,
1222 &iv.common.level_params,
1223 "inductive type",
1224 &iv.common.name,
1225 )?;
1226 let mut ctx = DeriveCtx::checked(env);
1227 ctx.ensure_sort(&iv.common.ty)?;
1228 let (binders, rest) = peel_all(&mut ctx, &iv.common.ty);
1229 let restw = ctx.whnf(&rest);
1230 let level = match restw {
1231 Expr::Sort(l) => l,
1232 other => {
1233 return Err(KernelError::InvalidInductive(format!(
1234 "inductive type '{}' must end in a sort, found '{}'",
1235 iv.common.name, other
1236 )));
1237 }
1238 };
1239 if binders.len() != (iv.num_params + iv.num_indices) as usize {
1240 return Err(KernelError::InvalidInductive(format!(
1241 "inductive '{}' declares {} params + {} indices but its type has {} binders",
1242 iv.common.name,
1243 iv.num_params,
1244 iv.num_indices,
1245 binders.len()
1246 )));
1247 }
1248 if iv.is_prop != level.is_zero() {
1249 return Err(KernelError::InvalidInductive(format!(
1250 "inductive '{}': is_prop flag ({}) is inconsistent with the declared sort",
1251 iv.common.name, iv.is_prop
1252 )));
1253 }
1254 if !iv.all.contains(&iv.common.name) {
1255 return Err(KernelError::InvalidInductive(format!(
1256 "inductive '{}' is missing from its own mutual group",
1257 iv.common.name
1258 )));
1259 }
1260 for (i, c) in iv.ctors.iter().enumerate() {
1261 if iv.ctors[..i].contains(c) {
1262 return Err(KernelError::InvalidInductive(format!(
1263 "duplicate constructor '{}' in inductive '{}'",
1264 c, iv.common.name
1265 )));
1266 }
1267 }
1268 Ok(())
1269}
1270
1271#[allow(clippy::result_large_err)]
1274fn family_pre_from_env<'e>(
1275 env: &'e Environment,
1276 all: &[Name],
1277 err_kind: fn(String) -> KernelError,
1278) -> Result<(DeriveCtx<'e>, FamilyPre), KernelError> {
1279 let mut lparams: Option<Vec<Name>> = None;
1280 let mut np: Option<u32> = None;
1281 let mut types = Vec::with_capacity(all.len());
1282 for tname in all {
1283 let siv = env.get_inductive_val(tname).ok_or_else(|| {
1284 err_kind(format!(
1285 "mutual inductive sibling '{}' is not declared in the environment",
1286 tname
1287 ))
1288 })?;
1289 if siv.all != all {
1290 return Err(err_kind(format!(
1291 "inductive '{}' declares a different mutual group",
1292 tname
1293 )));
1294 }
1295 match (&lparams, &np) {
1296 (None, None) => {
1297 lparams = Some(siv.common.level_params.clone());
1298 np = Some(siv.num_params);
1299 }
1300 (Some(lp), Some(n)) if *lp != siv.common.level_params || *n != siv.num_params => {
1301 return Err(err_kind(format!(
1302 "mutual inductive '{}' disagrees on level params or param count",
1303 tname
1304 )));
1305 }
1306 _ => {}
1307 }
1308 types.push((tname.clone(), siv.common.ty.clone()));
1309 }
1310 let lparams = lparams.unwrap_or_default();
1311 let np = np.unwrap_or(0);
1312 let mut ctx = DeriveCtx::checked(env);
1313 let pre = build_family_pre(&mut ctx, &lparams, np, &types)?;
1314 for (j, tname) in all.iter().enumerate() {
1316 if let Some(siv) = env.get_inductive_val(tname) {
1317 if siv.num_indices as usize != pre.indices[j].len() {
1318 return Err(err_kind(format!(
1319 "inductive '{}' declares {} indices but its type has {}",
1320 tname,
1321 siv.num_indices,
1322 pre.indices[j].len()
1323 )));
1324 }
1325 }
1326 }
1327 Ok((ctx, pre))
1328}
1329
1330#[allow(clippy::result_large_err)]
1335pub(crate) fn verify_constructor_val(
1336 env: &Environment,
1337 cv: &ConstructorVal,
1338) -> Result<(), KernelError> {
1339 let iv = env.get_inductive_val(&cv.induct).ok_or_else(|| {
1340 KernelError::InvalidInductive(format!(
1341 "constructor '{}' references unknown inductive '{}'",
1342 cv.common.name, cv.induct
1343 ))
1344 })?;
1345 if cv.common.level_params != iv.common.level_params {
1346 return Err(KernelError::InvalidInductive(format!(
1347 "constructor '{}' must use the level parameters of '{}'",
1348 cv.common.name, cv.induct
1349 )));
1350 }
1351 if cv.num_params != iv.num_params {
1352 return Err(KernelError::InvalidInductive(format!(
1353 "constructor '{}': num_params ({}) disagrees with inductive '{}' ({})",
1354 cv.common.name, cv.num_params, cv.induct, iv.num_params
1355 )));
1356 }
1357 match iv.ctors.iter().position(|c| c == &cv.common.name) {
1358 Some(pos) if pos as u32 == cv.cidx => {}
1359 Some(pos) => {
1360 return Err(KernelError::InvalidInductive(format!(
1361 "constructor '{}' has cidx {} but is at position {} in '{}'",
1362 cv.common.name, cv.cidx, pos, cv.induct
1363 )));
1364 }
1365 None => {
1366 return Err(KernelError::InvalidInductive(format!(
1367 "constructor '{}' is not listed by inductive '{}'",
1368 cv.common.name, cv.induct
1369 )));
1370 }
1371 }
1372 let all = iv.all.clone();
1373 let induct = cv.induct.clone();
1374 let (mut ctx, pre) = family_pre_from_env(env, &all, KernelError::InvalidInductive)?;
1375 let type_idx = pre.names.iter().position(|n| *n == induct).ok_or_else(|| {
1376 KernelError::InvalidInductive(format!(
1377 "inductive '{}' missing from its own mutual group",
1378 induct
1379 ))
1380 })?;
1381 let sig = check_ctor(&mut ctx, &pre, type_idx, &cv.common.name, &cv.common.ty)?;
1382 if sig.fields.len() != cv.num_fields as usize {
1383 return Err(KernelError::InvalidInductive(format!(
1384 "constructor '{}': num_fields ({}) disagrees with its type ({} fields)",
1385 cv.common.name,
1386 cv.num_fields,
1387 sig.fields.len()
1388 )));
1389 }
1390 Ok(())
1391}
1392
1393#[allow(clippy::result_large_err)]
1400pub(crate) fn verify_recursor_val(env: &Environment, rv: &RecursorVal) -> Result<(), KernelError> {
1401 if rv.all.is_empty() {
1402 return Err(KernelError::InvalidRecursor(format!(
1403 "recursor '{}' has an empty mutual group",
1404 rv.common.name
1405 )));
1406 }
1407 let mut lparams: Option<Vec<Name>> = None;
1409 let mut np: Option<u32> = None;
1410 let mut specs = Vec::with_capacity(rv.all.len());
1411 let single = rv.all.len() == 1;
1412 let flat_style = rv.common.name.depth() <= 1;
1417 for tname in &rv.all {
1418 let iv = env.get_inductive_val(tname).ok_or_else(|| {
1419 KernelError::InvalidRecursor(format!(
1420 "recursor '{}' references unknown inductive '{}'",
1421 rv.common.name, tname
1422 ))
1423 })?;
1424 if iv.all != rv.all {
1425 return Err(KernelError::InvalidRecursor(format!(
1426 "recursor '{}' and inductive '{}' disagree on the mutual group",
1427 rv.common.name, tname
1428 )));
1429 }
1430 match (&lparams, &np) {
1431 (None, None) => {
1432 lparams = Some(iv.common.level_params.clone());
1433 np = Some(iv.num_params);
1434 }
1435 (Some(lp), Some(n)) if *lp != iv.common.level_params || *n != iv.num_params => {
1436 return Err(KernelError::InvalidRecursor(format!(
1437 "mutual inductive '{}' disagrees on level params or param count",
1438 tname
1439 )));
1440 }
1441 _ => {}
1442 }
1443 let mut ctors = Vec::with_capacity(iv.ctors.len());
1444 for cname in &iv.ctors {
1445 let cval = env.get_constructor_val(cname).ok_or_else(|| {
1446 KernelError::InvalidRecursor(format!(
1447 "recursor '{}': constructor '{}' of '{}' is not declared yet",
1448 rv.common.name, cname, tname
1449 ))
1450 })?;
1451 ctors.push((cname.clone(), cval.common.ty.clone()));
1452 }
1453 let rec_name = if single {
1454 rv.common.name.clone()
1455 } else if flat_style {
1456 Name::str(format!("{}.rec", tname))
1457 } else {
1458 Name::mk_str(tname.clone(), "rec")
1459 };
1460 specs.push(InductiveSpec {
1461 name: tname.clone(),
1462 ty: iv.common.ty.clone(),
1463 ctors,
1464 rec_name: Some(rec_name),
1465 });
1466 }
1467 let lparams = lparams.unwrap_or_default();
1468 let np = np.unwrap_or(0);
1469 let fam = check_and_derive_family(env, &lparams, np, &specs)?;
1470
1471 let mut tc = TypeChecker::new(env);
1472 for d_ci in &fam.recursors {
1473 if let ConstantInfo::Recursor(d) = d_ci {
1474 if recursor_matches(&mut tc, d, rv) {
1475 return Ok(());
1476 }
1477 }
1478 }
1479 Err(KernelError::InvalidRecursor(format!(
1480 "recursor '{}' does not match the kernel-derived recursor for '{}'",
1481 rv.common.name, rv.all[0]
1482 )))
1483}
1484
1485pub(crate) fn recursor_matches(
1486 tc: &mut TypeChecker<'_>,
1487 d: &RecursorVal,
1488 rv: &RecursorVal,
1489) -> bool {
1490 if d.common.level_params.len() != rv.common.level_params.len() {
1491 return false;
1492 }
1493 if d.num_params != rv.num_params
1494 || d.num_indices != rv.num_indices
1495 || d.num_motives != rv.num_motives
1496 || d.num_minors != rv.num_minors
1497 || d.k != rv.k
1498 || d.rules.len() != rv.rules.len()
1499 {
1500 return false;
1501 }
1502 let levels: Vec<Level> = rv
1504 .common
1505 .level_params
1506 .iter()
1507 .cloned()
1508 .map(Level::param)
1509 .collect();
1510 let d_ty = instantiate_type_lparams(&d.common.ty, &d.common.level_params, &levels);
1511 if !tc.is_def_eq(&d_ty, &rv.common.ty) {
1512 return false;
1513 }
1514 for drule in &d.rules {
1515 let srule = match rv.get_rule(&drule.ctor) {
1516 Some(r) => r,
1517 None => return false,
1518 };
1519 if srule.nfields != drule.nfields {
1520 return false;
1521 }
1522 let d_rhs = instantiate_type_lparams(&drule.rhs, &d.common.level_params, &levels);
1523 if !tc.is_def_eq(&d_rhs, &srule.rhs) {
1524 return false;
1525 }
1526 }
1527 true
1528}
1529
1530#[cfg(test)]
1531mod tests {
1532 use super::*;
1533
1534 fn nat_spec() -> InductiveSpec {
1535 let nat = Expr::Const(Name::str("Nat"), vec![]);
1536 InductiveSpec::new(
1537 Name::str("Nat"),
1538 Expr::Sort(Level::succ(Level::zero())),
1539 vec![
1540 (Name::str("Nat.zero"), nat.clone()),
1541 (
1542 Name::str("Nat.succ"),
1543 Expr::Pi(
1544 BinderInfo::Default,
1545 Name::str("n"),
1546 Node::new(nat.clone()),
1547 Node::new(nat),
1548 ),
1549 ),
1550 ],
1551 )
1552 .with_rec_name(Name::str("Nat.rec"))
1553 }
1554
1555 #[test]
1556 fn nat_derivation_shape() {
1557 let env = Environment::new();
1558 let fam = check_and_derive_family(&env, &[], 0, &[nat_spec()]).expect("derive Nat");
1559 assert_eq!(fam.inductives.len(), 1);
1560 assert_eq!(fam.constructors.len(), 2);
1561 assert_eq!(fam.recursors.len(), 1);
1562 let rv = match &fam.recursors[0] {
1563 ConstantInfo::Recursor(rv) => rv,
1564 other => panic!("expected recursor, got {:?}", other),
1565 };
1566 assert_eq!(rv.num_motives, 1);
1567 assert_eq!(rv.num_minors, 2);
1568 assert_eq!(rv.num_params, 0);
1569 assert_eq!(rv.num_indices, 0);
1570 assert!(!rv.k);
1571 assert_eq!(rv.common.level_params, vec![Name::str("u")]);
1573 let succ_rule = rv.get_rule(&Name::str("Nat.succ")).expect("succ rule");
1575 assert_eq!(succ_rule.nfields, 1);
1576 }
1577
1578 #[test]
1579 fn unchecked_matches_checked_for_nat() {
1580 let env = Environment::new();
1581 let a = check_and_derive_family(&env, &[], 0, &[nat_spec()]).expect("checked");
1582 let b = derive_family_unchecked(&[], 0, &[nat_spec()]).expect("unchecked");
1583 assert_eq!(a.recursors, b.recursors);
1584 assert_eq!(a.constructors, b.constructors);
1585 }
1586
1587 #[test]
1588 fn nested_inductive_rejected() {
1589 let mut env = Environment::new();
1591 crate::builtin::init_builtin_env(&mut env).expect("builtin env");
1592 let t = Expr::Const(Name::str("T"), vec![]);
1593 let list_t = Expr::App(
1594 Node::new(Expr::Const(Name::str("List"), vec![Level::zero()])),
1595 Node::new(t.clone()),
1596 );
1597 let spec = InductiveSpec::new(
1598 Name::str("T"),
1599 Expr::Sort(Level::succ(Level::zero())),
1600 vec![(
1601 Name::str("T.mk"),
1602 Expr::Pi(
1603 BinderInfo::Default,
1604 Name::str("l"),
1605 Node::new(list_t),
1606 Node::new(t),
1607 ),
1608 )],
1609 );
1610 let err = check_and_derive_family(&env, &[], 0, &[spec]).expect_err("must reject");
1611 assert!(matches!(err, KernelError::UnsupportedNestedInductive(_)));
1612 }
1613}