Build an env with Bool (2 ctors, 0 fields each) and its recursor. Bool : Sort 1 Bool.true : Bool Bool.false : Bool Bool.rec : ∀ (motive : Bool → Sort u) (h₁ : motive Bool.true) (h₂ : motive Bool.false) (t : Bool), motive t
()
| 4556 | .map(|i| KUniv::param(i, M::meta_field(ix_common::env::Name::anon()))) |
| 4557 | .collect(); |
| 4558 | |
| 4559 | // Peel foralls from the domain to detect wrapping. |
| 4560 | // After peeling, the head should be `I_target params idx_args`. |
| 4561 | let wdom = self.whnf(dom)?; |
| 4562 | let mut inner = wdom.clone(); |
| 4563 | let mut forall_doms: Vec<KExpr<M>> = Vec::new(); |
| 4564 | |
| 4565 | while let ExprData::All(_, _, fd, fb, _) = inner.data() { |
| 4566 | // Check if this forall's result type (after peeling) has a block |
| 4567 | // inductive as head. If inner itself IS a block inductive app, stop. |
| 4568 | let (h, _) = collect_app_spine(&inner); |
| 4569 | if matches!(h.data(), ExprData::Const(id, _, _) |
| 4570 | if flat.iter().any(|m| m.id.addr == id.addr)) |
| 4571 | { |
| 4572 | break; |
| 4573 | } |
| 4574 | forall_doms.push(fd.clone()); |
| 4575 | inner = fb.clone(); |
| 4576 | } |
| 4577 | let n_xs = checked_usize_to_u64::<M>( |
| 4578 | "generated recursor wrapped-field binder count", |
| 4579 | forall_doms.len(), |
| 4580 | )?; |
| 4581 | |
| 4582 | // Extract index args from the inner application: `I_target params idx_args` |
| 4583 | let inner_w = self.whnf(&inner)?; |
| 4584 | let (_, inner_args) = collect_app_spine(&inner_w); |
| 4585 | let idx_args: Vec<KExpr<M>> = |
| 4586 | inner_args.iter().skip(target_n_params).cloned().collect(); |
| 4587 | |
| 4588 | // Build the IH core: rec[target] params motives minors indices field |
| 4589 | // All Var references are relative to total_lams (+ n_xs for forall-wrapped case). |
| 4590 | let depth = checked_metadata_sum::<M>( |
| 4591 | "generated recursor induction-hypothesis depth", |
| 4592 | &[total_lams, n_xs], |
| 4593 | )?; |
| 4594 | let n_rec_params_u64 = checked_usize_to_u64::<M>( |
| 4595 | "generated recursor parameter count", |
| 4596 | n_rec_params, |
| 4597 | )?; |
| 4598 | let n_motives_u64 = |
| 4599 | checked_usize_to_u64::<M>("generated recursor motive count", n_motives)?; |
| 4600 | |
| 4601 | let mut ih = self.intern(KExpr::cnst(peer_rec.clone(), rec_lvls)); |
| 4602 | // Apply params |
| 4603 | for pi in 0..n_rec_params { |
| 4604 | let pi = |
| 4605 | checked_usize_to_u64::<M>("generated recursor parameter index", pi)?; |
| 4606 | let pvar = self.intern(KExpr::var(depth - 1 - pi, anon())); |
| 4607 | ih = self.intern(KExpr::app(ih, pvar)); |
| 4608 | } |
| 4609 | // Apply motives |
| 4610 | for mi in 0..n_motives { |
| 4611 | let mi = |
| 4612 | checked_usize_to_u64::<M>("generated recursor motive index", mi)?; |
| 4613 | let mvar = |
| 4614 | self.intern(KExpr::var(depth - 1 - n_rec_params_u64 - mi, anon())); |
| 4615 | ih = self.intern(KExpr::app(ih, mvar)); |