MCPcopy Create free account
hub / github.com/argumentcomputer/ix / try_detect_nested

Method try_detect_nested

crates/kernel/src/inductive.rs:620–751  ·  view source on GitHub ↗

Check if a field domain is a nested inductive occurrence and, if so, add an auxiliary entry to the flat block. A nested occurrence is: after peeling foralls, the result is `ExtInd Ds is` where `ExtInd` is a previously-declared inductive (not in our block) and some param arg `Ds[i]` mentions a block inductive. Important: do not WHNF the domain here.** Compile-side `replace_if_nested` (and Lean's

(
    &mut self,
    dom: &KExpr<M>,
    block_addrs: &[Address],
    flat: &mut Vec<FlatBlockMember<M>>,
    aux_seen: &mut Vec<(Address, Vec<[u8; 32]>)>,
    univ_offset: u64,
    param_depth: usize

Source from the content-addressed store, hash-verified

618 Ok(flat)
619 }
620
621 /// Check if a field domain is a nested inductive occurrence and, if so,
622 /// add an auxiliary entry to the flat block.
623 ///
624 /// A nested occurrence is: after peeling foralls, the result is `ExtInd Ds is`
625 /// where `ExtInd` is a previously-declared inductive (not in our block) and
626 /// some param arg `Ds[i]` mentions a block inductive.
627 ///
628 /// **Important: do not WHNF the domain here.** Compile-side
629 /// `replace_if_nested` (and Lean's C++ `is_nested_inductive_app`,
630 /// `inductive.cpp:920`) checks the head literally — if the head is a
631 /// definition like `IO.Ref`, it is *not* a nested-inductive occurrence.
632 /// WHNF would unfold `IO.Ref α` to `ST.Ref IO.RealWorld α`, which IS an
633 /// inductive — the kernel would then synthesize auxiliaries (e.g.
634 /// `_nested.ST_Ref_*`) that the compile side never generates, and
635 /// `populate_recursor_rules_from_block` would fail with `rec_ids/flat
636 /// count mismatch`. Peel `All` constructors structurally instead.
637 fn try_detect_nested(
638 &mut self,
639 dom: &KExpr<M>,
640 block_addrs: &[Address],
641 flat: &mut Vec<FlatBlockMember<M>>,
642 aux_seen: &mut Vec<(Address, Vec<KExpr<M>>)>,
643 univ_offset: u64,
644 param_depth: usize, // depth at the param context (before field locals)
645 n_rec_params: u64, // number of inductive parameters (valid Var refs in spec_params)
646 ) -> Result<(), TcError<M>> {
647 let saved_lctx = self.lctx.len();
648 let result = (|| -> Result<(), TcError<M>> {
649 // Peel foralls structurally — no WHNF, see doc comment above. Open
650 // each peeled binder with a temporary fvar so domain-local dependencies
651 // in external inductive parameters are rejected by the same locality
652 // check as field-local dependencies.
653 let mut cur = dom.clone();
654 while let ExprData::All(_, _, inner_dom, body, _) = cur.data() {
655 let inner_dom = inner_dom.clone();
656 let body = body.clone();
657 let (open, _) = self.open_binder_anon(inner_dom, &body);
658 cur = open;
659 }
660
661 let (head, args) = collect_app_spine(&cur);
662 let head_id = match head.data() {
663 ExprData::Const(id, _, _) => id.clone(),
664 _ => return Ok(()),
665 };
666
667 // Skip if head is already a block member (direct recursive, not nested).
668 if block_addrs.contains(&head_id.addr) {
669 return Ok(());
670 }
671 // Also skip if head is already a flat block member (already detected).
672 if flat.iter().any(|m| m.id.addr == head_id.addr && !m.is_aux) {
673 return Ok(());
674 }
675
676 // Check if head is an external inductive.
677 let (ext_params, ext_indices, ext_ctors, ext_lvls) =

Callers 1

build_flat_blockMethod · 0.80

Calls 15

collect_app_spineFunction · 0.85
expr_mentions_any_addrFunction · 0.85
open_binder_anonMethod · 0.80
has_fvarsMethod · 0.80
lbrMethod · 0.80
as_bytesMethod · 0.80
pushMethod · 0.80
mk_ind_univsMethod · 0.80
truncateMethod · 0.80
lenMethod · 0.45
cloneMethod · 0.45
dataMethod · 0.45

Tested by

no test coverage detected