lean4-htt/src/Lean/Meta/CtorRecognizer.lean
Kyle Miller 23236ef520
fix: have Lean.Meta.isConstructorApp'? be aware of n + k Nat offsets (#6270)
This PR fixes a bug that could cause the `injectivity` tactic to fail in
reducible mode, which could cause unfolding lemma generation to fail
(used by tactics such as `unfold`). In particular,
`Lean.Meta.isConstructorApp'?` was not aware that `n + 1` is equivalent
to `Nat.succ n`.

Closes #5064
2024-12-01 18:04:32 +00:00

112 lines
3.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.LitValues
import Lean.Meta.Offset
namespace Lean.Meta
private def getConstructorVal? (env : Environment) (ctorName : Name) : Option ConstructorVal :=
match env.find? ctorName with
| some (.ctorInfo v) => v
| _ => none
/--
If `e` is a constructor application,
then return the corresponding `ConstructorVal`.
-/
def isConstructorAppCore? (e : Expr) : MetaM (Option ConstructorVal) := do
let .const n _ := e.getAppFn | return none
let some v := getConstructorVal? (← getEnv) n | return none
if v.numParams + v.numFields == e.getAppNumArgs then
return some v
else
return none
/--
If `e` is a constructor application or a builtin literal defeq to a constructor application,
then return the corresponding `ConstructorVal`.
-/
def isConstructorApp? (e : Expr) : MetaM (Option ConstructorVal) := do
isConstructorAppCore? (← litToCtor e)
/--
Similar to `isConstructorApp?`, but uses `whnf`.
It also uses `isOffset?` for `Nat`.
See also `Lean.Meta.constructorApp'?`.
-/
def isConstructorApp'? (e : Expr) : MetaM (Option ConstructorVal) := do
if let some (_, k) ← isOffset? e then
if k = 0 then
return none
else
let .ctorInfo val ← getConstInfo ``Nat.succ | return none
return some val
else if let some r ← isConstructorApp? e then
return r
else try
/-
We added the `try` block here because `whnf` fails at terms `n ^ m`
when `m` is a big numeral, and `n` is a numeral. This is a little bit hackish.
-/
isConstructorApp? (← whnf e)
catch _ =>
return none
/--
Returns `true`, if `e` is constructor application of builtin literal defeq to
a constructor application.
-/
def isConstructorApp (e : Expr) : MetaM Bool :=
return (← isConstructorApp? e).isSome
/--
Returns `true` if `isConstructorApp e` or `isConstructorApp (← whnf e)`
-/
def isConstructorApp' (e : Expr) : MetaM Bool := do
if (← isConstructorApp e) then return true
return (← isConstructorApp (← whnf e))
/--
If `e` is a constructor application, return a pair containing the corresponding `ConstructorVal` and the constructor
application arguments.
-/
def constructorApp? (e : Expr) : MetaM (Option (ConstructorVal × Array Expr)) := do
let e ← litToCtor e
let .const declName _ := e.getAppFn | return none
let some v := getConstructorVal? (← getEnv) declName | return none
if v.numParams + v.numFields == e.getAppNumArgs then
return some (v, e.getAppArgs)
else
return none
/--
Similar to `constructorApp?`, but on failure it puts `e` in WHNF and tries again.
It also uses `isOffset?` for `Nat`.
See also `Lean.Meta.isConstructorApp'?`.
-/
def constructorApp'? (e : Expr) : MetaM (Option (ConstructorVal × Array Expr)) := do
if let some (e, k) ← isOffset? e then
if k = 0 then
return none
else
let .ctorInfo val ← getConstInfo ``Nat.succ | return none
if k = 1 then return some (val, #[e])
else return some (val, #[mkNatAdd e (toExpr (k-1))])
else if let some r ← constructorApp? e then
return some r
else try
/-
We added the `try` block here because `whnf` fails at terms `n ^ m`
when `m` is a big numeral, and `n` is a numeral. This is a little bit hackish.
-/
constructorApp? (← whnf e)
catch _ =>
return none
end Lean.Meta