Commit 4e6903f
Recursive functions returning a scalar were **unwritable**. The
canonical shape, straight out of `examples/trefoil.tangle`:
```tangle
def length(w) = match w with
| identity => 0
| s1 . rest => 1 + length(rest)
| _ => 0
end
```
failed with `Cannot add Num and Word[0]`.
## Cause
While checking a function's own body, the function was bound with a
**hard-coded return type of `TWord 0`**, marked `(* placeholder *)` —
and nothing ever checked the inferred body type against that assumption.
So `length(rest)` typed as `Word[0]`, making `1 + length(rest)` a `Num +
Word[0]`.
## Fix
A recursive function's return type occurs in its own derivation, so it
must be a **fixpoint**: assume a return type, check the body under that
assumption, and accept only if the body then has *exactly* the assumed
type. Anything weaker is a guess that merely failed to crash.
Candidate seeds come first from the match arms that **don't** mention
the function — for `length` those are the two `0` arms, giving `Num` —
then the scalar types, then the original `TWord 0` so prior behaviour
stays reachable. The list is finite and ordered, so it terminates. If no
candidate is a fixpoint, it falls back to the original single pass and
re-raises its original error: **a definition that didn't typecheck
before still doesn't, with the same message.**
## The logic existed twice
`check_statement` had one copy and `check_program`'s pass 1b had another
— and **only `check_program`'s is reached for whole programs**. My first
attempt at this fix therefore changed nothing observable, which is
exactly how the placeholder survived so long: fixing one copy *looks*
like fixing the bug. They're now a single `bind_function_def` called
from both.
## Scope — what this does NOT do
This closes the recursive-typing half of #88. The remaining example
failures are a **different question**: equal-width requirements on `==`
and on match arms.
That one cannot be changed in OCaml alone. Lean's rule requires both
operands at the same width:
```lean
| tEqWord (Γ : Ctx) (e₁ e₂ : Expr) (n : Nat) :
HasType Γ e₁ (.word n) →
HasType Γ e₂ (.word n) →
HasType Γ (.eq e₁ e₂) .bool
```
and TG-3's 496 kernel-checked obligations tie OCaml's `infer_expr` to
that spec. Changing one engine would silently break the differential. It
needs a cross-engine ruling the way #50 did — raised separately, with
the evidence.
## Tests
Five new cases, chosen so they can't pass vacuously:
| Test | Guards against |
|---|---|
| the `length` shape typechecks | the original bug |
| its inferred signature **is `Num`** | "compiles" ≠ "correct type" |
| a caller can use the result at `Num` | the signature being unusable
downstream |
| non-recursive definitions unaffected | the fixpoint search
over-reaching |
| a genuinely ill-typed recursive fn **still fails** | the fallback
swallowing real errors |
Typecheck suite 119 → 124. TG-3 still 1008/0. All other suites
unchanged; corpus and RSR gates still green.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent b53fc0e commit 4e6903f
2 files changed
Lines changed: 180 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
652 | 652 | | |
653 | 653 | | |
654 | 654 | | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
655 | 758 | | |
656 | 759 | | |
657 | 760 | | |
| |||
671 | 774 | | |
672 | 775 | | |
673 | 776 | | |
674 | | - | |
675 | | - | |
676 | | - | |
677 | | - | |
678 | | - | |
679 | | - | |
680 | | - | |
681 | | - | |
682 | | - | |
683 | | - | |
684 | | - | |
685 | | - | |
686 | | - | |
| 777 | + | |
687 | 778 | | |
688 | 779 | | |
689 | 780 | | |
| |||
813 | 904 | | |
814 | 905 | | |
815 | 906 | | |
816 | | - | |
817 | | - | |
818 | | - | |
819 | | - | |
820 | | - | |
821 | | - | |
822 | | - | |
823 | | - | |
824 | | - | |
825 | | - | |
| 907 | + | |
826 | 908 | | |
827 | 909 | | |
828 | 910 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
623 | 623 | | |
624 | 624 | | |
625 | 625 | | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
626 | 701 | | |
627 | 702 | | |
628 | 703 | | |
| |||
0 commit comments