diff --git a/src/Lean/Meta/IndPredBelow.lean b/src/Lean/Meta/IndPredBelow.lean index 3145f29d31..7118a32058 100644 --- a/src/Lean/Meta/IndPredBelow.lean +++ b/src/Lean/Meta/IndPredBelow.lean @@ -571,10 +571,13 @@ def findBelowIdx (xs : Array Expr) (motive : Expr) : MetaM $ Option (Expr × Nat else pure none | _, _ => pure none +/-- Generates the auxiliary lemmas `below` and `brecOn` for a recursive inductive predicate. +The current generator doesn't support nested predicates, but pattern-matching on them still works +thanks to well-founded recursion. -/ def mkBelow (declName : Name) : MetaM Unit := do if (← isInductivePredicate declName) then let x ← getConstInfoInduct declName - if x.isRec then + if x.isRec && !x.isNested then let ctx ← IndPredBelow.mkContext declName let decl ← IndPredBelow.mkBelowDecl ctx addDecl decl @@ -585,7 +588,7 @@ def mkBelow (declName : Name) : MetaM Unit := do let decl ← IndPredBelow.mkBrecOnDecl ctx i addDecl decl catch e => trace[Meta.IndPredBelow] "failed to prove brecOn for {ctx.belowNames[i]!}\n{e.toMessageData}" - else trace[Meta.IndPredBelow] "Not recursive" + else trace[Meta.IndPredBelow] "Nested or not recursive" else trace[Meta.IndPredBelow] "Not inductive predicate" builtin_initialize diff --git a/tests/lean/run/2389.lean b/tests/lean/run/2389.lean new file mode 100644 index 0000000000..6923e6c5e6 --- /dev/null +++ b/tests/lean/run/2389.lean @@ -0,0 +1,35 @@ +/-! +# Verify that nested predicates don't trigger the generation of `below` lemmas +Since the case of nested predicates is not currently handled by `mkBelow` (in `src/Lean/Meta/IndPredBelow.lean`), +trying to generate `OnlyZeros.below` triggers an error upon defining the inductive type. +-/ + +inductive Forall (P : α → Prop) : List α → Prop + | nil : Forall P [] + | cons : {x : α} → P x → Forall P l → Forall P (x::l) + +inductive Tree : Type := + | leaf : Nat → Tree + | node : List Tree → Tree + +set_option trace.Meta.IndPredBelow true in + +/-- Despite not having `.below` and `.brecOn`, +the type is still usable thanks to well-founded recursion. -/ +inductive OnlyZeros : Tree → Prop := + | leaf : OnlyZeros (.leaf 0) + | node (l : List Tree): Forall OnlyZeros l → OnlyZeros (.node l) + +/-- Equivalent definition of `OnlyZeros`, defined by a function instead of an inductive type. -/ +def onlyZeros : Tree → Prop + | .leaf n => n = 0 + | .node [] => True + | .node (x::s) => onlyZeros x ∧ onlyZeros (.node s) + +/-- Pattern-matching on `OnlyZeros` works despite `below` and `brecOn` not being generated. -/ +def toFixPoint : OnlyZeros t → onlyZeros t + | .leaf => rfl + | .node [] _ => True.intro + | .node (x::s) (.cons h p) => by + rw [onlyZeros] -- necessary because `onlyZeros` isn't structurally recursive + exact And.intro (toFixPoint h) (toFixPoint (.node s p)) diff --git a/tests/lean/run/2389.lean.expected.out b/tests/lean/run/2389.lean.expected.out new file mode 100644 index 0000000000..635fa04e07 --- /dev/null +++ b/tests/lean/run/2389.lean.expected.out @@ -0,0 +1 @@ +[Meta.IndPredBelow] Nested or not recursive