Add recursive functions to Strata Core#499
Conversation
Add test for polymorphic function in DDM Add tests for verifier errors (polymorphism, no decreaes, etc) Remove reundant scope with scopeSelf
Add support for more complex triggers in s-expr Remove uses of Visibility
MikaelMayer
left a comment
There was a problem hiding this comment.
This PR adds comprehensive support for recursive functions in Strata Core with a well-designed implementation. The changes are structured logically across the DDM layer (adding @[scopeSelf] metadata), the Lambda dialect (axiom generation), and the Core language (grammar, translation, SMT encoding). The test coverage is excellent, including positive tests, error cases, and a complex example (BinaryTreeSize). The documentation clearly explains the new @[scopeSelf] attribute and its purpose.
The implementation correctly handles the key challenges: bringing the function name into scope for recursive calls, generating per-constructor axioms for SMT reasoning, and properly rejecting unsupported cases (polymorphic recursion, missing decreases clauses). The ITE evaluation change to always reduce both branches is a sensible improvement that enables better partial evaluation.
I have a few minor suggestions for clarity and potential improvements, but overall this is high-quality work that's ready to merge after addressing the comments.
Issue #, if available:
Description of changes: This PR adds support for recursive functions in Strata Core, including their definition in concrete syntax, partial evaluation, and an axiom-based SMT encoding.
Main changes:
@[scopeSelf]that acts like@[scope]but also adds the function's name to its body's scope. This is structured and handled very similarly to@[declareDatatype], which involves a similar recursive structure. Main changes are inAST.lean,Format.lean,Core.lean. This adds a@[cases]metadata attribute to indicate the parameter on which case splitting should occur.Funcindicating whether a function is recursive and the argument to case-split on. This information is used inEnv.leanwhen the function is added to the environment. Here we also rule out recursive functions that are not yet supported: polymorphic functions, functions with more sophisticated termination measures, and functions without an explicitcasesclause.RecursiveAxioms.leancontains the generation of axioms asLExprfor a recursive function. Generating the axiom is fairly simple, since we just create term e.g.forall h t, length (List.cons h t) = length (List.cons h t)and then partially evaluate the right-hand side. Since recursive functions are marked asinlineIfConstron their case-splitting parameter, this produces the simplified form.Other changes
SMT/DDMTransform/Translatefor trigger patterns complex enough to handle constructors applied to arguments (e.g.List.cons h t).thenandelsebranches and changes the small-step semantics to match.Tests
RecursiveAxiomTests.leantests the generation of recursive axioms as Lambda terms.SMTEncoderDatatypeTests.leanthat demonstrates the SMT encoding of the recursive function axioms.RecursiveFunctionDDMTest.leantests the DDM parsing for recursive function.RecursiveFunctionErrorTest.leandemonstrates not-yet-supported recursive functions.RecursiveFunctionTests.leancontain a variety of programs with recursive functions, some of which contain VCs which can be solved purely by partial evaluation and others which require SMT-based reasoning.BinaryTreeSize.leangives a more complex example that demonstrates inductive reasoning over types and functions.What is not supported (yet): polymorphic and mutually recursive functions, termination checking.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.