lean4-htt/tests/lean/run/4751.lean
Joachim Breitner 671ce7afd3
fix: IndPred: track function's motive in a let binding, use withoutProofIrrelevance, no chaining (#4839)
this improves support for structural recursion over inductive
*predicates* when there are reflexive arguments.

Consider
```lean
inductive F: Prop where
  | base
  | step (fn: Nat → F)

-- set_option trace.Meta.IndPredBelow.search true
set_option pp.proofs true

def F.asdf1 : (f : F) → True
  | base => trivial
  | step f => F.asdf1 (f 0)
termination_by structural f => f`
```

Previously the search for the right induction hypothesis would fail with
```
could not solve using backwards chaining x✝¹ : F
x✝ : x✝¹.below
f : Nat → F
a✝¹ : ∀ (a : Nat), (f a).below
a✝ : Nat → True
⊢ True
```

The backchaining process will try to use `a✝ : Nat → True`, but then has
no idea what to use for `Nat`.

There are three steps here to fix this.

1. We let-bind the function's type before the whole process. Now the
   goal is

   ```
   funType : F → Prop := fun x => True
   x✝ : x✝¹.below
   f : Nat → F
   a✝¹ : ∀ (a : Nat), (f a).below
   a✝ : ∀ (a : Nat), funType (f a)
   ⊢ funType (f 0)
   ```
2. Instead of using the general purpose backchaining proof search, which
is more
powerful than we need here (we need on recursive search and no
backtracking),
   we have a custom search that looks for local assumptions that 
   provide evidence of `funType`, and extracts the arguments from that
   “type” application to construct the recursive call.

   Above, it will thus unify `f a =?= f 0`.

3. In order to make progress here, we also turn on use
`withoutProofIrrelevance`,
because else `isDefEq` is happy to say “they are equal” without actually
looking
   at the terms and thus assigning `?a := 0`.

This idea of let-binding the function's motive may also be useful for
the other recursion compilers, as it may simplify the FunInd
construction. This is to be investigated.

fixes #4751
2024-07-28 17:22:27 +00:00

50 lines
1.2 KiB
Text

inductive F: Prop where
| base
| step (fn: Nat → F)
-- set_option trace.Meta.IndPredBelow.search true
-- set_option trace.Elab.definition.structural true
set_option pp.proofs true
def F.asdf1 : (f : F) → True
| base => trivial
| step g => match g 1 with
| base => trivial
| step h => F.asdf1 (h 1)
termination_by structural f => f
def TTrue (_f : F) := True
def F.asdf2 : (f : F) → TTrue f
| base => trivial
| step f => F.asdf2 (f 0)
termination_by structural f => f
inductive ITrue (f : F) : Prop where | trivial
def F.asdf3 : (f : F) → ITrue f
| base => .trivial
| step f => F.asdf3 (f 0)
termination_by structural f => f
-- Variants with extra arguments
inductive T : Prop → Prop where
| base : T True
| step (fn: Nat → T (True → p)) : T p
def T.foo {P : Prop} : (f : T P) → P
| base => True.intro
| step f => foo (f 0) True.intro
termination_by structural f => f
-- The same, but as a non-reflexive data type
inductive T' : Prop → Prop where
| base : T' True
| step (t : T' (True → p)) : T' p
def T'.foo {P : Prop} : (f : T' P) → P
| base => True.intro
| step t => foo t True.intro
termination_by structural f => f