Populate canonical recursor rules from the actual recursor block peers. `generate_block_recursors` is driven from the inductive block, where the recursor constants are not necessarily block members. With block-level recursor checking, the recursor block is available before comparing any sibling. Build the rule RHSs once from that block and store them back at the generated-recursors indices. This
(
&mut self,
ind_block_id: &KId<M>,
rec_block_id: &KId<M>,
)
| 3340 | let fvar = self.intern(KExpr::var((n_binders - 1 - i) as u64, anon())); |
| 3341 | ctor_app = self.intern(KExpr::app(ctor_app, fvar)); |
| 3342 | } |
| 3343 | conclusion = self.intern(KExpr::app(conclusion, ctor_app)); |
| 3344 | |
| 3345 | // Fold: ∀ (ihs...) (fields...), conclusion (from inside out) |
| 3346 | // Pop IHs first (innermost) |
| 3347 | for i in (0..n_ihs).rev() { |
| 3348 | self.lctx.truncate(self.lctx.len() - 1); |
| 3349 | conclusion = self.intern(KExpr::all( |
| 3350 | anon(), |
| 3351 | bi_default(), |
| 3352 | ih_domains[i].clone(), |
| 3353 | conclusion, |
| 3354 | )); |
| 3355 | } |
| 3356 | // Pop fields |
| 3357 | for i in (0..n_fields).rev() { |
| 3358 | self.lctx.truncate(self.lctx.len() - 1); |
| 3359 | conclusion = self.intern(KExpr::all( |
| 3360 | anon(), |
| 3361 | bi_default(), |
| 3362 | field_domains[i].clone(), |
| 3363 | conclusion, |
| 3364 | )); |
| 3365 | } |
| 3366 | |
| 3367 | self.lctx.truncate(saved); |
| 3368 | Ok(conclusion) |
| 3369 | } |
| 3370 | |
| 3371 | /// Build an IH type for a recursive field. |
| 3372 | /// |
| 3373 | /// For a direct recursive field (type = `I_bi params idx_args`): |
| 3374 | /// IH = `motive_bi(idx_args, field_var)` |
| 3375 | /// |
| 3376 | /// For a forall-wrapped recursive field (type = `∀ xs, I_bi params idx_args(xs)`): |
| 3377 | /// IH = `∀ xs, motive_bi(idx_args(xs), field xs)` |
| 3378 | /// |
| 3379 | /// Called when depth = minor_saved + n_fields + k (k IHs already pushed). |
| 3380 | fn build_direct_ih( |
| 3381 | &mut self, |
| 3382 | field_idx: usize, |
| 3383 | block_ind_idx: usize, |
| 3384 | n_params: usize, |
| 3385 | n_fields: usize, |
| 3386 | k: usize, // number of IHs already pushed before this one |
| 3387 | minor_saved: usize, // depth at entry of build_minor_at_depth |
| 3388 | motive_base: usize, |
| 3389 | field_domains: &[KExpr<M>], |
| 3390 | block_addrs: &[Address], |
| 3391 | ) -> Result<KExpr<M>, TcError<M>> { |
| 3392 | let anon = || M::meta_field(ix_common::env::Name::anon()); |
| 3393 | let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); |
| 3394 | |
| 3395 | // Lift the field domain from its original depth (minor_saved + field_idx) |
| 3396 | // to the current depth (minor_saved + n_fields + k). |
| 3397 | let dom = &field_domains[field_idx]; |
| 3398 | let fields_and_ihs = checked_binder_sum::<M>( |
| 3399 | "generated minor field + induction-hypothesis depth", |
no test coverage detected