Commit 9216029
fix(typecheck): infer recursive functions' return type by fixpoint (#88)
Recursive functions returning a scalar were unwritable. The canonical shape,
straight out of examples/trefoil.tangle:
def length(w) = match w with
| identity => 0
| s1 . rest => 1 + length(rest)
| _ => 0
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 the recursive call
`length(rest)` typed as Word[0], and `1 + length(rest)` was Num + Word[0].
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 are drawn first from the match arms that do NOT 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 this terminates. If no candidate is a
fixpoint we fall back to the original single pass, which re-raises its
original error — a definition that did not typecheck before still does not,
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 — so the first version of
this fix changed nothing observable. They are now a single
`bind_function_def` called from both. That duplication is why the placeholder
survived: fixing one copy looked like fixing the bug.
## Scope
This closes the recursive-typing half of #88. The remaining example failures
are a DIFFERENT question — equal-width requirements on `==` and on match arms
— which cannot be changed in OCaml alone: Lean's `tEqWord` requires both
operands at the same width `n`, and TG-3's 496 kernel-checked obligations tie
OCaml's inference to that spec. It needs a cross-engine ruling like #50 did;
raised separately.
Tests: 5 new cases covering the `length` shape, that the inferred signature is
actually Num (not merely that it compiles), that a caller can use it at Num,
that non-recursive definitions are untouched, and that a genuinely ill-typed
recursive function still fails. Typecheck suite 119 -> 124; TG-3 still
1008/0; all other suites unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>1 parent b53fc0e commit 9216029
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