This adds support for mutual structural recursive functions. For now this is opt-in: The functions must have a `termination_by structural …` annotation (new since #4542) for this to work: ```lean mutual inductive A | self : A → A | other : B → A | empty inductive B | self : B → B | other : A → B | empty end mutual def A.size : A → Nat | .self a => a.size + 1 | .other b => b.size + 1 | .empty => 0 termination_by structural x => x def B.size : B → Nat | .self b => b.size + 1 | .other a => a.size + 1 | .empty => 0 termination_by structural x => x end ``` The recursive functions don’t have to be in a one-to-one relation to a set of mutually recursive inductive data types. It is possible to ignore some of the types: ```lean def A.self_size : A → Nat | .self a => a.self_size + 1 | .other _ => 0 | .empty => 0 termination_by structural x => x ``` or have more than one function per argument type: ```lean def isEven : Nat → Prop | 0 => True | n+1 => ¬ isOdd n termination_by structural x => x def isOdd : Nat → Prop | 0 => False | n+1 => ¬ isEven n termination_by structural x => x ``` This does not include * Support for nested inductive data types or nested recursion * Inferring mutual structural recursion in the absence of `termination_by`. * Functional induction principles for these. * Mutually recursive functions that live in different universes. This may be possible, maybe after beefing up the `.below` and `.brecOn` functions; we can look into this some other time, maybe when there are concrete use cases. --------- Co-authored-by: Richard Kiss <him@richardkiss.com> Co-authored-by: Tobias Grosser <tobias@grosser.es>
38 lines
1.5 KiB
Text
38 lines
1.5 KiB
Text
/-
|
|
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura
|
|
-/
|
|
prelude
|
|
import Lean.Expr
|
|
|
|
namespace Lean
|
|
|
|
structure HasConstCache (declNames : Array Name) where
|
|
cache : HashMapImp Expr Bool := mkHashMapImp
|
|
|
|
unsafe def HasConstCache.containsUnsafe (e : Expr) : StateM (HasConstCache declNames) Bool := do
|
|
if let some r := (← get).cache.find? (beq := ⟨ptrEq⟩) e then
|
|
return r
|
|
else
|
|
match e with
|
|
| .const n .. => return declNames.contains n
|
|
| .app f a => cache e (← containsUnsafe f <||> containsUnsafe a)
|
|
| .lam _ d b _ => cache e (← containsUnsafe d <||> containsUnsafe b)
|
|
| .forallE _ d b _ => cache e (← containsUnsafe d <||> containsUnsafe b)
|
|
| .letE _ t v b _ => cache e (← containsUnsafe t <||> containsUnsafe v <||> containsUnsafe b)
|
|
| .mdata _ b => cache e (← containsUnsafe b)
|
|
| .proj _ _ b => cache e (← containsUnsafe b)
|
|
| _ => return false
|
|
where
|
|
cache (e : Expr) (r : Bool) : StateM (HasConstCache declNames) Bool := do
|
|
modify fun ⟨cache⟩ => ⟨cache.insert (beq := ⟨ptrEq⟩) e r |>.1⟩
|
|
return r
|
|
|
|
/--
|
|
Return true iff `e` contains the constant `declName`.
|
|
Remark: the results for visited expressions are stored in the state cache. -/
|
|
@[implemented_by HasConstCache.containsUnsafe]
|
|
opaque HasConstCache.contains (e : Expr) : StateM (HasConstCache declNames) Bool
|
|
|
|
end Lean
|