lean4-htt/tests/lean/run/terminationByStructurally.lean
Joachim Breitner fb0c46a011
feat: termination_by structural (#4542)
This implements the `termination_by structural` syntax proposed in
#3909.

I went with `termination_by structural` over, say,
`termination_by (config := {method := .structural})` mainly because it
was
easier to get going (otherwise I’d have to look into how to define
recursive
parsers, as `Parser.config` depends on `term` and `termination_by` is
part of
term. But also because I find it more ergonomic and aesthetic as a user.
But syntax can still change.

The `termination_by?` syntax will no longer force well-founded
recursion,
and instead the inferred `termination_by structurally` annotation will
be shown
if structural termination is possible.

While I was it, this fixes #4546 the easy way (log errors about but
otherwise
ignore incomplete `termination_by` sets for mutual recursion). Maybe we
get
multiple replacements (#4551), but even then this this good behavior.

Involves a bit of shuffling around `TerimationHints` (now validated for
a
clique already by `PreDefinition.main`) and `TerminationArguments` (now
lifted
out of the `WF` namespace, and a bit simplified).

Fixes #3909

---------

Co-authored-by: Richard Kiss <him@richardkiss.com>
2024-07-01 16:51:30 +00:00

85 lines
2.1 KiB
Text

def foo (n : Nat) : Nat := match n with
| 0 => 0
| n+1 => foo n
termination_by structural n
-- Test that this is indeed by structural recursion
example : foo (n + 3) = foo n := Eq.refl _
-- Check that we can still refer to a variable called `structural` in
-- the `termination_by` syntax
def bar (structural : Nat) : True := match structural with
| 0 => .intro
| structural+1 => bar structural
termination_by «structural»
namespace Errors
-- A few error conditions
/--
error: argument #1 cannot be used for structural recursion
it is unchanged in the recursive calls
-/
#guard_msgs in
def foo1 (n : Nat) : Nat := foo1 n
termination_by structural n
/--
error: argument #2 cannot be used for structural recursion
its type is an inductive family and indices are not variables
n.succ.le 100
-/
#guard_msgs in
def foo2 (n : Nat) (h : n < 100) : Nat := match n with
| 0 => 0
| n+1 => foo2 n (by omega)
termination_by structural h
/--
error: one parameter bound in `termination_by`, but the body of Errors.foo3 only binds 0 parameters.
-/
#guard_msgs in
def foo3 (n : Nat) : Nat → Nat := match n with
| 0 => id
| n+1 => foo3 n
termination_by structural m => m
/--
error: argument #1 cannot be used for structural recursion
failed to eliminate recursive application
ackermann (n + 1) m
-/
#guard_msgs in
def ackermann (n m : Nat) := match n, m with
| 0, m => m + 1
| .succ n, 0 => ackermann n 1
| .succ n, .succ m => ackermann n (ackermann (n + 1) m)
termination_by structural n
/--
error: argument #2 cannot be used for structural recursion
failed to eliminate recursive application
ackermann2 n 1
-/
#guard_msgs in
def ackermann2 (n m : Nat) := match n, m with
| 0, m => m + 1
| .succ n, 0 => ackermann2 n 1
| .succ n, .succ m => ackermann2 n (ackermann2 (n + 1) m)
termination_by structural m
/--
error: The termination argument of a structurally recursive function must be one of the parameters 'n', but
id n + 1
isn't one of these.
-/
#guard_msgs in
def foo4 (n : Nat) : Nat → Nat := match n with
| 0 => id
| n+1 => foo4 n
termination_by structural id n + 1
end Errors