Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. Β See raw diff
- .gitattributes +3 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/CheckTactic.lean +24 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MVarRenaming.lean +36 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatchPatternAttr.lean +37 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherApp/Basic.lean +80 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherApp/Transform.lean +443 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherInfo.lean +134 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/Value.lean +27 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AC.lean +7 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AC/Main.lean +242 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Acyclic.lean +60 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Apply.lean +377 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Assert.lean +145 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Assumption.lean +34 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AuxLemma.lean +62 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Backtrack.lean +205 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Cases.lean +361 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Cleanup.lean +75 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Clear.lean +73 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Congr.lean +118 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Constructor.lean +49 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Contradiction.lean +232 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Delta.lean +43 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/ElimInfo.lean +281 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/ExposeNames.lean +66 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Ext.lean +85 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FVarSubst.lean +84 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunInd.lean +1715 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunIndCollect.lean +115 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunIndInfo.lean +86 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Generalize.lean +152 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind.lean +89 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/AlphaShareCommon.lean +112 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith.lean +15 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Attr.lean +125 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Beta.lean +77 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Canon.lean +211 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Cases.lean +191 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/CasesMatch.lean +82 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Core.lean +431 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Ctor.lean +60 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Diseq.lean +86 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EMatch.lean +577 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EMatchTheorem.lean +1277 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EqResolution.lean +133 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ExprPtr.lean +33 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Ext.lean +41 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ExtAttr.lean +44 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ForallProp.lean +126 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Injection.lean +39 -0
.gitattributes
CHANGED
|
@@ -4503,3 +4503,6 @@ external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/
|
|
| 4503 |
external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans.ttf filter=lfs diff=lfs merge=lfs -text
|
| 4504 |
external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 4505 |
external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 4503 |
external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans.ttf filter=lfs diff=lfs merge=lfs -text
|
| 4504 |
external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 4505 |
external/alphageometry/.venv-ag/Lib/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf filter=lfs diff=lfs merge=lfs -text
|
| 4506 |
+
external/alphageometry/.venv-ag/Lib/site-packages/scipy.libs/libscipy_openblas-f07f5a5d207a3a47104dca54d6d0c86a.dll filter=lfs diff=lfs merge=lfs -text
|
| 4507 |
+
external/alphageometry/.venv-ag/Lib/site-packages/scipy/sparse/linalg/tests/propack_test_data.npz filter=lfs diff=lfs merge=lfs -text
|
| 4508 |
+
external/alphageometry/.venv-ag/Lib/site-packages/numpy/_core/lib/npymath.lib filter=lfs diff=lfs merge=lfs -text
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/CheckTactic.lean
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Joe Hendrix
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.CheckTactic
|
| 10 |
+
|
| 11 |
+
def mkCheckGoalType (val type : Expr) : MetaM Expr := do
|
| 12 |
+
let lvl β mkFreshLevelMVar
|
| 13 |
+
pure <| mkApp2 (mkConst ``CheckGoalType [lvl]) type val
|
| 14 |
+
|
| 15 |
+
def matchCheckGoalType (stx : Syntax) (goalType : Expr) : MetaM (Expr Γ Expr Γ Level) := do
|
| 16 |
+
let u β mkFreshLevelMVar
|
| 17 |
+
let type β mkFreshExprMVar (some (.sort u))
|
| 18 |
+
let val β mkFreshExprMVar (some type)
|
| 19 |
+
let extType := mkAppN (.const ``CheckGoalType [u]) #[type, val]
|
| 20 |
+
if !(β isDefEq goalType extType) then
|
| 21 |
+
throwErrorAt stx "Goal{indentExpr goalType}\nis expected to match {indentExpr extType}"
|
| 22 |
+
pure (val, type, u)
|
| 23 |
+
|
| 24 |
+
end Lean.Meta.CheckTactic
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MVarRenaming.lean
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.ReplaceExpr
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta
|
| 10 |
+
|
| 11 |
+
/-- A mapping from MVarId to MVarId -/
|
| 12 |
+
structure MVarRenaming where
|
| 13 |
+
map : MVarIdMap MVarId := {}
|
| 14 |
+
|
| 15 |
+
def MVarRenaming.isEmpty (s : MVarRenaming) : Bool :=
|
| 16 |
+
s.map.isEmpty
|
| 17 |
+
|
| 18 |
+
def MVarRenaming.find? (s : MVarRenaming) (mvarId : MVarId) : Option MVarId :=
|
| 19 |
+
s.map.find? mvarId
|
| 20 |
+
|
| 21 |
+
def MVarRenaming.find! (s : MVarRenaming) (mvarId : MVarId) : MVarId :=
|
| 22 |
+
(s.find? mvarId).get!
|
| 23 |
+
|
| 24 |
+
def MVarRenaming.insert (s : MVarRenaming) (mvarId mvarId' : MVarId) : MVarRenaming :=
|
| 25 |
+
{ s with map := s.map.insert mvarId mvarId' }
|
| 26 |
+
|
| 27 |
+
def MVarRenaming.apply (s : MVarRenaming) (e : Expr) : Expr :=
|
| 28 |
+
if !e.hasMVar then e
|
| 29 |
+
else if s.map.isEmpty then e
|
| 30 |
+
else e.replace fun e => match e with
|
| 31 |
+
| Expr.mvar mvarId => match s.map.find? mvarId with
|
| 32 |
+
| none => e
|
| 33 |
+
| some newMVarId => mkMVar newMVarId
|
| 34 |
+
| _ => none
|
| 35 |
+
|
| 36 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatchPatternAttr.lean
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Attributes
|
| 8 |
+
|
| 9 |
+
namespace Lean
|
| 10 |
+
|
| 11 |
+
/--
|
| 12 |
+
Instructs the pattern matcher to unfold occurrences of this definition.
|
| 13 |
+
|
| 14 |
+
By default, only constructors and literals can be used for pattern matching. Using
|
| 15 |
+
`@[match_pattern]` allows using other definitions, as long as they eventually reduce to
|
| 16 |
+
constructors and literals.
|
| 17 |
+
|
| 18 |
+
Example:
|
| 19 |
+
```
|
| 20 |
+
@[match_pattern]
|
| 21 |
+
def yellowString : String := "yellow"
|
| 22 |
+
|
| 23 |
+
def isYellow (color : String) : Bool :=
|
| 24 |
+
match color with
|
| 25 |
+
| yellowString => true
|
| 26 |
+
| _ => false
|
| 27 |
+
```
|
| 28 |
+
-/
|
| 29 |
+
@[builtin_doc]
|
| 30 |
+
builtin_initialize matchPatternAttr : TagAttribute β
|
| 31 |
+
registerTagAttribute `match_pattern "mark that a definition can be used in a pattern (remark: the dependent pattern matching compiler will unfold the definition)"
|
| 32 |
+
|
| 33 |
+
@[export lean_has_match_pattern_attribute]
|
| 34 |
+
def hasMatchPatternAttribute (env : Environment) (n : Name) : Bool :=
|
| 35 |
+
matchPatternAttr.hasTag env n
|
| 36 |
+
|
| 37 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherApp/Basic.lean
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Match.MatcherInfo
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta
|
| 10 |
+
|
| 11 |
+
structure MatcherApp where
|
| 12 |
+
matcherName : Name
|
| 13 |
+
matcherLevels : Array Level
|
| 14 |
+
uElimPos? : Option Nat
|
| 15 |
+
discrInfos : Array Match.DiscrInfo
|
| 16 |
+
params : Array Expr
|
| 17 |
+
motive : Expr
|
| 18 |
+
discrs : Array Expr
|
| 19 |
+
altNumParams : Array Nat
|
| 20 |
+
alts : Array Expr
|
| 21 |
+
remaining : Array Expr
|
| 22 |
+
|
| 23 |
+
/--
|
| 24 |
+
Recognizes if `e` is a matcher application, and destructs it into the `MatcherApp` data structure.
|
| 25 |
+
|
| 26 |
+
This can optionally also treat `casesOn` recursor applications as a special case
|
| 27 |
+
of matcher applications.
|
| 28 |
+
-/
|
| 29 |
+
def matchMatcherApp? [Monad m] [MonadEnv m] [MonadError m] (e : Expr) (alsoCasesOn := false) :
|
| 30 |
+
m (Option MatcherApp) := do
|
| 31 |
+
if let .const declName declLevels := e.getAppFn then
|
| 32 |
+
if let some info β getMatcherInfo? declName then
|
| 33 |
+
let args := e.getAppArgs
|
| 34 |
+
if args.size < info.arity then
|
| 35 |
+
return none
|
| 36 |
+
return some {
|
| 37 |
+
matcherName := declName
|
| 38 |
+
matcherLevels := declLevels.toArray
|
| 39 |
+
uElimPos? := info.uElimPos?
|
| 40 |
+
discrInfos := info.discrInfos
|
| 41 |
+
params := args.extract 0 info.numParams
|
| 42 |
+
motive := args[info.getMotivePos]!
|
| 43 |
+
discrs := args[(info.numParams + 1)...(info.numParams + 1 + info.numDiscrs)]
|
| 44 |
+
altNumParams := info.altNumParams
|
| 45 |
+
alts := args[(info.numParams + 1 + info.numDiscrs)...(info.numParams + 1 + info.numDiscrs + info.numAlts)]
|
| 46 |
+
remaining := args[(info.numParams + 1 + info.numDiscrs + info.numAlts)...args.size]
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
if alsoCasesOn && isCasesOnRecursor (β getEnv) declName then
|
| 50 |
+
let indName := declName.getPrefix
|
| 51 |
+
let .inductInfo info β getConstInfo indName | return none
|
| 52 |
+
let args := e.getAppArgs
|
| 53 |
+
unless args.size >= info.numParams + 1 /- motive -/ + info.numIndices + 1 /- major -/ + info.numCtors do return none
|
| 54 |
+
let params := args[*...info.numParams]
|
| 55 |
+
let motive := args[info.numParams]!
|
| 56 |
+
let discrs := args[(info.numParams + 1)...(info.numParams + 1 + info.numIndices + 1)]
|
| 57 |
+
let discrInfos := .replicate (info.numIndices + 1) {}
|
| 58 |
+
let alts := args[(info.numParams + 1 + info.numIndices + 1)...(info.numParams + 1 + info.numIndices + 1 + info.numCtors)]
|
| 59 |
+
let remaining := args[(info.numParams + 1 + info.numIndices + 1 + info.numCtors)...*]
|
| 60 |
+
let uElimPos? := if info.levelParams.length == declLevels.length then none else some 0
|
| 61 |
+
let mut altNumParams := #[]
|
| 62 |
+
for ctor in info.ctors do
|
| 63 |
+
let .ctorInfo ctorInfo β getConstInfo ctor | unreachable!
|
| 64 |
+
altNumParams := altNumParams.push ctorInfo.numFields
|
| 65 |
+
return some {
|
| 66 |
+
matcherName := declName
|
| 67 |
+
matcherLevels := declLevels.toArray
|
| 68 |
+
uElimPos?, discrInfos, params, motive, discrs, alts, remaining, altNumParams
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
return none
|
| 72 |
+
|
| 73 |
+
def MatcherApp.toExpr (matcherApp : MatcherApp) : Expr :=
|
| 74 |
+
let result := mkAppN (mkConst matcherApp.matcherName matcherApp.matcherLevels.toList) matcherApp.params
|
| 75 |
+
let result := mkApp result matcherApp.motive
|
| 76 |
+
let result := mkAppN result matcherApp.discrs
|
| 77 |
+
let result := mkAppN result matcherApp.alts
|
| 78 |
+
mkAppN result matcherApp.remaining
|
| 79 |
+
|
| 80 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherApp/Transform.lean
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Joachim Breitner
|
| 5 |
+
-/
|
| 6 |
+
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Meta.Match
|
| 9 |
+
import Lean.Meta.InferType
|
| 10 |
+
import Lean.Meta.Check
|
| 11 |
+
import Lean.Meta.Tactic.Split
|
| 12 |
+
|
| 13 |
+
namespace Lean.Meta.MatcherApp
|
| 14 |
+
|
| 15 |
+
/-- Auxiliary function for MatcherApp.addArg -/
|
| 16 |
+
private partial def updateAlts (unrefinedArgType : Expr) (typeNew : Expr) (altNumParams : Array Nat) (alts : Array Expr) (refined : Bool) (i : Nat) : MetaM (Array Nat Γ Array Expr) := do
|
| 17 |
+
if h : i < alts.size then
|
| 18 |
+
let alt := alts[i]
|
| 19 |
+
let numParams := altNumParams[i]!
|
| 20 |
+
let typeNew β whnfD typeNew
|
| 21 |
+
match typeNew with
|
| 22 |
+
| Expr.forallE _ d b _ =>
|
| 23 |
+
let (alt, refined) β forallBoundedTelescope d (some numParams) fun xs d => do
|
| 24 |
+
let alt β try instantiateLambda alt xs catch _ => throwError "unexpected matcher application, insufficient number of parameters in alternative"
|
| 25 |
+
forallBoundedTelescope d (some 1) fun x _ => do
|
| 26 |
+
let alt β mkLambdaFVars x alt -- x is the new argument we are adding to the alternative
|
| 27 |
+
let refined β if refined then
|
| 28 |
+
pure refined
|
| 29 |
+
else
|
| 30 |
+
pure <| !(β isDefEq unrefinedArgType (β inferType x[0]!))
|
| 31 |
+
return (β mkLambdaFVars xs alt, refined)
|
| 32 |
+
updateAlts unrefinedArgType (b.instantiate1 alt) (altNumParams.set! i (numParams+1)) (alts.set i alt) refined (i+1)
|
| 33 |
+
| _ => throwError "unexpected type at MatcherApp.addArg"
|
| 34 |
+
else
|
| 35 |
+
if refined then
|
| 36 |
+
return (altNumParams, alts)
|
| 37 |
+
else
|
| 38 |
+
throwError "failed to add argument to matcher application, argument type was not refined by `casesOn`"
|
| 39 |
+
|
| 40 |
+
/--
|
| 41 |
+
Given
|
| 42 |
+
- matcherApp `match_i As (fun xs => motive[xs]) discrs (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining`, and
|
| 43 |
+
- expression `e : B[discrs]`,
|
| 44 |
+
Construct the term
|
| 45 |
+
`match_i As (fun xs => B[xs] -> motive[xs]) discrs (fun ys_1 (y : B[C_1[ys_1]]) => alt_1) ... (fun ys_n (y : B[C_n[ys_n]]) => alt_n) e remaining`.
|
| 46 |
+
|
| 47 |
+
We only abstract discriminants that are fvars. We used to use `kabstract` to abstract all
|
| 48 |
+
discriminants from `B[discrs]`, but that changes the type of the arg in ways that make it no
|
| 49 |
+
longer compatible with the original recursive function (issue #7322).
|
| 50 |
+
|
| 51 |
+
If this is still not great, then we could try to use `kabstract`, but only on the last parameter
|
| 52 |
+
of the `arg` (the termination proof obligation).
|
| 53 |
+
|
| 54 |
+
This method assumes
|
| 55 |
+
- the `matcherApp.motive` is a lambda abstraction where `xs.size == discrs.size`
|
| 56 |
+
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
|
| 57 |
+
|
| 58 |
+
This is used in `Lean.Elab.PreDefinition.WF.Fix` when replacing recursive calls with calls to
|
| 59 |
+
the argument provided by `fix` to refine type of the local variable used for recursive calls,
|
| 60 |
+
which may mention `major`. See there for how to use this function.
|
| 61 |
+
-/
|
| 62 |
+
def addArg (matcherApp : MatcherApp) (e : Expr) : MetaM MatcherApp :=
|
| 63 |
+
lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do
|
| 64 |
+
unless motiveArgs.size == matcherApp.discrs.size do
|
| 65 |
+
-- This error can only happen if someone implemented a transformation that rewrites the motive created by `mkMatcher`.
|
| 66 |
+
throwError "unexpected matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
|
| 67 |
+
let eType β inferType e
|
| 68 |
+
let eTypeAbst := matcherApp.discrs.size.foldRev (init := eType) fun i _ eTypeAbst =>
|
| 69 |
+
let discr := matcherApp.discrs[i]
|
| 70 |
+
if discr.isFVar then
|
| 71 |
+
let motiveArg := motiveArgs[i]!
|
| 72 |
+
eTypeAbst.replaceFVar discr motiveArg
|
| 73 |
+
else
|
| 74 |
+
eTypeAbst
|
| 75 |
+
let motiveBody β mkArrow eTypeAbst motiveBody
|
| 76 |
+
let matcherLevels β match matcherApp.uElimPos? with
|
| 77 |
+
| none => pure matcherApp.matcherLevels
|
| 78 |
+
| some pos =>
|
| 79 |
+
let uElim β getLevel motiveBody
|
| 80 |
+
pure <| matcherApp.matcherLevels.set! pos uElim
|
| 81 |
+
let motive β mkLambdaFVars motiveArgs motiveBody
|
| 82 |
+
-- Construct `aux` `match_i As (fun xs => B[xs] β motive[xs]) discrs`, and infer its type `auxType`.
|
| 83 |
+
-- We use `auxType` to infer the type `B[C_i[ys_i]]` of the new argument in each alternative.
|
| 84 |
+
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) matcherApp.params
|
| 85 |
+
let aux := mkApp aux motive
|
| 86 |
+
let aux := mkAppN aux matcherApp.discrs
|
| 87 |
+
unless (β isTypeCorrect aux) do
|
| 88 |
+
throwError "failed to add argument to matcher application, type error when constructing the new motive"
|
| 89 |
+
let auxType β inferType aux
|
| 90 |
+
let (altNumParams, alts) β updateAlts eType auxType matcherApp.altNumParams matcherApp.alts false 0
|
| 91 |
+
return { matcherApp with
|
| 92 |
+
matcherLevels := matcherLevels,
|
| 93 |
+
motive := motive,
|
| 94 |
+
alts := alts,
|
| 95 |
+
altNumParams := altNumParams,
|
| 96 |
+
remaining := #[e] ++ matcherApp.remaining
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
/-- Similar to `MatcherApp.addArg`, but returns `none` on failure. -/
|
| 100 |
+
def addArg? (matcherApp : MatcherApp) (e : Expr) : MetaM (Option MatcherApp) :=
|
| 101 |
+
try
|
| 102 |
+
return some (β matcherApp.addArg e)
|
| 103 |
+
catch _ =>
|
| 104 |
+
return none
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
/-- Given
|
| 108 |
+
- matcherApp `match_i As (fun xs => motive[xs]) discrs (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining`, and
|
| 109 |
+
- a expression `B[discrs]` (which may not be a type, e.g. `n : Nat`),
|
| 110 |
+
returns the expressions `fun ys_1 ... ys_i => B[C_1[ys_1]] ... B[C_n[ys_n]]`,
|
| 111 |
+
|
| 112 |
+
This method assumes
|
| 113 |
+
- the `matcherApp.motive` is a lambda abstraction where `xs.size == discrs.size`
|
| 114 |
+
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
|
| 115 |
+
|
| 116 |
+
This is similar to `MatcherApp.addArg` when you only have an expression to
|
| 117 |
+
refined, and not a type with a value.
|
| 118 |
+
|
| 119 |
+
This is used in `Lean.Elab.PreDefinition.WF.GuessFix` when constructing the context of recursive
|
| 120 |
+
calls to refine the functions' parameter, which may mention `major`.
|
| 121 |
+
See there for how to use this function.
|
| 122 |
+
-/
|
| 123 |
+
def refineThrough (matcherApp : MatcherApp) (e : Expr) : MetaM (Array Expr) :=
|
| 124 |
+
lambdaTelescope matcherApp.motive fun motiveArgs _motiveBody => do
|
| 125 |
+
unless motiveArgs.size == matcherApp.discrs.size do
|
| 126 |
+
-- This error can only happen if someone implemented a transformation that rewrites the motive created by `mkMatcher`.
|
| 127 |
+
throwError "failed to transfer argument through matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
|
| 128 |
+
|
| 129 |
+
let eAbst β matcherApp.discrs.size.foldRevM (init := e) fun i _ eAbst => do
|
| 130 |
+
let motiveArg := motiveArgs[i]!
|
| 131 |
+
let discr := matcherApp.discrs[i]
|
| 132 |
+
let eTypeAbst β kabstract eAbst discr
|
| 133 |
+
return eTypeAbst.instantiate1 motiveArg
|
| 134 |
+
-- Let's create something thatβs a `Sort` and mentions `e`
|
| 135 |
+
-- (recall that `e` itself possibly isn't a type),
|
| 136 |
+
-- by writing `e = e`, so that we can use it as a motive
|
| 137 |
+
let eEq β mkEq eAbst eAbst
|
| 138 |
+
|
| 139 |
+
let matcherLevels β match matcherApp.uElimPos? with
|
| 140 |
+
| none => pure matcherApp.matcherLevels
|
| 141 |
+
| some pos =>
|
| 142 |
+
pure <| matcherApp.matcherLevels.set! pos levelZero
|
| 143 |
+
let motive β mkLambdaFVars motiveArgs eEq
|
| 144 |
+
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) matcherApp.params
|
| 145 |
+
let aux := mkApp aux motive
|
| 146 |
+
let aux := mkAppN aux matcherApp.discrs
|
| 147 |
+
unless (β isTypeCorrect aux) do
|
| 148 |
+
throwError "failed to transfer argument through matcher application, type error when constructing the new motive"
|
| 149 |
+
let auxType β inferType aux
|
| 150 |
+
forallTelescope auxType fun altAuxs _ => do
|
| 151 |
+
let altAuxTys β altAuxs.mapM (inferType Β·)
|
| 152 |
+
(Array.zip matcherApp.altNumParams altAuxTys).mapM fun (altNumParams, altAuxTy) => do
|
| 153 |
+
forallBoundedTelescope altAuxTy altNumParams fun fvs body => do
|
| 154 |
+
unless fvs.size = altNumParams do
|
| 155 |
+
throwError "failed to transfer argument through matcher application, alt type must be telescope with #{altNumParams} arguments"
|
| 156 |
+
-- extract type from our synthetic equality
|
| 157 |
+
let body := body.getArg! 2
|
| 158 |
+
-- and abstract over the parameters of the alternatives, so that we can safely pass the Expr out
|
| 159 |
+
mkLambdaFVars fvs body
|
| 160 |
+
|
| 161 |
+
/-- A non-failing version of `MatcherApp.refineThrough` -/
|
| 162 |
+
def refineThrough? (matcherApp : MatcherApp) (e : Expr) :
|
| 163 |
+
MetaM (Option (Array Expr)) :=
|
| 164 |
+
try
|
| 165 |
+
return some (β matcherApp.refineThrough e)
|
| 166 |
+
catch _ =>
|
| 167 |
+
return none
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
private def withUserNamesImpl {Ξ±} (fvars : Array Expr) (names : Array Name) (k : MetaM Ξ±) : MetaM Ξ± := do
|
| 171 |
+
let lctx := (Array.zip fvars names).foldl (init := β (getLCtx)) fun lctx (fvar, name) =>
|
| 172 |
+
lctx.setUserName fvar.fvarId! name
|
| 173 |
+
withLCtx' lctx k
|
| 174 |
+
|
| 175 |
+
/--
|
| 176 |
+
Sets the user name of the FVars in the local context according to the given array of names.
|
| 177 |
+
|
| 178 |
+
If they differ in size the shorter size wins.
|
| 179 |
+
-/
|
| 180 |
+
def withUserNames {n} [MonadControlT MetaM n] [Monad n]
|
| 181 |
+
{Ξ±} (fvars : Array Expr) (names : Array Name) (k : n Ξ±) : n Ξ± := do
|
| 182 |
+
mapMetaM (withUserNamesImpl fvars names) k
|
| 183 |
+
|
| 184 |
+
/-
|
| 185 |
+
`Match.forallAltTelescope` lifted to a monad transformer
|
| 186 |
+
(and only passing those arguments that we care about below)
|
| 187 |
+
-/
|
| 188 |
+
private def forallAltTelescope'
|
| 189 |
+
{n} [Monad n] [MonadControlT MetaM n]
|
| 190 |
+
{Ξ±} (origAltType : Expr) (numParams numDiscrEqs : Nat)
|
| 191 |
+
(k : Array Expr β Array Expr β n Ξ±) : n Ξ± := do
|
| 192 |
+
map2MetaM (fun k =>
|
| 193 |
+
Match.forallAltVarsTelescope origAltType numParams numDiscrEqs
|
| 194 |
+
fun ys args _mask _bodyType => k ys args
|
| 195 |
+
) k
|
| 196 |
+
|
| 197 |
+
/--
|
| 198 |
+
Performs a possibly type-changing transformation to a `MatcherApp`.
|
| 199 |
+
|
| 200 |
+
* `onParams` is run on each parameter and discriminant
|
| 201 |
+
* `onMotive` runs on the body of the motive, and is passed the motive parameters
|
| 202 |
+
(one for each `MatcherApp.discrs`)
|
| 203 |
+
* `onAlt` runs on each alternative, and is passed the expected type of the alternative,
|
| 204 |
+
as inferred from the motive
|
| 205 |
+
* `onRemaining` runs on the remaining arguments (and may change their number)
|
| 206 |
+
|
| 207 |
+
If `useSplitter` is true, the matcher is replaced with the splitter.
|
| 208 |
+
NB: Not all operations on `MatcherApp` can handle one `matcherName` is a splitter.
|
| 209 |
+
|
| 210 |
+
If `addEqualities` is true, then equalities connecting the discriminant to the parameters of the
|
| 211 |
+
alternative (like in `match h : x with β¦`) are be added, if not already there.
|
| 212 |
+
|
| 213 |
+
This function works even if the type of alternatives do *not* fit the inferred type. This
|
| 214 |
+
allows you to post-process the `MatcherApp` with `MatcherApp.inferMatchType`, which will
|
| 215 |
+
infer a type, given all the alternatives.
|
| 216 |
+
-/
|
| 217 |
+
def transform
|
| 218 |
+
{n} [MonadLiftT MetaM n] [MonadControlT MetaM n] [Monad n] [MonadError n] [MonadEnv n] [MonadLog n]
|
| 219 |
+
[AddMessageContext n] [MonadOptions n]
|
| 220 |
+
(matcherApp : MatcherApp)
|
| 221 |
+
(useSplitter := false)
|
| 222 |
+
(addEqualities : Bool := false)
|
| 223 |
+
(onParams : Expr β n Expr := pure)
|
| 224 |
+
(onMotive : Array Expr β Expr β n Expr := fun _ e => pure e)
|
| 225 |
+
(onAlt : Nat β Expr β Expr β n Expr := fun _ _ e => pure e)
|
| 226 |
+
(onRemaining : Array Expr β n (Array Expr) := pure) :
|
| 227 |
+
n MatcherApp := do
|
| 228 |
+
|
| 229 |
+
-- We also handle CasesOn applications here, and need to treat them specially in a
|
| 230 |
+
-- few places.
|
| 231 |
+
-- TODO: Expand MatcherApp with the necessary fields to make this more uniform
|
| 232 |
+
-- (in particular, include discrEq and whether there is a splitter)
|
| 233 |
+
let isCasesOn := isCasesOnRecursor (β getEnv) matcherApp.matcherName
|
| 234 |
+
|
| 235 |
+
let numDiscrEqs β
|
| 236 |
+
if isCasesOn then pure 0 else
|
| 237 |
+
match β getMatcherInfo? matcherApp.matcherName with
|
| 238 |
+
| some info => pure info.getNumDiscrEqs
|
| 239 |
+
| none => throwError "matcher {matcherApp.matcherName} has no MatchInfo found"
|
| 240 |
+
|
| 241 |
+
let params' β matcherApp.params.mapM onParams
|
| 242 |
+
let discrs' β matcherApp.discrs.mapM onParams
|
| 243 |
+
|
| 244 |
+
let (motive', uElim, addHEqualities) β lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do
|
| 245 |
+
unless motiveArgs.size == matcherApp.discrs.size do
|
| 246 |
+
throwError "unexpected matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
|
| 247 |
+
let mut motiveBody' β onMotive motiveArgs motiveBody
|
| 248 |
+
|
| 249 |
+
-- Prepend `(x = e) β` or `(x β e) β ` to the motive when an equality is requested
|
| 250 |
+
-- and not already present, and remember whether we added an Eq or a HEq
|
| 251 |
+
let mut addHEqualities : Array (Option Bool) := #[]
|
| 252 |
+
for arg in motiveArgs, discr in discrs', di in matcherApp.discrInfos do
|
| 253 |
+
if addEqualities && di.hName?.isNone then
|
| 254 |
+
if β isProof arg then
|
| 255 |
+
addHEqualities := addHEqualities.push none
|
| 256 |
+
else
|
| 257 |
+
let heq β mkEqHEq discr arg
|
| 258 |
+
motiveBody' β liftMetaM <| mkArrow heq motiveBody'
|
| 259 |
+
addHEqualities := addHEqualities.push heq.isHEq
|
| 260 |
+
else
|
| 261 |
+
addHEqualities := addHEqualities.push none
|
| 262 |
+
|
| 263 |
+
return (β mkLambdaFVars motiveArgs motiveBody', β getLevel motiveBody', addHEqualities)
|
| 264 |
+
|
| 265 |
+
let matcherLevels β match matcherApp.uElimPos? with
|
| 266 |
+
| none => pure matcherApp.matcherLevels
|
| 267 |
+
| some pos => pure <| matcherApp.matcherLevels.set! pos uElim
|
| 268 |
+
|
| 269 |
+
-- We pass `Eq.refl`s for all the equations we added as extra arguments
|
| 270 |
+
-- (and count them along the way)
|
| 271 |
+
let mut remaining' := #[]
|
| 272 |
+
let mut extraEqualities : Nat := 0
|
| 273 |
+
for discr in discrs'.reverse, b in addHEqualities.reverse do
|
| 274 |
+
match b with
|
| 275 |
+
| none => pure ()
|
| 276 |
+
| some is_heq =>
|
| 277 |
+
remaining' := remaining'.push (β (if is_heq then mkHEqRefl else mkEqRefl) discr)
|
| 278 |
+
extraEqualities := extraEqualities + 1
|
| 279 |
+
|
| 280 |
+
if useSplitter && !isCasesOn then
|
| 281 |
+
let aux1 := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) params'
|
| 282 |
+
let aux1 := mkApp aux1 motive'
|
| 283 |
+
let aux1 := mkAppN aux1 discrs'
|
| 284 |
+
unless (β isTypeCorrect aux1) do
|
| 285 |
+
prependError m!"failed to transform matcher, type error when constructing new pre-splitter motive:{indentExpr aux1}\nfailed with" do
|
| 286 |
+
check aux1
|
| 287 |
+
let origAltTypes β inferArgumentTypesN matcherApp.alts.size aux1
|
| 288 |
+
|
| 289 |
+
-- We replace the matcher with the splitter
|
| 290 |
+
let matchEqns β Match.getEquationsFor matcherApp.matcherName
|
| 291 |
+
let splitter := matchEqns.splitterName
|
| 292 |
+
|
| 293 |
+
let aux2 := mkAppN (mkConst splitter matcherLevels.toList) params'
|
| 294 |
+
let aux2 := mkApp aux2 motive'
|
| 295 |
+
let aux2 := mkAppN aux2 discrs'
|
| 296 |
+
unless (β isTypeCorrect aux2) do
|
| 297 |
+
prependError m!"failed to transform matcher, type error when constructing splitter motive:{indentExpr aux2}\nfailed with" do
|
| 298 |
+
check aux2
|
| 299 |
+
let altTypes β inferArgumentTypesN matcherApp.alts.size aux2
|
| 300 |
+
|
| 301 |
+
let mut alts' := #[]
|
| 302 |
+
for altIdx in [:matcherApp.alts.size],
|
| 303 |
+
alt in matcherApp.alts,
|
| 304 |
+
numParams in matcherApp.altNumParams,
|
| 305 |
+
splitterNumParams in matchEqns.splitterAltNumParams,
|
| 306 |
+
origAltType in origAltTypes,
|
| 307 |
+
altType in altTypes do
|
| 308 |
+
let alt' β forallAltTelescope' origAltType (numParams - numDiscrEqs) 0 fun ys args => do
|
| 309 |
+
let altType β instantiateForall altType ys
|
| 310 |
+
-- The splitter inserts its extra parameters after the first ys.size parameters, before
|
| 311 |
+
-- the parameters for the numDiscrEqs
|
| 312 |
+
forallBoundedTelescope altType (splitterNumParams - ys.size) fun ys2 altType => do
|
| 313 |
+
forallBoundedTelescope altType numDiscrEqs fun ys3 altType => do
|
| 314 |
+
forallBoundedTelescope altType extraEqualities fun ys4 altType => do
|
| 315 |
+
let alt β try instantiateLambda alt (args ++ ys3)
|
| 316 |
+
catch _ => throwError "unexpected matcher application, insufficient number of parameters in alternative"
|
| 317 |
+
let alt' β onAlt altIdx altType alt
|
| 318 |
+
mkLambdaFVars (ys ++ ys2 ++ ys3 ++ ys4) alt'
|
| 319 |
+
alts' := alts'.push alt'
|
| 320 |
+
|
| 321 |
+
remaining' := remaining' ++ (β onRemaining matcherApp.remaining)
|
| 322 |
+
|
| 323 |
+
return { matcherApp with
|
| 324 |
+
matcherName := splitter
|
| 325 |
+
matcherLevels := matcherLevels
|
| 326 |
+
params := params'
|
| 327 |
+
motive := motive'
|
| 328 |
+
discrs := discrs'
|
| 329 |
+
altNumParams := matchEqns.splitterAltNumParams.map (Β· + extraEqualities)
|
| 330 |
+
alts := alts'
|
| 331 |
+
remaining := remaining'
|
| 332 |
+
}
|
| 333 |
+
else
|
| 334 |
+
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) params'
|
| 335 |
+
let aux := mkApp aux motive'
|
| 336 |
+
let aux := mkAppN aux discrs'
|
| 337 |
+
unless (β isTypeCorrect aux) do
|
| 338 |
+
logError m!"failed to transform matcher, type error when constructing new motive:{indentExpr aux}"
|
| 339 |
+
check aux
|
| 340 |
+
let altTypes β inferArgumentTypesN matcherApp.alts.size aux
|
| 341 |
+
|
| 342 |
+
let mut alts' := #[]
|
| 343 |
+
for altIdx in [:matcherApp.alts.size],
|
| 344 |
+
alt in matcherApp.alts,
|
| 345 |
+
numParams in matcherApp.altNumParams,
|
| 346 |
+
altType in altTypes do
|
| 347 |
+
let alt' β forallBoundedTelescope altType numParams fun xs altType => do
|
| 348 |
+
forallBoundedTelescope altType extraEqualities fun ys4 altType => do
|
| 349 |
+
-- we should try to preserve the variable names in the alternative
|
| 350 |
+
let names β lambdaTelescope alt fun xs _ => xs.mapM (Β·.fvarId!.getUserName)
|
| 351 |
+
withUserNames xs names do
|
| 352 |
+
let alt β instantiateLambda alt xs
|
| 353 |
+
let alt' β onAlt altIdx altType alt
|
| 354 |
+
mkLambdaFVars (xs ++ ys4) alt'
|
| 355 |
+
alts' := alts'.push alt'
|
| 356 |
+
|
| 357 |
+
remaining' := remaining' ++ (β onRemaining matcherApp.remaining)
|
| 358 |
+
|
| 359 |
+
return { matcherApp with
|
| 360 |
+
matcherLevels := matcherLevels
|
| 361 |
+
params := params'
|
| 362 |
+
motive := motive'
|
| 363 |
+
discrs := discrs'
|
| 364 |
+
altNumParams := matcherApp.altNumParams.map (Β· + extraEqualities)
|
| 365 |
+
alts := alts'
|
| 366 |
+
remaining := remaining'
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
/--
|
| 372 |
+
Given a `MatcherApp`, replaces the motive with one that is inferred from the actual types of the
|
| 373 |
+
alternatives.
|
| 374 |
+
|
| 375 |
+
For example, given
|
| 376 |
+
```
|
| 377 |
+
(match (motive := Nat β Unit β ?) n with
|
| 378 |
+
0 => 1
|
| 379 |
+
_ => true) ()
|
| 380 |
+
```
|
| 381 |
+
(for any `?`; the motiveβs result type be ignored) will give this type
|
| 382 |
+
```
|
| 383 |
+
(match n with
|
| 384 |
+
| 0 => Nat
|
| 385 |
+
| _ => Bool)
|
| 386 |
+
```
|
| 387 |
+
|
| 388 |
+
The given `MatcherApp` must not use a splitter in `matcherName`.
|
| 389 |
+
The resulting expression *will* use the splitter corresponding to `matcherName` (this is necessary
|
| 390 |
+
for the construction).
|
| 391 |
+
|
| 392 |
+
Internally, this needs to reduce the matcher in a given branch; this is done using
|
| 393 |
+
`Split.simpMatchTarget`.
|
| 394 |
+
-/
|
| 395 |
+
def inferMatchType (matcherApp : MatcherApp) : MetaM MatcherApp := do
|
| 396 |
+
-- In matcherApp.motive, replace the (dummy) matcher body with a type
|
| 397 |
+
-- derived from the inferred types of the alternatives
|
| 398 |
+
let nExtra := matcherApp.remaining.size
|
| 399 |
+
matcherApp.transform (useSplitter := true)
|
| 400 |
+
(onMotive := fun motiveArgs body => do
|
| 401 |
+
let extraParams β arrowDomainsN nExtra body
|
| 402 |
+
let propMotive β mkLambdaFVars motiveArgs (.sort levelZero)
|
| 403 |
+
let propAlts β matcherApp.alts.mapM fun termAlt =>
|
| 404 |
+
lambdaTelescope termAlt fun xs termAltBody => do
|
| 405 |
+
-- We have alt parameters and parameters corresponding to the extra args
|
| 406 |
+
let xs1 := xs[*...(xs.size - nExtra)]
|
| 407 |
+
let xs2 := xs[(xs.size - nExtra)...xs.size]
|
| 408 |
+
-- logInfo m!"altIH: {xs} => {altIH}"
|
| 409 |
+
let altType β inferType termAltBody
|
| 410 |
+
for x in xs2 do
|
| 411 |
+
if altType.hasAnyFVar (Β· == x.fvarId!) then
|
| 412 |
+
throwError "Type {altType} of alternative {termAlt} still depends on {x}"
|
| 413 |
+
-- logInfo m!"altIH type: {altType}"
|
| 414 |
+
mkLambdaFVars xs1 altType
|
| 415 |
+
let matcherLevels β match matcherApp.uElimPos? with
|
| 416 |
+
| none => pure matcherApp.matcherLevels
|
| 417 |
+
| some pos => pure <| matcherApp.matcherLevels.set! pos levelOne
|
| 418 |
+
let typeMatcherApp := { matcherApp with
|
| 419 |
+
motive := propMotive
|
| 420 |
+
matcherLevels := matcherLevels
|
| 421 |
+
discrs := motiveArgs
|
| 422 |
+
alts := propAlts
|
| 423 |
+
remaining := #[]
|
| 424 |
+
}
|
| 425 |
+
mkArrowN extraParams typeMatcherApp.toExpr
|
| 426 |
+
)
|
| 427 |
+
(onAlt := fun _altIdx expAltType alt => do
|
| 428 |
+
let altType β inferType alt
|
| 429 |
+
let eq β mkEq expAltType altType
|
| 430 |
+
let proof β mkFreshExprSyntheticOpaqueMVar eq
|
| 431 |
+
let goal := proof.mvarId!
|
| 432 |
+
-- logInfo m!"Goal: {goal}"
|
| 433 |
+
let goal β Split.simpMatchTarget goal
|
| 434 |
+
-- logInfo m!"Goal after splitting: {goal}"
|
| 435 |
+
try
|
| 436 |
+
goal.refl
|
| 437 |
+
catch _ =>
|
| 438 |
+
logInfo m!"Cannot close goal after splitting: {goal}"
|
| 439 |
+
goal.admit
|
| 440 |
+
mkEqMPR proof alt
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
end Lean.Meta.MatcherApp
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherInfo.lean
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta
|
| 10 |
+
namespace Match
|
| 11 |
+
|
| 12 |
+
structure DiscrInfo where
|
| 13 |
+
/-- `some h` if the discriminant is annotated with `h:` -/
|
| 14 |
+
hName? : Option Name := none
|
| 15 |
+
deriving Inhabited
|
| 16 |
+
|
| 17 |
+
/--
|
| 18 |
+
A "matcher" auxiliary declaration has the following structure:
|
| 19 |
+
- `numParams` parameters
|
| 20 |
+
- motive
|
| 21 |
+
- `numDiscrs` discriminators (aka major premises)
|
| 22 |
+
- `altNumParams.size` alternatives (aka minor premises) where alternative `i` has `altNumParams[i]` parameters
|
| 23 |
+
- `uElimPos?` is `some pos` when the matcher can eliminate in different universe levels, and
|
| 24 |
+
`pos` is the position of the universe level parameter that specifies the elimination universe.
|
| 25 |
+
It is `none` if the matcher only eliminates into `Prop`. -/
|
| 26 |
+
structure MatcherInfo where
|
| 27 |
+
numParams : Nat
|
| 28 |
+
numDiscrs : Nat
|
| 29 |
+
altNumParams : Array Nat
|
| 30 |
+
uElimPos? : Option Nat
|
| 31 |
+
/--
|
| 32 |
+
`discrInfos[i] = { hName? := some h }` if the i-th discriminant was annotated with `h :`.
|
| 33 |
+
-/
|
| 34 |
+
discrInfos : Array DiscrInfo
|
| 35 |
+
|
| 36 |
+
def MatcherInfo.numAlts (info : MatcherInfo) : Nat :=
|
| 37 |
+
info.altNumParams.size
|
| 38 |
+
|
| 39 |
+
def MatcherInfo.arity (info : MatcherInfo) : Nat :=
|
| 40 |
+
info.numParams + 1 + info.numDiscrs + info.numAlts
|
| 41 |
+
|
| 42 |
+
def MatcherInfo.getFirstDiscrPos (info : MatcherInfo) : Nat :=
|
| 43 |
+
info.numParams + 1
|
| 44 |
+
|
| 45 |
+
def MatcherInfo.getDiscrRange (info : MatcherInfo) : Std.Range :=
|
| 46 |
+
[info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs]
|
| 47 |
+
|
| 48 |
+
def MatcherInfo.getFirstAltPos (info : MatcherInfo) : Nat :=
|
| 49 |
+
info.numParams + 1 + info.numDiscrs
|
| 50 |
+
|
| 51 |
+
def MatcherInfo.getAltRange (info : MatcherInfo) : Std.Range :=
|
| 52 |
+
[info.getFirstAltPos : info.getFirstAltPos + info.numAlts]
|
| 53 |
+
|
| 54 |
+
def MatcherInfo.getMotivePos (info : MatcherInfo) : Nat :=
|
| 55 |
+
info.numParams
|
| 56 |
+
|
| 57 |
+
def getNumEqsFromDiscrInfos (infos : Array DiscrInfo) : Nat := Id.run do
|
| 58 |
+
let mut r := 0
|
| 59 |
+
for info in infos do
|
| 60 |
+
if info.hName?.isSome then
|
| 61 |
+
r := r + 1
|
| 62 |
+
return r
|
| 63 |
+
|
| 64 |
+
def MatcherInfo.getNumDiscrEqs (info : MatcherInfo) : Nat :=
|
| 65 |
+
getNumEqsFromDiscrInfos info.discrInfos
|
| 66 |
+
|
| 67 |
+
namespace Extension
|
| 68 |
+
|
| 69 |
+
structure Entry where
|
| 70 |
+
name : Name
|
| 71 |
+
info : MatcherInfo
|
| 72 |
+
|
| 73 |
+
structure State where
|
| 74 |
+
map : SMap Name MatcherInfo := {}
|
| 75 |
+
|
| 76 |
+
instance : Inhabited State := β¨{}β©
|
| 77 |
+
|
| 78 |
+
def State.addEntry (s : State) (e : Entry) : State := { s with map := s.map.insert e.name e.info }
|
| 79 |
+
def State.switch (s : State) : State := { s with map := s.map.switch }
|
| 80 |
+
|
| 81 |
+
builtin_initialize extension : SimplePersistentEnvExtension Entry State β
|
| 82 |
+
registerSimplePersistentEnvExtension {
|
| 83 |
+
addEntryFn := State.addEntry
|
| 84 |
+
addImportedFn := fun es => (mkStateFromImportedEntries State.addEntry {} es).switch
|
| 85 |
+
asyncMode := .async
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
def addMatcherInfo (env : Environment) (matcherName : Name) (info : MatcherInfo) : Environment :=
|
| 89 |
+
let _ : Inhabited Environment := β¨envβ©
|
| 90 |
+
assert! env.asyncMayContain matcherName
|
| 91 |
+
extension.addEntry env { name := matcherName, info := info }
|
| 92 |
+
|
| 93 |
+
def getMatcherInfo? (env : Environment) (declName : Name) : Option MatcherInfo :=
|
| 94 |
+
(extension.findStateAsync env declName).map.find? declName
|
| 95 |
+
|
| 96 |
+
end Extension
|
| 97 |
+
|
| 98 |
+
def addMatcherInfo [Monad m] [MonadEnv m] (matcherName : Name) (info : MatcherInfo) : m Unit :=
|
| 99 |
+
modifyEnv fun env => Extension.addMatcherInfo env matcherName info
|
| 100 |
+
|
| 101 |
+
end Match
|
| 102 |
+
|
| 103 |
+
export Match (MatcherInfo)
|
| 104 |
+
|
| 105 |
+
def getMatcherInfoCore? (env : Environment) (declName : Name) : Option MatcherInfo :=
|
| 106 |
+
Match.Extension.getMatcherInfo? env declName
|
| 107 |
+
|
| 108 |
+
def getMatcherInfo? [Monad m] [MonadEnv m] (declName : Name) : m (Option MatcherInfo) :=
|
| 109 |
+
return getMatcherInfoCore? (β getEnv) declName
|
| 110 |
+
|
| 111 |
+
@[export lean_is_matcher]
|
| 112 |
+
def isMatcherCore (env : Environment) (declName : Name) : Bool :=
|
| 113 |
+
getMatcherInfoCore? env declName |>.isSome
|
| 114 |
+
|
| 115 |
+
def isMatcher [Monad m] [MonadEnv m] (declName : Name) : m Bool :=
|
| 116 |
+
return isMatcherCore (β getEnv) declName
|
| 117 |
+
|
| 118 |
+
def isMatcherAppCore? (env : Environment) (e : Expr) : Option MatcherInfo :=
|
| 119 |
+
let fn := e.getAppFn
|
| 120 |
+
if fn.isConst then
|
| 121 |
+
if let some matcherInfo := getMatcherInfoCore? env fn.constName! then
|
| 122 |
+
if e.getAppNumArgs β₯ matcherInfo.arity then some matcherInfo else none
|
| 123 |
+
else
|
| 124 |
+
none
|
| 125 |
+
else
|
| 126 |
+
none
|
| 127 |
+
|
| 128 |
+
def isMatcherAppCore (env : Environment) (e : Expr) : Bool :=
|
| 129 |
+
isMatcherAppCore? env e |>.isSome
|
| 130 |
+
|
| 131 |
+
def isMatcherApp [Monad m] [MonadEnv m] (e : Expr) : m Bool :=
|
| 132 |
+
return isMatcherAppCore (β getEnv) e
|
| 133 |
+
|
| 134 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/Value.lean
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.LitValues
|
| 8 |
+
import Lean.Expr
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta
|
| 11 |
+
|
| 12 |
+
/-- Return true is `e` is a term that should be processed by the `match`-compiler using `casesValues` -/
|
| 13 |
+
def isMatchValue (e : Expr) : MetaM Bool := do
|
| 14 |
+
let e β instantiateMVars e
|
| 15 |
+
if (β getNatValue? e).isSome then return true
|
| 16 |
+
if (β getIntValue? e).isSome then return true
|
| 17 |
+
if (β getFinValue? e).isSome then return true
|
| 18 |
+
if (β getBitVecValue? e).isSome then return true
|
| 19 |
+
if (getStringValue? e).isSome then return true
|
| 20 |
+
if (β getCharValue? e).isSome then return true
|
| 21 |
+
if (β getUInt8Value? e).isSome then return true
|
| 22 |
+
if (β getUInt16Value? e).isSome then return true
|
| 23 |
+
if (β getUInt32Value? e).isSome then return true
|
| 24 |
+
if (β getUInt64Value? e).isSome then return true
|
| 25 |
+
return false
|
| 26 |
+
|
| 27 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AC.lean
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Dany Fabian
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.AC.Main
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AC/Main.lean
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Dany Fabian
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Data.AC
|
| 8 |
+
import Lean.Meta.AppBuilder
|
| 9 |
+
import Lean.Meta.Tactic.Refl
|
| 10 |
+
import Lean.Meta.Tactic.Simp.Main
|
| 11 |
+
import Lean.Elab.Tactic.Rewrite
|
| 12 |
+
|
| 13 |
+
namespace Lean.Meta.AC
|
| 14 |
+
open Lean.Data.AC
|
| 15 |
+
open Lean.Elab.Tactic
|
| 16 |
+
open Std
|
| 17 |
+
|
| 18 |
+
abbrev ACExpr := Lean.Data.AC.Expr
|
| 19 |
+
|
| 20 |
+
structure PreContext where
|
| 21 |
+
id : Nat
|
| 22 |
+
op : Expr
|
| 23 |
+
assoc : Expr
|
| 24 |
+
comm : Option Expr
|
| 25 |
+
idem : Option Expr
|
| 26 |
+
deriving Inhabited
|
| 27 |
+
|
| 28 |
+
instance : ContextInformation (PreContext Γ Array Bool) where
|
| 29 |
+
isComm ctx := ctx.1.comm.isSome
|
| 30 |
+
isIdem ctx := ctx.1.idem.isSome
|
| 31 |
+
isNeutral ctx x := ctx.2[x]!
|
| 32 |
+
|
| 33 |
+
instance : EvalInformation PreContext ACExpr where
|
| 34 |
+
arbitrary _ := Data.AC.Expr.var 0
|
| 35 |
+
evalOp _ := Data.AC.Expr.op
|
| 36 |
+
evalVar _ x := Data.AC.Expr.var x
|
| 37 |
+
|
| 38 |
+
def getInstance (cls : Name) (exprs : Array Expr) : MetaM (Option Expr) := do
|
| 39 |
+
try
|
| 40 |
+
let app β mkAppM cls exprs
|
| 41 |
+
trace[Meta.AC] "trying: {indentExpr app}"
|
| 42 |
+
let inst β synthInstance app
|
| 43 |
+
trace[Meta.AC] "got instance"
|
| 44 |
+
return some inst
|
| 45 |
+
catch
|
| 46 |
+
| _ => return none
|
| 47 |
+
|
| 48 |
+
def preContext (expr : Expr) : MetaM (Option PreContext) := do
|
| 49 |
+
if let some assoc := βgetInstance ``Associative #[expr] then
|
| 50 |
+
return some
|
| 51 |
+
{ assoc,
|
| 52 |
+
op := expr
|
| 53 |
+
id := 0
|
| 54 |
+
comm := βgetInstance ``Commutative #[expr]
|
| 55 |
+
idem := βgetInstance ``IdempotentOp #[expr] }
|
| 56 |
+
|
| 57 |
+
return none
|
| 58 |
+
|
| 59 |
+
inductive PreExpr
|
| 60 |
+
| op (lhs rhs : PreExpr)
|
| 61 |
+
| var (e : Expr)
|
| 62 |
+
|
| 63 |
+
@[match_pattern] def bin (op l r : Expr) :=
|
| 64 |
+
Expr.app (Expr.app op l) r
|
| 65 |
+
|
| 66 |
+
def toACExpr (op l r : Expr) : MetaM (Array Expr Γ ACExpr) := do
|
| 67 |
+
let (preExpr, vars) β
|
| 68 |
+
toPreExpr (mkApp2 op l r)
|
| 69 |
+
|>.run β
|
| 70 |
+
let vars := vars.toArray.insertionSort Expr.lt
|
| 71 |
+
let varMap := vars.foldl (fun xs x => xs.insert x xs.size) (β
: Std.HashMap Expr Nat) |>.get!
|
| 72 |
+
|
| 73 |
+
return (vars, toACExpr varMap preExpr)
|
| 74 |
+
where
|
| 75 |
+
toPreExpr : Expr β StateT ExprSet MetaM PreExpr
|
| 76 |
+
| e@(bin opβ l r) => do
|
| 77 |
+
if βisDefEq op opβ then
|
| 78 |
+
return PreExpr.op (βtoPreExpr l) (βtoPreExpr r)
|
| 79 |
+
modify fun vars => vars.insert e
|
| 80 |
+
return PreExpr.var e
|
| 81 |
+
| e => do
|
| 82 |
+
modify fun vars => vars.insert e
|
| 83 |
+
return PreExpr.var e
|
| 84 |
+
|
| 85 |
+
toACExpr (varMap : Expr β Nat) : PreExpr β ACExpr
|
| 86 |
+
| PreExpr.op l r => Data.AC.Expr.op (toACExpr varMap l) (toACExpr varMap r)
|
| 87 |
+
| PreExpr.var x => Data.AC.Expr.var (varMap x)
|
| 88 |
+
|
| 89 |
+
/--
|
| 90 |
+
In order to prevent the kernel trying to reduce the atoms of the expression, we abstract the proof
|
| 91 |
+
over them. But `ac_rfl` proofs are not completely abstract in the value of the atoms β it recognizes
|
| 92 |
+
neutral elements. So we have to abstract over these proofs as well.
|
| 93 |
+
-/
|
| 94 |
+
def abstractAtoms (preContext : PreContext) (atoms : Array Expr)
|
| 95 |
+
(k : Array (Expr Γ Option Expr) β MetaM Expr) : MetaM Expr := do
|
| 96 |
+
let Ξ± β inferType atoms[0]!
|
| 97 |
+
let u β getLevel Ξ±
|
| 98 |
+
let rec go i (acc : Array (Expr Γ Option Expr)) (vars : Array Expr) (args : Array Expr) := do
|
| 99 |
+
if h : i < atoms.size then
|
| 100 |
+
withLocalDeclD `x Ξ± fun v => do
|
| 101 |
+
match (β getInstance ``LawfulIdentity #[preContext.op, atoms[i]]) with
|
| 102 |
+
| none =>
|
| 103 |
+
go (i+1) (acc.push (v, .none)) (vars.push v) (args.push atoms[i])
|
| 104 |
+
| some inst =>
|
| 105 |
+
withLocalDeclD `inst (mkApp3 (mkConst ``LawfulIdentity [u]) Ξ± preContext.op v) fun iv =>
|
| 106 |
+
go (i+1) (acc.push (v, .some iv)) (vars ++ #[v,iv]) (args ++ #[atoms[i], inst])
|
| 107 |
+
else
|
| 108 |
+
let proof β k acc
|
| 109 |
+
let proof β mkLambdaFVars vars proof
|
| 110 |
+
let proof := mkAppN proof args
|
| 111 |
+
return proof
|
| 112 |
+
go 0 #[] #[] #[]
|
| 113 |
+
|
| 114 |
+
def buildNormProof (preContext : PreContext) (l r : Expr) : MetaM (Lean.Expr Γ Lean.Expr) := do
|
| 115 |
+
let (atoms, acExpr) β toACExpr preContext.op l r
|
| 116 |
+
let proof β abstractAtoms preContext atoms fun varsData => do
|
| 117 |
+
let Ξ± β inferType atoms[0]!
|
| 118 |
+
let u β getLevel Ξ±
|
| 119 |
+
let context β mkContext Ξ± u varsData
|
| 120 |
+
let isNeutrals := varsData.map (Β·.2.isSome)
|
| 121 |
+
let vars := varsData.map (Β·.1)
|
| 122 |
+
let acExprNormed := Data.AC.evalList ACExpr preContext $ Data.AC.norm (preContext, isNeutrals) acExpr
|
| 123 |
+
let lhs := convert acExpr
|
| 124 |
+
let rhs := convert acExprNormed
|
| 125 |
+
let proof := mkAppN (mkConst ``Context.eq_of_norm [u]) #[Ξ±, context, lhs, rhs, βmkEqRefl (mkConst ``Bool.true)]
|
| 126 |
+
let proofType β mkEq (convertTarget vars acExpr) (convertTarget vars acExprNormed)
|
| 127 |
+
let proof := mkExpectedPropHint proof proofType
|
| 128 |
+
return proof
|
| 129 |
+
let some (_, _, tgt) := (β inferType proof).eq? | panic! "unexpected proof type"
|
| 130 |
+
return (proof, tgt)
|
| 131 |
+
where
|
| 132 |
+
mkContext (Ξ± : Expr) (u : Level) (vars : Array (Expr Γ Option Expr)) : MetaM Expr := do
|
| 133 |
+
let arbitrary := vars[0]!.1
|
| 134 |
+
let plift := mkApp (mkConst ``PLift [.zero])
|
| 135 |
+
let pliftUp := mkApp2 (mkConst ``PLift.up [.zero])
|
| 136 |
+
let noneE tp := mkApp (mkConst ``Option.none [.zero]) (plift tp)
|
| 137 |
+
let someE tp v := mkApp2 (mkConst ``Option.some [.zero]) (plift tp) (pliftUp tp v)
|
| 138 |
+
let vars β vars.mapM fun β¨x, inst?β© =>
|
| 139 |
+
let isNeutral :=
|
| 140 |
+
let isNeutralClass := mkApp3 (mkConst ``LawfulIdentity [u]) Ξ± preContext.op x
|
| 141 |
+
match inst? with
|
| 142 |
+
| none => noneE isNeutralClass
|
| 143 |
+
| some isNeutral => someE isNeutralClass isNeutral
|
| 144 |
+
return mkApp4 (mkConst ``Variable.mk [u]) Ξ± preContext.op x isNeutral
|
| 145 |
+
|
| 146 |
+
let vars := vars.toList
|
| 147 |
+
let vars β mkListLit (mkApp2 (mkConst ``Variable [u]) Ξ± preContext.op) vars
|
| 148 |
+
|
| 149 |
+
let comm :=
|
| 150 |
+
let commClass := mkApp2 (mkConst ``Commutative [u]) Ξ± preContext.op
|
| 151 |
+
match preContext.comm with
|
| 152 |
+
| none => noneE commClass
|
| 153 |
+
| some comm => someE commClass comm
|
| 154 |
+
|
| 155 |
+
let idem :=
|
| 156 |
+
let idemClass := mkApp2 (mkConst ``IdempotentOp [u]) Ξ± preContext.op
|
| 157 |
+
match preContext.idem with
|
| 158 |
+
| none => noneE idemClass
|
| 159 |
+
| some idem => someE idemClass idem
|
| 160 |
+
|
| 161 |
+
return mkApp7 (mkConst ``Lean.Data.AC.Context.mk [u]) Ξ± preContext.op preContext.assoc comm idem vars arbitrary
|
| 162 |
+
|
| 163 |
+
convert : ACExpr β Expr
|
| 164 |
+
| .op l r => mkApp2 (mkConst ``Data.AC.Expr.op) (convert l) (convert r)
|
| 165 |
+
| .var x => mkApp (mkConst ``Data.AC.Expr.var) $ mkNatLit x
|
| 166 |
+
|
| 167 |
+
convertTarget (vars : Array Expr) : ACExpr β Expr
|
| 168 |
+
| .op l r => mkApp2 preContext.op (convertTarget vars l) (convertTarget vars r)
|
| 169 |
+
| .var x => vars[x]!
|
| 170 |
+
|
| 171 |
+
def post (e : Expr) : SimpM Simp.Step := do
|
| 172 |
+
let ctx β Simp.getContext
|
| 173 |
+
match e, ctx.parent? with
|
| 174 |
+
| bin opβ l r, some (bin opβ _ _) =>
|
| 175 |
+
if βisDefEq opβ opβ then
|
| 176 |
+
return Simp.Step.done { expr := e }
|
| 177 |
+
match βpreContext opβ with
|
| 178 |
+
| some pc =>
|
| 179 |
+
let (proof, newTgt) β buildNormProof pc l r
|
| 180 |
+
return Simp.Step.done { expr := newTgt, proof? := proof }
|
| 181 |
+
| none => return Simp.Step.done { expr := e }
|
| 182 |
+
| bin op l r, _ =>
|
| 183 |
+
match βpreContext op with
|
| 184 |
+
| some pc =>
|
| 185 |
+
let (proof, newTgt) β buildNormProof pc l r
|
| 186 |
+
return Simp.Step.done { expr := newTgt, proof? := proof }
|
| 187 |
+
| none => return Simp.Step.done { expr := e }
|
| 188 |
+
| e, _ => return Simp.Step.done { expr := e }
|
| 189 |
+
|
| 190 |
+
def rewriteUnnormalized (mvarId : MVarId) : MetaM MVarId := do
|
| 191 |
+
let simpCtx β Simp.mkContext
|
| 192 |
+
(simpTheorems := {})
|
| 193 |
+
(congrTheorems := (β getSimpCongrTheorems))
|
| 194 |
+
(config := Simp.neutralConfig)
|
| 195 |
+
let tgt β instantiateMVars (β mvarId.getType)
|
| 196 |
+
let (res, _) β Simp.main tgt simpCtx (methods := { post })
|
| 197 |
+
applySimpResultToTarget mvarId tgt res
|
| 198 |
+
|
| 199 |
+
def rewriteUnnormalizedRefl (goal : MVarId) : MetaM Unit := do
|
| 200 |
+
(β rewriteUnnormalized goal).refl
|
| 201 |
+
|
| 202 |
+
@[builtin_tactic acRfl] def acRflTactic : Lean.Elab.Tactic.Tactic := fun _ => do
|
| 203 |
+
let goal β getMainGoal
|
| 204 |
+
goal.withContext <| rewriteUnnormalizedRefl goal
|
| 205 |
+
|
| 206 |
+
def acNfHypMeta (goal : MVarId) (fvarId : FVarId) : MetaM (Option MVarId) := do
|
| 207 |
+
goal.withContext do
|
| 208 |
+
let simpCtx β Simp.mkContext
|
| 209 |
+
(simpTheorems := {})
|
| 210 |
+
(congrTheorems := (β getSimpCongrTheorems))
|
| 211 |
+
(config := Simp.neutralConfig)
|
| 212 |
+
let tgt β instantiateMVars (β fvarId.getType)
|
| 213 |
+
let (res, _) β Simp.main tgt simpCtx (methods := { post })
|
| 214 |
+
return (β applySimpResultToLocalDecl goal fvarId res false).map (Β·.snd)
|
| 215 |
+
|
| 216 |
+
/-- Implementation of the `ac_nf` tactic when operating on the main goal. -/
|
| 217 |
+
def acNfTargetTactic : TacticM Unit :=
|
| 218 |
+
liftMetaTactic1 fun goal => rewriteUnnormalized goal
|
| 219 |
+
|
| 220 |
+
/-- Implementation of the `ac_nf` tactic when operating on a hypothesis. -/
|
| 221 |
+
def acNfHypTactic (fvarId : FVarId) : TacticM Unit :=
|
| 222 |
+
liftMetaTactic1 fun goal => acNfHypMeta goal fvarId
|
| 223 |
+
|
| 224 |
+
@[builtin_tactic acNf0]
|
| 225 |
+
def evalNf0 : Tactic := fun stx => do
|
| 226 |
+
match stx with
|
| 227 |
+
| `(tactic| ac_nf0 $[$loc?]?) =>
|
| 228 |
+
let loc := if let some loc := loc? then expandLocation loc else Location.targets #[] true
|
| 229 |
+
withMainContext do
|
| 230 |
+
match loc with
|
| 231 |
+
| Location.targets hyps target =>
|
| 232 |
+
if target then acNfTargetTactic
|
| 233 |
+
(β getFVarIds hyps).forM acNfHypTactic
|
| 234 |
+
| Location.wildcard =>
|
| 235 |
+
acNfTargetTactic
|
| 236 |
+
(β (β getMainGoal).getNondepPropHyps).forM acNfHypTactic
|
| 237 |
+
| _ => Lean.Elab.throwUnsupportedSyntax
|
| 238 |
+
|
| 239 |
+
builtin_initialize
|
| 240 |
+
registerTraceClass `Meta.AC
|
| 241 |
+
|
| 242 |
+
end Lean.Meta.AC
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Acyclic.lean
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.MatchUtil
|
| 8 |
+
import Lean.Meta.Tactic.Simp.Main
|
| 9 |
+
|
| 10 |
+
namespace Lean.MVarId
|
| 11 |
+
open Meta
|
| 12 |
+
|
| 13 |
+
private def isTarget (lhs rhs : Expr) : MetaM Bool := do
|
| 14 |
+
if !lhs.isFVar || !lhs.occurs rhs then
|
| 15 |
+
return false
|
| 16 |
+
else
|
| 17 |
+
isConstructorApp' rhs
|
| 18 |
+
|
| 19 |
+
/--
|
| 20 |
+
Close the given goal if `h` is a proof for an equality such as `as = a :: as`.
|
| 21 |
+
Inductive datatypes in Lean are acyclic.
|
| 22 |
+
-/
|
| 23 |
+
def acyclic (mvarId : MVarId) (h : Expr) : MetaM Bool := mvarId.withContext do
|
| 24 |
+
let type β whnfD (β inferType h)
|
| 25 |
+
trace[Meta.Tactic.acyclic] "type: {type}"
|
| 26 |
+
let some (_, lhs, rhs) := type.eq? | return false
|
| 27 |
+
if (β isTarget lhs rhs) then
|
| 28 |
+
go h lhs rhs
|
| 29 |
+
else if (β isTarget rhs lhs) then
|
| 30 |
+
go (β mkEqSymm h) rhs lhs
|
| 31 |
+
else
|
| 32 |
+
return false
|
| 33 |
+
where
|
| 34 |
+
go (h lhs rhs : Expr) : MetaM Bool := do
|
| 35 |
+
try
|
| 36 |
+
let sizeOf_lhs β mkAppM ``sizeOf #[lhs]
|
| 37 |
+
let sizeOf_rhs β mkAppM ``sizeOf #[rhs]
|
| 38 |
+
let sizeOfEq β mkLT sizeOf_lhs sizeOf_rhs
|
| 39 |
+
let hlt β mkFreshExprSyntheticOpaqueMVar sizeOfEq
|
| 40 |
+
-- TODO: we only need the `sizeOf` simp theorems
|
| 41 |
+
let ctx β Simp.mkContext
|
| 42 |
+
(config := { arith := true })
|
| 43 |
+
(simpTheorems := #[ (β getSimpTheorems) ])
|
| 44 |
+
match (β simpTarget hlt.mvarId! ctx {}).1 with
|
| 45 |
+
| some _ => return false
|
| 46 |
+
| none =>
|
| 47 |
+
let heq β mkCongrArg sizeOf_lhs.appFn! (β mkEqSymm h)
|
| 48 |
+
let hlt_self β mkAppM ``Nat.lt_of_lt_of_eq #[hlt, heq]
|
| 49 |
+
let hlt_irrelf β mkAppM ``Nat.lt_irrefl #[sizeOf_lhs]
|
| 50 |
+
mvarId.assign (β mkFalseElim (β mvarId.getType) (mkApp hlt_irrelf hlt_self))
|
| 51 |
+
trace[Meta.Tactic.acyclic] "succeeded"
|
| 52 |
+
return true
|
| 53 |
+
catch ex =>
|
| 54 |
+
trace[Meta.Tactic.acyclic] "failed with\n{ex.toMessageData}"
|
| 55 |
+
return false
|
| 56 |
+
|
| 57 |
+
builtin_initialize
|
| 58 |
+
registerTraceClass `Meta.Tactic.acyclic
|
| 59 |
+
|
| 60 |
+
end Lean.MVarId
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Apply.lean
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Siddhartha Gadgil
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.FindMVar
|
| 8 |
+
import Lean.Meta.SynthInstance
|
| 9 |
+
import Lean.Meta.CollectMVars
|
| 10 |
+
import Lean.Meta.Tactic.Util
|
| 11 |
+
import Lean.PrettyPrinter
|
| 12 |
+
|
| 13 |
+
namespace Lean.Meta
|
| 14 |
+
/--
|
| 15 |
+
Compute the number of expected arguments and whether the result type is of the form
|
| 16 |
+
(?m ...) where ?m is an unassigned metavariable.
|
| 17 |
+
-/
|
| 18 |
+
def getExpectedNumArgsAux (e : Expr) : MetaM (Nat Γ Bool) :=
|
| 19 |
+
withDefault <| forallTelescopeReducing e fun xs body =>
|
| 20 |
+
pure (xs.size, body.getAppFn.isMVar)
|
| 21 |
+
|
| 22 |
+
def getExpectedNumArgs (e : Expr) : MetaM Nat := do
|
| 23 |
+
let (numArgs, _) β getExpectedNumArgsAux e
|
| 24 |
+
pure numArgs
|
| 25 |
+
|
| 26 |
+
private def throwApplyError {Ξ±} (mvarId : MVarId)
|
| 27 |
+
(eType : Expr) (conclusionType? : Option Expr) (targetType : Expr)
|
| 28 |
+
(term? : Option MessageData) : MetaM Ξ± := do
|
| 29 |
+
throwTacticEx `apply mvarId <| MessageData.ofLazyM (es := #[eType, targetType]) do
|
| 30 |
+
let conclusionType := conclusionType?.getD eType
|
| 31 |
+
let note := if conclusionType?.isSome then .note m!"The full type of {term?.getD "the term"} is{indentExpr eType}" else m!""
|
| 32 |
+
let (conclusionType, targetType) β addPPExplicitToExposeDiff conclusionType targetType
|
| 33 |
+
let conclusion := if conclusionType?.isNone then "type" else "conclusion"
|
| 34 |
+
return m!"could not unify the {conclusion} of {term?.getD "the term"}{indentExpr conclusionType}\n\
|
| 35 |
+
with the goal{indentExpr targetType}{note}"
|
| 36 |
+
|
| 37 |
+
def synthAppInstances (tacticName : Name) (mvarId : MVarId) (mvarsNew : Array Expr) (binderInfos : Array BinderInfo)
|
| 38 |
+
(synthAssignedInstances : Bool) (allowSynthFailures : Bool) : MetaM Unit := do
|
| 39 |
+
let mut todo := #[]
|
| 40 |
+
-- Collect metavariables to synthesize
|
| 41 |
+
for mvar in mvarsNew, binderInfo in binderInfos do
|
| 42 |
+
if binderInfo.isInstImplicit then
|
| 43 |
+
if synthAssignedInstances || !(β mvar.mvarId!.isAssigned) then
|
| 44 |
+
todo := todo.push mvar
|
| 45 |
+
while !todo.isEmpty do
|
| 46 |
+
todo β step todo
|
| 47 |
+
where
|
| 48 |
+
/--
|
| 49 |
+
Try to synthesize instances for the metavariables `mvars`.
|
| 50 |
+
Returns metavariables that still need to be synthesized.
|
| 51 |
+
We can view the resulting array as the set of metavariables that we should try again.
|
| 52 |
+
This is needed when applying or rewriting with functions with complex instances.
|
| 53 |
+
For example, consider `rw [@map_smul]` where `map_smul` is
|
| 54 |
+
```
|
| 55 |
+
map_smul {F : Type u_1} {M : Type u_2} {N : Type u_3} {Ο : M β N}
|
| 56 |
+
{X : Type u_4} {Y : Type u_5}
|
| 57 |
+
[SMul M X] [SMul N Y] [FunLike F X Y] [MulActionSemiHomClass F Ο X Y]
|
| 58 |
+
(f : F) (c : M) (x : X) : DFunLike.coe f (c β’ x) = Ο c β’ DFunLike.coe f x
|
| 59 |
+
```
|
| 60 |
+
and `MulActionSemiHomClass` is defined as
|
| 61 |
+
```
|
| 62 |
+
class MulActionSemiHomClass (F : Type _)
|
| 63 |
+
{M N : outParam (Type _)} (Ο : outParam (M β N))
|
| 64 |
+
(X Y : outParam (Type _)) [SMul M X] [SMul N Y] [FunLike F X Y] : Prop where
|
| 65 |
+
```
|
| 66 |
+
The left-hand-side of the equation does not bind `N`. Thus, `SMul N Y` cannot
|
| 67 |
+
be synthesized until we synthesize `MulActionSemiHomClass F Ο X Y`. Note that
|
| 68 |
+
`N` is an output parameter for `MulActionSemiHomClass`.
|
| 69 |
+
-/
|
| 70 |
+
step (mvars : Array Expr) : MetaM (Array Expr) := do
|
| 71 |
+
-- `ex?` stores the exception for this first synthesis failure in this step.
|
| 72 |
+
let mut ex? := none
|
| 73 |
+
-- `true` if we managed to synthesize an instance after we hit a failure.
|
| 74 |
+
-- That is, there is a chance we may succeed if we try again.
|
| 75 |
+
let mut progressAfterEx := false
|
| 76 |
+
-- Metavariables that we failed to synthesize.
|
| 77 |
+
let mut postponed := #[]
|
| 78 |
+
for mvar in mvars do
|
| 79 |
+
let mvarType β inferType mvar
|
| 80 |
+
let mvarVal? β try
|
| 81 |
+
let mvarVal β synthInstance mvarType
|
| 82 |
+
unless postponed.isEmpty do
|
| 83 |
+
progressAfterEx := true
|
| 84 |
+
pure (some mvarVal)
|
| 85 |
+
catch ex =>
|
| 86 |
+
ex? := some ex
|
| 87 |
+
postponed := postponed.push mvar
|
| 88 |
+
pure none
|
| 89 |
+
if let some mvarVal := mvarVal? then
|
| 90 |
+
unless (β isDefEq mvar mvarVal) do
|
| 91 |
+
-- There is no point in trying again for this kind of failure
|
| 92 |
+
unless allowSynthFailures do
|
| 93 |
+
throwTacticEx tacticName mvarId "failed to assign synthesized instance"
|
| 94 |
+
if let some ex := ex? then
|
| 95 |
+
if progressAfterEx then
|
| 96 |
+
return postponed
|
| 97 |
+
else
|
| 98 |
+
-- There is no point in running `step` again. We should give up (`allowSynthFailures`),
|
| 99 |
+
-- or throw the first exception we found in this `step`.
|
| 100 |
+
if allowSynthFailures then return #[] else throw ex
|
| 101 |
+
else
|
| 102 |
+
-- Done. We successfully synthesized all metavariables.
|
| 103 |
+
return #[]
|
| 104 |
+
|
| 105 |
+
def appendParentTag (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo) : MetaM Unit := do
|
| 106 |
+
let parentTag β mvarId.getTag
|
| 107 |
+
if newMVars.size == 1 then
|
| 108 |
+
-- if there is only one subgoal, we inherit the parent tag
|
| 109 |
+
newMVars[0]!.mvarId!.setTag parentTag
|
| 110 |
+
else
|
| 111 |
+
unless parentTag.isAnonymous do
|
| 112 |
+
newMVars.size.forM fun i _ => do
|
| 113 |
+
let mvarIdNew := newMVars[i].mvarId!
|
| 114 |
+
unless (β mvarIdNew.isAssigned) do
|
| 115 |
+
unless binderInfos[i]!.isInstImplicit do
|
| 116 |
+
let currTag β mvarIdNew.getTag
|
| 117 |
+
mvarIdNew.setTag (appendTag parentTag currTag)
|
| 118 |
+
|
| 119 |
+
/--
|
| 120 |
+
If `synthAssignedInstances` is `true`, then `apply` will synthesize instance implicit arguments
|
| 121 |
+
even if they have assigned by `isDefEq`, and then check whether the synthesized value matches the
|
| 122 |
+
one inferred. The `congr` tactic sets this flag to false.
|
| 123 |
+
-/
|
| 124 |
+
def postprocessAppMVars (tacticName : Name) (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo)
|
| 125 |
+
(synthAssignedInstances := true) (allowSynthFailures := false) : MetaM Unit := do
|
| 126 |
+
synthAppInstances tacticName mvarId newMVars binderInfos synthAssignedInstances allowSynthFailures
|
| 127 |
+
-- TODO: default and auto params
|
| 128 |
+
appendParentTag mvarId newMVars binderInfos
|
| 129 |
+
|
| 130 |
+
private def dependsOnOthers (mvar : Expr) (otherMVars : Array Expr) : MetaM Bool :=
|
| 131 |
+
otherMVars.anyM fun otherMVar => do
|
| 132 |
+
if mvar == otherMVar then
|
| 133 |
+
return false
|
| 134 |
+
else
|
| 135 |
+
let otherMVarType β inferType otherMVar
|
| 136 |
+
return (otherMVarType.findMVar? fun mvarId => mvarId == mvar.mvarId!).isSome
|
| 137 |
+
|
| 138 |
+
/-- Partitions the given mvars in to two arrays (non-deps, deps)
|
| 139 |
+
according to whether the given mvar depends on other mvars in the array.-/
|
| 140 |
+
private def partitionDependentMVars (mvars : Array Expr) : MetaM (Array MVarId Γ Array MVarId) :=
|
| 141 |
+
mvars.foldlM (init := (#[], #[])) fun (nonDeps, deps) mvar => do
|
| 142 |
+
let currMVarId := mvar.mvarId!
|
| 143 |
+
if (β dependsOnOthers mvar mvars) then
|
| 144 |
+
return (nonDeps, deps.push currMVarId)
|
| 145 |
+
else
|
| 146 |
+
return (nonDeps.push currMVarId, deps)
|
| 147 |
+
|
| 148 |
+
private def reorderGoals (mvars : Array Expr) : ApplyNewGoals β MetaM (List MVarId)
|
| 149 |
+
| ApplyNewGoals.nonDependentFirst => do
|
| 150 |
+
let (nonDeps, deps) β partitionDependentMVars mvars
|
| 151 |
+
return nonDeps.toList ++ deps.toList
|
| 152 |
+
| ApplyNewGoals.nonDependentOnly => do
|
| 153 |
+
let (nonDeps, _) β partitionDependentMVars mvars
|
| 154 |
+
return nonDeps.toList
|
| 155 |
+
| ApplyNewGoals.all => return mvars.toList.map Lean.Expr.mvarId!
|
| 156 |
+
|
| 157 |
+
/-- Custom `isDefEq` for the `apply` tactic -/
|
| 158 |
+
private def isDefEqApply (approx : Bool) (a b : Expr) : MetaM Bool := do
|
| 159 |
+
if approx then
|
| 160 |
+
approxDefEq <| isDefEqGuarded a b
|
| 161 |
+
else
|
| 162 |
+
isDefEqGuarded a b
|
| 163 |
+
|
| 164 |
+
/--
|
| 165 |
+
Close the given goal using `apply e`.
|
| 166 |
+
-/
|
| 167 |
+
def _root_.Lean.MVarId.apply (mvarId : MVarId) (e : Expr) (cfg : ApplyConfig := {})
|
| 168 |
+
(term? : Option MessageData := none) : MetaM (List MVarId) :=
|
| 169 |
+
mvarId.withContext do
|
| 170 |
+
mvarId.checkNotAssigned `apply
|
| 171 |
+
let targetType β mvarId.getType
|
| 172 |
+
let eType β inferType e
|
| 173 |
+
let (numArgs, hasMVarHead) β getExpectedNumArgsAux eType
|
| 174 |
+
/-
|
| 175 |
+
The `apply` tactic adds `_`s to `e`, and some of these `_`s become new goals.
|
| 176 |
+
When `hasMVarHead` is `false` we try different numbers, until we find a type compatible with `targetType`.
|
| 177 |
+
We used to try only `numArgs-targetTypeNumArgs` when `hasMVarHead = false`, but this is not always correct.
|
| 178 |
+
For example, consider the following example
|
| 179 |
+
```
|
| 180 |
+
example {Ξ± Ξ²} [LE_trans Ξ²] (x y z : Ξ± β Ξ²) (hβ : x β€ y) (hβ : y β€ z) : x β€ z := by
|
| 181 |
+
apply le_trans
|
| 182 |
+
assumption
|
| 183 |
+
assumption
|
| 184 |
+
```
|
| 185 |
+
In this example, `targetTypeNumArgs = 1` because `LE` for functions is defined as
|
| 186 |
+
```
|
| 187 |
+
instance {Ξ± : Type u} {Ξ² : Type v} [LE Ξ²] : LE (Ξ± β Ξ²) where
|
| 188 |
+
le f g := β i, f i β€ g i
|
| 189 |
+
```
|
| 190 |
+
-/
|
| 191 |
+
let rangeNumArgs β if hasMVarHead then
|
| 192 |
+
pure [numArgs : numArgs+1]
|
| 193 |
+
else
|
| 194 |
+
let targetTypeNumArgs β getExpectedNumArgs targetType
|
| 195 |
+
pure [numArgs - targetTypeNumArgs : numArgs+1]
|
| 196 |
+
/-
|
| 197 |
+
Auxiliary function for trying to add `n` underscores where `n β [i: rangeNumArgs.stop)`
|
| 198 |
+
See comment above
|
| 199 |
+
-/
|
| 200 |
+
let rec go (i : Nat) : MetaM (Array Expr Γ Array BinderInfo) := do
|
| 201 |
+
if i < rangeNumArgs.stop then
|
| 202 |
+
let s β saveState
|
| 203 |
+
let (newMVars, binderInfos, eType) β forallMetaTelescopeReducing eType i
|
| 204 |
+
if (β isDefEqApply cfg.approx eType targetType) then
|
| 205 |
+
return (newMVars, binderInfos)
|
| 206 |
+
else
|
| 207 |
+
s.restore
|
| 208 |
+
go (i+1)
|
| 209 |
+
else
|
| 210 |
+
|
| 211 |
+
let conclusionType? β if rangeNumArgs.start = 0 then
|
| 212 |
+
pure none
|
| 213 |
+
else
|
| 214 |
+
let (_, _, r) β forallMetaTelescopeReducing eType (some rangeNumArgs.start)
|
| 215 |
+
pure (some r)
|
| 216 |
+
throwApplyError mvarId eType conclusionType? targetType term?
|
| 217 |
+
termination_by rangeNumArgs.stop - i
|
| 218 |
+
let (newMVars, binderInfos) β go rangeNumArgs.start
|
| 219 |
+
postprocessAppMVars `apply mvarId newMVars binderInfos cfg.synthAssignedInstances cfg.allowSynthFailures
|
| 220 |
+
let e β instantiateMVars e
|
| 221 |
+
mvarId.assign (mkAppN e newMVars)
|
| 222 |
+
let newMVars β newMVars.filterM fun mvar => not <$> mvar.mvarId!.isAssigned
|
| 223 |
+
let otherMVarIds β getMVarsNoDelayed e
|
| 224 |
+
let newMVarIds β reorderGoals newMVars cfg.newGoals
|
| 225 |
+
let otherMVarIds := otherMVarIds.filter fun mvarId => !newMVarIds.contains mvarId
|
| 226 |
+
let result := newMVarIds ++ otherMVarIds.toList
|
| 227 |
+
result.forM (Β·.headBetaType)
|
| 228 |
+
return result
|
| 229 |
+
|
| 230 |
+
/-- Short-hand for applying a constant to the goal. -/
|
| 231 |
+
def _root_.Lean.MVarId.applyConst (mvar : MVarId) (c : Name) (cfg : ApplyConfig := {}) : MetaM (List MVarId) := do
|
| 232 |
+
mvar.apply (β mkConstWithFreshMVarLevels c) cfg (term? := m!"'{.ofConstName c}'")
|
| 233 |
+
|
| 234 |
+
/-- Close the given goal using `e`, instantiated with `n` metavariables. -/
|
| 235 |
+
def _root_.Lean.MVarId.applyN (mvarId : MVarId) (e : Expr) (n : Nat) (useApproxDefEq := true) : MetaM (List MVarId) :=
|
| 236 |
+
mvarId.withContext do
|
| 237 |
+
mvarId.checkNotAssigned `apply
|
| 238 |
+
let targetType β mvarId.getType
|
| 239 |
+
let eType β inferType e
|
| 240 |
+
let (mvarIds, _, eType) β forallMetaBoundedTelescope eType n
|
| 241 |
+
unless mvarIds.size == n do
|
| 242 |
+
throwError "Applied type takes fewer than {n} arguments:\n{indentExpr eType}"
|
| 243 |
+
unless (β isDefEqApply useApproxDefEq eType targetType) do
|
| 244 |
+
throwError "Type mismatch: target is{indentExpr targetType}\nbut applied expression has \
|
| 245 |
+
type{indentExpr eType}\nafter applying {n} arguments."
|
| 246 |
+
mvarId.assign (e.beta mvarIds)
|
| 247 |
+
return (mvarIds.map (Β·.mvarId!)).toList
|
| 248 |
+
|
| 249 |
+
end Meta
|
| 250 |
+
|
| 251 |
+
open Meta
|
| 252 |
+
namespace MVarId
|
| 253 |
+
|
| 254 |
+
partial def splitAndCore (mvarId : MVarId) : MetaM (List MVarId) :=
|
| 255 |
+
mvarId.withContext do
|
| 256 |
+
mvarId.checkNotAssigned `splitAnd
|
| 257 |
+
let type β mvarId.getType'
|
| 258 |
+
if !type.isAppOfArity ``And 2 then
|
| 259 |
+
return [mvarId]
|
| 260 |
+
else
|
| 261 |
+
let tag β mvarId.getTag
|
| 262 |
+
let rec go (type : Expr) : StateRefT (Array MVarId) MetaM Expr := do
|
| 263 |
+
let type β whnf type
|
| 264 |
+
if type.isAppOfArity ``And 2 then
|
| 265 |
+
let pβ := type.appFn!.appArg!
|
| 266 |
+
let pβ := type.appArg!
|
| 267 |
+
return mkApp4 (mkConst ``And.intro) pβ pβ (β go pβ) (β go pβ)
|
| 268 |
+
else
|
| 269 |
+
let idx := (β get).size + 1
|
| 270 |
+
let mvar β mkFreshExprSyntheticOpaqueMVar type (tag ++ (`h).appendIndexAfter idx)
|
| 271 |
+
modify fun s => s.push mvar.mvarId!
|
| 272 |
+
return mvar
|
| 273 |
+
let (val, s) β go type |>.run #[]
|
| 274 |
+
mvarId.assign val
|
| 275 |
+
return s.toList
|
| 276 |
+
|
| 277 |
+
/--
|
| 278 |
+
Apply `And.intro` as much as possible to goal `mvarId`.
|
| 279 |
+
-/
|
| 280 |
+
abbrev splitAnd (mvarId : MVarId) : MetaM (List MVarId) :=
|
| 281 |
+
splitAndCore mvarId
|
| 282 |
+
|
| 283 |
+
def exfalso (mvarId : MVarId) : MetaM MVarId :=
|
| 284 |
+
mvarId.withContext do
|
| 285 |
+
mvarId.checkNotAssigned `exfalso
|
| 286 |
+
let target β instantiateMVars (β mvarId.getType)
|
| 287 |
+
let u β getLevel target
|
| 288 |
+
let mvarIdNew β mkFreshExprSyntheticOpaqueMVar (mkConst ``False) (tag := (β mvarId.getTag))
|
| 289 |
+
mvarId.assign (mkApp2 (mkConst ``False.elim [u]) target mvarIdNew)
|
| 290 |
+
return mvarIdNew.mvarId!
|
| 291 |
+
|
| 292 |
+
/--
|
| 293 |
+
Apply the `n`-th constructor of the target type,
|
| 294 |
+
checking that it is an inductive type,
|
| 295 |
+
and that there are the expected number of constructors.
|
| 296 |
+
-/
|
| 297 |
+
def nthConstructor
|
| 298 |
+
(name : Name) (idx : Nat) (expected? : Option Nat := none) (goal : MVarId) :
|
| 299 |
+
MetaM (List MVarId) := do
|
| 300 |
+
goal.withContext do
|
| 301 |
+
goal.checkNotAssigned name
|
| 302 |
+
matchConstInduct (β goal.getType').getAppFn
|
| 303 |
+
(fun _ => throwTacticEx name goal "target is not an inductive datatype")
|
| 304 |
+
fun ival us => do
|
| 305 |
+
if let some e := expected? then unless ival.ctors.length == e do
|
| 306 |
+
throwTacticEx name goal
|
| 307 |
+
s!"{name} tactic works for inductive types with exactly {e} constructors"
|
| 308 |
+
if h : idx < ival.ctors.length then
|
| 309 |
+
goal.apply <| mkConst ival.ctors[idx] us
|
| 310 |
+
else
|
| 311 |
+
throwTacticEx name goal s!"index {idx} out of bounds, only {ival.ctors.length} constructors"
|
| 312 |
+
|
| 313 |
+
/--
|
| 314 |
+
Try to convert an `Iff` into an `Eq` by applying `iff_of_eq`.
|
| 315 |
+
If successful, returns the new goal, and otherwise returns the original `MVarId`.
|
| 316 |
+
|
| 317 |
+
This may be regarded as being a special case of `Lean.MVarId.liftReflToEq`, specifically for `Iff`.
|
| 318 |
+
-/
|
| 319 |
+
def iffOfEq (mvarId : MVarId) : MetaM MVarId := do
|
| 320 |
+
let res β observing? do
|
| 321 |
+
let [mvarId] β mvarId.apply (mkConst ``iff_of_eq []) | failure
|
| 322 |
+
return mvarId
|
| 323 |
+
return res.getD mvarId
|
| 324 |
+
|
| 325 |
+
/--
|
| 326 |
+
Try to convert an `Eq` into an `Iff` by applying `propext`.
|
| 327 |
+
If successful, then returns then new goal, otherwise returns the original `MVarId`.
|
| 328 |
+
-/
|
| 329 |
+
def propext (mvarId : MVarId) : MetaM MVarId := do
|
| 330 |
+
let res β observing? do
|
| 331 |
+
-- Avoid applying `propext` if the target is not an equality of `Prop`s.
|
| 332 |
+
-- We don't want a unification specializing `Sort*` to `Prop`.
|
| 333 |
+
let tgt β withReducible mvarId.getType'
|
| 334 |
+
let some (_, x, _) := tgt.eq? | failure
|
| 335 |
+
guard <| β Meta.isProp x
|
| 336 |
+
let [mvarId] β mvarId.apply (mkConst ``propext []) | failure
|
| 337 |
+
return mvarId
|
| 338 |
+
return res.getD mvarId
|
| 339 |
+
|
| 340 |
+
/--
|
| 341 |
+
Try to close the goal using `proof_irrel_heq`. Returns whether or not it succeeds.
|
| 342 |
+
|
| 343 |
+
We need to be somewhat careful not to assign metavariables while doing this, otherwise we might
|
| 344 |
+
specialize `Sort _` to `Prop`.
|
| 345 |
+
-/
|
| 346 |
+
def proofIrrelHeq (mvarId : MVarId) : MetaM Bool :=
|
| 347 |
+
mvarId.withContext do
|
| 348 |
+
let res β observing? do
|
| 349 |
+
mvarId.checkNotAssigned `proofIrrelHeq
|
| 350 |
+
let tgt β withReducible mvarId.getType'
|
| 351 |
+
let some (_, lhs, _, rhs) := tgt.heq? | failure
|
| 352 |
+
-- Note: `mkAppM` uses `withNewMCtxDepth`, which prevents `Sort _` from specializing to `Prop`
|
| 353 |
+
let pf β mkAppM ``proof_irrel_heq #[lhs, rhs]
|
| 354 |
+
mvarId.assign pf
|
| 355 |
+
return true
|
| 356 |
+
return res.getD false
|
| 357 |
+
|
| 358 |
+
/--
|
| 359 |
+
Try to close the goal using `Subsingleton.elim`. Returns whether or not it succeeds.
|
| 360 |
+
|
| 361 |
+
We are careful to apply `Subsingleton.elim` in a way that does not assign any metavariables.
|
| 362 |
+
This is to prevent the `Subsingleton Prop` instance from being used as justification to specialize
|
| 363 |
+
`Sort _` to `Prop`.
|
| 364 |
+
-/
|
| 365 |
+
def subsingletonElim (mvarId : MVarId) : MetaM Bool :=
|
| 366 |
+
mvarId.withContext do
|
| 367 |
+
let res β observing? do
|
| 368 |
+
mvarId.checkNotAssigned `subsingletonElim
|
| 369 |
+
let tgt β withReducible mvarId.getType'
|
| 370 |
+
let some (_, lhs, rhs) := tgt.eq? | failure
|
| 371 |
+
-- Note: `mkAppM` uses `withNewMCtxDepth`, which prevents `Sort _` from specializing to `Prop`
|
| 372 |
+
let pf β mkAppM ``Subsingleton.elim #[lhs, rhs]
|
| 373 |
+
mvarId.assign pf
|
| 374 |
+
return true
|
| 375 |
+
return res.getD false
|
| 376 |
+
|
| 377 |
+
end Lean.MVarId
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Assert.lean
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.FVarSubst
|
| 8 |
+
import Lean.Meta.Tactic.Intro
|
| 9 |
+
import Lean.Meta.Tactic.Revert
|
| 10 |
+
import Lean.Util.ForEachExpr
|
| 11 |
+
|
| 12 |
+
namespace Lean.Meta
|
| 13 |
+
|
| 14 |
+
/--
|
| 15 |
+
Convert the given goal `Ctx |- target` into `Ctx |- type -> target`.
|
| 16 |
+
It assumes `val` has type `type` -/
|
| 17 |
+
def _root_.Lean.MVarId.assert (mvarId : MVarId) (name : Name) (type : Expr) (val : Expr) : MetaM MVarId :=
|
| 18 |
+
mvarId.withContext do
|
| 19 |
+
mvarId.checkNotAssigned `assert
|
| 20 |
+
let tag β mvarId.getTag
|
| 21 |
+
let target β mvarId.getType
|
| 22 |
+
let newType := Lean.mkForall name BinderInfo.default type target
|
| 23 |
+
let newMVar β mkFreshExprSyntheticOpaqueMVar newType tag
|
| 24 |
+
mvarId.assign (mkApp newMVar val)
|
| 25 |
+
return newMVar.mvarId!
|
| 26 |
+
|
| 27 |
+
/-- Add the hypothesis `h : t`, given `v : t`, and return the new `FVarId`. -/
|
| 28 |
+
def _root_.Lean.MVarId.note (g : MVarId) (h : Name) (v : Expr) (t? : Option Expr := .none) :
|
| 29 |
+
MetaM (FVarId Γ MVarId) := do
|
| 30 |
+
(β g.assert h (β match t? with | some t => pure t | none => inferType v) v).intro1P
|
| 31 |
+
|
| 32 |
+
/--
|
| 33 |
+
Convert the given goal `Ctx |- target` into `Ctx |- let name : type := val; target`.
|
| 34 |
+
It assumes `val` has type `type` -/
|
| 35 |
+
def _root_.Lean.MVarId.define (mvarId : MVarId) (name : Name) (type : Expr) (val : Expr) : MetaM MVarId := do
|
| 36 |
+
mvarId.withContext do
|
| 37 |
+
mvarId.checkNotAssigned `define
|
| 38 |
+
let tag β mvarId.getTag
|
| 39 |
+
let target β mvarId.getType
|
| 40 |
+
let newType := Lean.mkLet name type val target
|
| 41 |
+
let newMVar β mkFreshExprSyntheticOpaqueMVar newType tag
|
| 42 |
+
mvarId.assign newMVar
|
| 43 |
+
return newMVar.mvarId!
|
| 44 |
+
|
| 45 |
+
/--
|
| 46 |
+
Convert the given goal `Ctx |- target` into `Ctx |- (hName : type) -> hName = val -> target`.
|
| 47 |
+
It assumes `val` has type `type` -/
|
| 48 |
+
def _root_.Lean.MVarId.assertExt (mvarId : MVarId) (name : Name) (type : Expr) (val : Expr) (hName : Name := `h) : MetaM MVarId := do
|
| 49 |
+
mvarId.withContext do
|
| 50 |
+
mvarId.checkNotAssigned `assert
|
| 51 |
+
let tag β mvarId.getTag
|
| 52 |
+
let target β mvarId.getType
|
| 53 |
+
let u β getLevel type
|
| 54 |
+
let hType := mkApp3 (mkConst `Eq [u]) type (mkBVar 0) val
|
| 55 |
+
let newType := Lean.mkForall name BinderInfo.default type $ Lean.mkForall hName BinderInfo.default hType target
|
| 56 |
+
let newMVar β mkFreshExprSyntheticOpaqueMVar newType tag
|
| 57 |
+
let rflPrf β mkEqRefl val
|
| 58 |
+
mvarId.assign (mkApp2 newMVar val rflPrf)
|
| 59 |
+
return newMVar.mvarId!
|
| 60 |
+
|
| 61 |
+
structure AssertAfterResult where
|
| 62 |
+
fvarId : FVarId
|
| 63 |
+
mvarId : MVarId
|
| 64 |
+
subst : FVarSubst
|
| 65 |
+
|
| 66 |
+
/--
|
| 67 |
+
Convert the given goal `Ctx |- target` into a goal containing `(userName : type)` after the local declaration with if `fvarId`.
|
| 68 |
+
It assumes `val` has type `type`, and that `type` is well-formed after `fvarId`.
|
| 69 |
+
Note that `val` does not need to be well-formed after `fvarId`. That is, it may contain variables that are defined after `fvarId`. -/
|
| 70 |
+
def _root_.Lean.MVarId.assertAfter (mvarId : MVarId) (fvarId : FVarId) (userName : Name) (type : Expr) (val : Expr) : MetaM AssertAfterResult := do
|
| 71 |
+
mvarId.checkNotAssigned `assertAfter
|
| 72 |
+
let (fvarIds, mvarId) β mvarId.revertAfter fvarId
|
| 73 |
+
let mvarId β mvarId.assert userName type val
|
| 74 |
+
let (fvarIdNew, mvarId) β mvarId.intro1P
|
| 75 |
+
let (fvarIdsNew, mvarId) β mvarId.introNP fvarIds.size
|
| 76 |
+
let mut subst := {}
|
| 77 |
+
for f in fvarIds, fNew in fvarIdsNew do
|
| 78 |
+
subst := subst.insert f (mkFVar fNew)
|
| 79 |
+
return { fvarId := fvarIdNew, mvarId, subst }
|
| 80 |
+
|
| 81 |
+
structure Hypothesis where
|
| 82 |
+
userName : Name
|
| 83 |
+
type : Expr
|
| 84 |
+
value : Expr
|
| 85 |
+
/-- The hypothesis' `BinderInfo` -/
|
| 86 |
+
binderInfo : BinderInfo := .default
|
| 87 |
+
/-- The hypothesis' `LocalDeclKind` -/
|
| 88 |
+
kind : LocalDeclKind := .default
|
| 89 |
+
|
| 90 |
+
/--
|
| 91 |
+
Convert the given goal `Ctx |- target` into `Ctx, (hs[0].userName : hs[0].type) ... |-target`.
|
| 92 |
+
It assumes `hs[i].val` has type `hs[i].type`. -/
|
| 93 |
+
def _root_.Lean.MVarId.assertHypotheses (mvarId : MVarId) (hs : Array Hypothesis) : MetaM (Array FVarId Γ MVarId) := do
|
| 94 |
+
if hs.isEmpty then
|
| 95 |
+
return (#[], mvarId)
|
| 96 |
+
else mvarId.withContext do
|
| 97 |
+
mvarId.checkNotAssigned `assertHypotheses
|
| 98 |
+
let tag β mvarId.getTag
|
| 99 |
+
let target β mvarId.getType
|
| 100 |
+
let targetNew := hs.foldr (init := target) fun h targetNew =>
|
| 101 |
+
.forallE h.userName h.type targetNew h.binderInfo
|
| 102 |
+
let mvarNew β mkFreshExprSyntheticOpaqueMVar targetNew tag
|
| 103 |
+
let val := hs.foldl (init := mvarNew) fun val h => .app val h.value
|
| 104 |
+
mvarId.assign val
|
| 105 |
+
let (fvarIds, mvarId) β mvarNew.mvarId!.introNP hs.size
|
| 106 |
+
mvarId.modifyLCtx fun lctx => Id.run do
|
| 107 |
+
let mut lctx := lctx
|
| 108 |
+
for h : i in [:hs.size] do
|
| 109 |
+
let h := hs[i]
|
| 110 |
+
if h.kind != .default then
|
| 111 |
+
lctx := lctx.setKind fvarIds[i]! h.kind
|
| 112 |
+
pure lctx
|
| 113 |
+
return (fvarIds, mvarId)
|
| 114 |
+
|
| 115 |
+
/--
|
| 116 |
+
Replace hypothesis `hyp` in goal `g` with `proof : typeNew`.
|
| 117 |
+
The new hypothesis is given the same user name as the original,
|
| 118 |
+
it attempts to avoid reordering hypotheses, and the original is cleared if possible.
|
| 119 |
+
-/
|
| 120 |
+
-- adapted from Lean.Meta.replaceLocalDeclCore
|
| 121 |
+
def _root_.Lean.MVarId.replace (g : MVarId) (hyp : FVarId) (proof : Expr) (typeNew : Option Expr := none) :
|
| 122 |
+
MetaM AssertAfterResult :=
|
| 123 |
+
g.withContext do
|
| 124 |
+
let typeNew β match typeNew with
|
| 125 |
+
| some t => pure t
|
| 126 |
+
| none => inferType proof
|
| 127 |
+
let ldecl β hyp.getDecl
|
| 128 |
+
-- `typeNew` may contain variables that occur after `hyp`.
|
| 129 |
+
-- Thus, we use the auxiliary function `findMaxFVar` to ensure `typeNew` is well-formed
|
| 130 |
+
-- at the position we are inserting it.
|
| 131 |
+
let (_, ldecl') β findMaxFVar typeNew |>.run ldecl
|
| 132 |
+
let result β g.assertAfter ldecl'.fvarId ldecl.userName typeNew proof
|
| 133 |
+
(return { result with mvarId := β result.mvarId.clear hyp }) <|> pure result
|
| 134 |
+
where
|
| 135 |
+
/-- Finds the `LocalDecl` for the FVar in `e` with the highest index. -/
|
| 136 |
+
findMaxFVar (e : Expr) : StateRefT LocalDecl MetaM Unit :=
|
| 137 |
+
e.forEach' fun e => do
|
| 138 |
+
if e.isFVar then
|
| 139 |
+
let ldecl' β e.fvarId!.getDecl
|
| 140 |
+
modify fun ldecl => if ldecl'.index > ldecl.index then ldecl' else ldecl
|
| 141 |
+
return false
|
| 142 |
+
else
|
| 143 |
+
return e.hasFVar
|
| 144 |
+
|
| 145 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Assumption.lean
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Util
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta
|
| 10 |
+
|
| 11 |
+
/-- Return a local declaration whose type is definitionally equal to `type`. -/
|
| 12 |
+
def findLocalDeclWithType? (type : Expr) : MetaM (Option FVarId) := do
|
| 13 |
+
(β getLCtx).findDeclRevM? fun localDecl => do
|
| 14 |
+
if localDecl.isImplementationDetail then
|
| 15 |
+
return none
|
| 16 |
+
else if (β isDefEq type localDecl.type) then
|
| 17 |
+
return some localDecl.fvarId
|
| 18 |
+
else
|
| 19 |
+
return none
|
| 20 |
+
|
| 21 |
+
/-- Return `true` if managed to close goal `mvarId` using an assumption. -/
|
| 22 |
+
def _root_.Lean.MVarId.assumptionCore (mvarId : MVarId) : MetaM Bool :=
|
| 23 |
+
mvarId.withContext do
|
| 24 |
+
mvarId.checkNotAssigned `assumption
|
| 25 |
+
match (β findLocalDeclWithType? (β mvarId.getType)) with
|
| 26 |
+
| none => return false
|
| 27 |
+
| some fvarId => mvarId.assign (mkFVar fvarId); return true
|
| 28 |
+
|
| 29 |
+
/-- Close goal `mvarId` using an assumption. Throw error message if failed. -/
|
| 30 |
+
def _root_.Lean.MVarId.assumption (mvarId : MVarId) : MetaM Unit :=
|
| 31 |
+
unless (β mvarId.assumptionCore) do
|
| 32 |
+
throwTacticEx `assumption mvarId
|
| 33 |
+
|
| 34 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AuxLemma.lean
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.AddDecl
|
| 8 |
+
import Lean.Meta.Basic
|
| 9 |
+
import Lean.DefEqAttrib
|
| 10 |
+
|
| 11 |
+
namespace Lean.Meta
|
| 12 |
+
|
| 13 |
+
structure AuxLemmas where
|
| 14 |
+
lemmas : PHashMap Expr (Name Γ List Name) := {}
|
| 15 |
+
deriving Inhabited
|
| 16 |
+
|
| 17 |
+
builtin_initialize auxLemmasExt : EnvExtension AuxLemmas β
|
| 18 |
+
registerEnvExtension (pure {}) (asyncMode := .local) -- a mere cache, keep local
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
Helper method for creating auxiliary lemmas in the environment.
|
| 22 |
+
|
| 23 |
+
It uses a cache that maps `type` to declaration name. The cache is not stored in `.olean` files.
|
| 24 |
+
It is useful to make sure the same auxiliary lemma is not created over and over again in the same
|
| 25 |
+
environment branch. For expensive auxiliary lemmas that should be deduplicated even across
|
| 26 |
+
different environment branches, consider using `realizeConst` instead.
|
| 27 |
+
|
| 28 |
+
This method is useful for tactics (e.g., `simp`) that may perform preprocessing steps to lemmas provided by
|
| 29 |
+
users. For example, `simp` preprocessor may convert a lemma into multiple ones.
|
| 30 |
+
-/
|
| 31 |
+
def mkAuxLemma (levelParams : List Name) (type : Expr) (value : Expr) (kind? : Option Name := none)
|
| 32 |
+
(cache := true) (inferRfl := false) : MetaM Name := do
|
| 33 |
+
let env β getEnv
|
| 34 |
+
let s := auxLemmasExt.getState env
|
| 35 |
+
let mkNewAuxLemma := do
|
| 36 |
+
let auxName β mkAuxDeclName (kind := kind?.getD `_proof)
|
| 37 |
+
let decl :=
|
| 38 |
+
if env.hasUnsafe type || env.hasUnsafe value then
|
| 39 |
+
-- `result` contains unsafe code, thus we cannot use a theorem.
|
| 40 |
+
Declaration.defnDecl {
|
| 41 |
+
name := auxName
|
| 42 |
+
hints := ReducibilityHints.opaque
|
| 43 |
+
safety := DefinitionSafety.unsafe
|
| 44 |
+
levelParams, type, value
|
| 45 |
+
}
|
| 46 |
+
else
|
| 47 |
+
Declaration.thmDecl {
|
| 48 |
+
name := auxName
|
| 49 |
+
levelParams, type, value
|
| 50 |
+
}
|
| 51 |
+
addDecl decl
|
| 52 |
+
if inferRfl then
|
| 53 |
+
inferDefEqAttr auxName
|
| 54 |
+
modifyEnv fun env => auxLemmasExt.modifyState env fun β¨lemmasβ© => β¨lemmas.insert type (auxName, levelParams)β©
|
| 55 |
+
return auxName
|
| 56 |
+
if cache then
|
| 57 |
+
if let some (name, levelParams') := s.lemmas.find? type then
|
| 58 |
+
if levelParams == levelParams' then
|
| 59 |
+
return name
|
| 60 |
+
mkNewAuxLemma
|
| 61 |
+
|
| 62 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Backtrack.lean
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2023 Kim Morrison. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kim Morrison
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Data.List.BasicAux
|
| 8 |
+
import Lean.Meta.Iterator
|
| 9 |
+
import Lean.Meta.Tactic.IndependentOf
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
# `backtrack`
|
| 13 |
+
|
| 14 |
+
A meta-tactic for running backtracking search, given a non-deterministic tactic
|
| 15 |
+
`alternatives : MVarId β Nondet MetaM (List MVarId)`.
|
| 16 |
+
|
| 17 |
+
`backtrack alternatives goals` will recursively try to solve all goals in `goals`,
|
| 18 |
+
and the subgoals generated, backtracking as necessary.
|
| 19 |
+
|
| 20 |
+
In its default behaviour, it will either solve all goals, or fail.
|
| 21 |
+
A customisable `suspend` hook in `BacktrackConfig` allows suspend a goal (or subgoal),
|
| 22 |
+
so that it will be returned instead of processed further.
|
| 23 |
+
Other hooks `proc` and `discharge` (described in `BacktrackConfig`) allow running other
|
| 24 |
+
tactics before `alternatives`, or if all search branches from a given goal fail.
|
| 25 |
+
|
| 26 |
+
Currently only `solveByElim` is implemented in terms of `backtrack`.
|
| 27 |
+
-/
|
| 28 |
+
|
| 29 |
+
namespace Lean.Meta.Tactic.Backtrack
|
| 30 |
+
|
| 31 |
+
open Lean Meta
|
| 32 |
+
|
| 33 |
+
/--
|
| 34 |
+
Configuration structure to control the behaviour of `backtrack`:
|
| 35 |
+
* control the maximum depth and behaviour (fail or return subgoals) at the maximum depth,
|
| 36 |
+
* and hooks allowing
|
| 37 |
+
* modifying intermediate goals before running the external tactic,
|
| 38 |
+
* 'suspending' goals, returning them in the result, and
|
| 39 |
+
* discharging subgoals if the external tactic fails.
|
| 40 |
+
-/
|
| 41 |
+
structure BacktrackConfig where
|
| 42 |
+
/-- Maximum recursion depth. -/
|
| 43 |
+
maxDepth : Nat := 6
|
| 44 |
+
/-- An arbitrary procedure which can be used to modify the list of goals
|
| 45 |
+
before each attempt to generate alternatives.
|
| 46 |
+
Called as `proc goals curr`, where `goals` are the original goals for `backtracking`,
|
| 47 |
+
and `curr` are the current goals.
|
| 48 |
+
Returning `some l` will replace the current goals with `l` and recurse
|
| 49 |
+
(consuming one step of maximum depth).
|
| 50 |
+
Returning `none` will proceed to generating alternative without changing goals.
|
| 51 |
+
Failure will cause backtracking.
|
| 52 |
+
(defaults to `none`) -/
|
| 53 |
+
proc : List MVarId β List MVarId β MetaM (Option (List MVarId)) := fun _ _ => pure none
|
| 54 |
+
/-- If `suspend g`, then we do not consider alternatives for `g`,
|
| 55 |
+
but return `g` as a new subgoal. (defaults to `false`) -/
|
| 56 |
+
suspend : MVarId β MetaM Bool := fun _ => pure false
|
| 57 |
+
/-- `discharge g` is called on goals for which there were no alternatives.
|
| 58 |
+
If `none` we return `g` as a new subgoal.
|
| 59 |
+
If `some l`, we replace `g` by `l` in the list of active goals, and recurse.
|
| 60 |
+
If failure, we backtrack. (defaults to failure) -/
|
| 61 |
+
discharge : MVarId β MetaM (Option (List MVarId)) := fun _ => failure
|
| 62 |
+
/--
|
| 63 |
+
If we solve any "independent" goals, don't fail.
|
| 64 |
+
See `Lean.MVarId.independent?` for the definition of independence.
|
| 65 |
+
-/
|
| 66 |
+
commitIndependentGoals : Bool := false
|
| 67 |
+
|
| 68 |
+
namespace Backtrack
|
| 69 |
+
|
| 70 |
+
/--
|
| 71 |
+
Pretty print a list of goals.
|
| 72 |
+
-/
|
| 73 |
+
private def ppMVarId (g : MVarId) : MetaM Format := ppExpr =<< g.getType
|
| 74 |
+
|
| 75 |
+
/--
|
| 76 |
+
Pretty print a list of goals.
|
| 77 |
+
-/
|
| 78 |
+
private def ppMVarIds (gs : List MVarId) : MetaM (List Format) := gs.mapM ppMVarId
|
| 79 |
+
|
| 80 |
+
/-- Run a monadic function on every element of a list,
|
| 81 |
+
returning the list of elements on which the function fails, and the list of successful results. -/
|
| 82 |
+
def tryAllM [Monad m] [Alternative m] (L : List Ξ±) (f : Ξ± β m Ξ²) : m (List Ξ± Γ List Ξ²) := do
|
| 83 |
+
let R β L.mapM (fun a => (Sum.inr <$> f a) <|> (pure (Sum.inl a)))
|
| 84 |
+
return (R.filterMap (fun s => match s with | .inl a => a | _ => none),
|
| 85 |
+
R.filterMap (fun s => match s with | .inr b => b | _ => none))
|
| 86 |
+
|
| 87 |
+
variable (cfg : BacktrackConfig)
|
| 88 |
+
variable (trace : Name := .anonymous)
|
| 89 |
+
variable (next : MVarId β (List MVarId β MetaM (Option (List MVarId))) β MetaM (List MVarId))
|
| 90 |
+
|
| 91 |
+
/--
|
| 92 |
+
* `n : Nat` steps remaining.
|
| 93 |
+
* `curr : List MVarId` the current list of unsolved goals.
|
| 94 |
+
* `acc : List MVarId` a list of "suspended" goals, which will be returned as subgoals.
|
| 95 |
+
-/
|
| 96 |
+
-- `acc` is intentionally a `List` rather than an `Array` so we can share across branches.
|
| 97 |
+
private def run (goals : List MVarId) (n : Nat) (curr acc : List MVarId) : MetaM (List MVarId) := do
|
| 98 |
+
match n with
|
| 99 |
+
| 0 => do
|
| 100 |
+
-- We're out of fuel.
|
| 101 |
+
throwError "backtrack exceeded the recursion limit"
|
| 102 |
+
| n + 1 => do
|
| 103 |
+
-- First, run `cfg.proc`, to see if it wants to modify the goals.
|
| 104 |
+
let procResult? β try
|
| 105 |
+
cfg.proc goals curr
|
| 106 |
+
catch e =>
|
| 107 |
+
withTraceNode trace
|
| 108 |
+
(return m!"{exceptEmoji Β·} BacktrackConfig.proc failed: {e.toMessageData}") do
|
| 109 |
+
throw e
|
| 110 |
+
match procResult? with
|
| 111 |
+
| some curr' => run goals n curr' acc
|
| 112 |
+
| none =>
|
| 113 |
+
match curr with
|
| 114 |
+
-- If there are no active goals, return the accumulated goals.
|
| 115 |
+
| [] => withTraceNode trace (return m!"{exceptEmoji Β·} success!") do
|
| 116 |
+
return acc.reverse
|
| 117 |
+
| g :: gs =>
|
| 118 |
+
-- Discard any goals which have already been assigned.
|
| 119 |
+
if β g.isAssigned then
|
| 120 |
+
withTraceNode trace (return m!"{exceptEmoji Β·} discarding already assigned goal {g}") do
|
| 121 |
+
run goals (n+1) gs acc
|
| 122 |
+
else
|
| 123 |
+
withTraceNode trace
|
| 124 |
+
-- Note: the `addMessageContextFull` ensures we show the goal using the mvar context before
|
| 125 |
+
-- the `do` block below runs, potentially unifying mvars in the goal.
|
| 126 |
+
(return m!"{exceptEmoji Β·} working on: {β addMessageContextFull g}")
|
| 127 |
+
do
|
| 128 |
+
-- Check if we should suspend the search here:
|
| 129 |
+
if (β cfg.suspend g) then
|
| 130 |
+
withTraceNode trace
|
| 131 |
+
(fun _ => return m!"βΈοΈ suspending search and returning as subgoal") do
|
| 132 |
+
run goals (n+1) gs (g :: acc)
|
| 133 |
+
else
|
| 134 |
+
try
|
| 135 |
+
-- We attempt to find an alternative,
|
| 136 |
+
-- for which all resulting sub-goals can be discharged using `run n`.
|
| 137 |
+
next g (fun r => observing? do run goals n (r ++ gs) acc)
|
| 138 |
+
catch _ =>
|
| 139 |
+
-- No lemmas could be applied:
|
| 140 |
+
match (β cfg.discharge g) with
|
| 141 |
+
| none => (withTraceNode trace
|
| 142 |
+
(fun _ => return m!"βοΈ deemed acceptable, returning as subgoal") do
|
| 143 |
+
run goals (n+1) gs (g :: acc))
|
| 144 |
+
| some l => (withTraceNode trace
|
| 145 |
+
(fun _ => return m!"β¬ discharger generated new subgoals") do
|
| 146 |
+
run goals n (l ++ gs) acc)
|
| 147 |
+
|
| 148 |
+
/--
|
| 149 |
+
A wrapper around `run`, which works on "independent" goals separately first,
|
| 150 |
+
to reduce backtracking.
|
| 151 |
+
-/
|
| 152 |
+
private partial def processIndependentGoals (orig : List MVarId) (goals remaining : List MVarId) :
|
| 153 |
+
MetaM (List MVarId) := do
|
| 154 |
+
-- Partition the remaining goals into "independent" goals
|
| 155 |
+
-- (which should be solvable without affecting the solvability of other goals)
|
| 156 |
+
-- and all the others.
|
| 157 |
+
let (igs, ogs) β remaining.partitionM (MVarId.isIndependentOf goals)
|
| 158 |
+
if igs.isEmpty then
|
| 159 |
+
-- If there are no independent goals, we solve all the goals together via backtracking search.
|
| 160 |
+
return (β run cfg trace next orig cfg.maxDepth remaining [])
|
| 161 |
+
else
|
| 162 |
+
withTraceNode trace
|
| 163 |
+
(fun _ => return m!"independent goals {β ppMVarIds igs},"
|
| 164 |
+
++ m!" working on them before {β ppMVarIds ogs}") do
|
| 165 |
+
-- Invoke `run` on each of the independent goals separately,
|
| 166 |
+
-- gathering the subgoals on which `run` fails,
|
| 167 |
+
-- and the new subgoals generated from goals on which it is successful.
|
| 168 |
+
let (failed, newSubgoals') β tryAllM igs fun g =>
|
| 169 |
+
run cfg trace next orig cfg.maxDepth [g] []
|
| 170 |
+
let newSubgoals := newSubgoals'.flatten
|
| 171 |
+
withTraceNode trace
|
| 172 |
+
(fun _ => return m!"failed: {β ppMVarIds failed}, new: {β ppMVarIds newSubgoals}") do
|
| 173 |
+
-- Update the list of goals with respect to which we need to check independence.
|
| 174 |
+
let goals' := (β goals.filterM (fun g => do pure !(β g.isAssigned))) ++ newSubgoals
|
| 175 |
+
-- If `commitIndependentGoals` is `true`, we will return the new goals
|
| 176 |
+
-- regardless of whether we can make further progress on the other goals.
|
| 177 |
+
if cfg.commitIndependentGoals && !newSubgoals.isEmpty then
|
| 178 |
+
return newSubgoals ++ failed ++ (β (processIndependentGoals orig goals' ogs <|> pure ogs))
|
| 179 |
+
else if !failed.isEmpty then
|
| 180 |
+
-- If `commitIndependentGoals` is `false`, and we failed on any of the independent goals,
|
| 181 |
+
-- then overall failure is inevitable so we can stop here.
|
| 182 |
+
failure
|
| 183 |
+
else
|
| 184 |
+
-- Finally, having solved this batch of independent goals,
|
| 185 |
+
-- recurse (potentially now finding new independent goals).
|
| 186 |
+
return newSubgoals ++ (β processIndependentGoals orig goals' ogs)
|
| 187 |
+
|
| 188 |
+
end Backtrack
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
/--
|
| 192 |
+
Attempts to solve the `goals`, by recursively calling `next` on each
|
| 193 |
+
subgoal that appears with a callback to reenter backtracking search.
|
| 194 |
+
|
| 195 |
+
Further flow control options are available via the `Config` argument.
|
| 196 |
+
|
| 197 |
+
Returns a list of subgoals which were "suspended" via the `suspend` or
|
| 198 |
+
`discharge` hooks in `Config`. In the default configuration, `backtrack`
|
| 199 |
+
will either return an empty list or fail.
|
| 200 |
+
-/
|
| 201 |
+
def backtrack (cfg : BacktrackConfig := {}) (trace : Name := .anonymous)
|
| 202 |
+
(next : MVarId β MetaM (Meta.Iterator (List MVarId)))
|
| 203 |
+
(goals : List MVarId) : MetaM (List MVarId) := do
|
| 204 |
+
let resolve g f := do (βnext g).firstM f
|
| 205 |
+
Backtrack.processIndependentGoals cfg trace resolve goals goals goals
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Cases.lean
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.AppBuilder
|
| 8 |
+
import Lean.Meta.Tactic.Induction
|
| 9 |
+
import Lean.Meta.Tactic.Injection
|
| 10 |
+
import Lean.Meta.Tactic.Assert
|
| 11 |
+
import Lean.Meta.Tactic.Subst
|
| 12 |
+
import Lean.Meta.Tactic.Acyclic
|
| 13 |
+
import Lean.Meta.Tactic.UnifyEq
|
| 14 |
+
|
| 15 |
+
namespace Lean.Meta
|
| 16 |
+
|
| 17 |
+
private def throwInductiveTypeExpected (type : Expr) : MetaM Ξ± := do
|
| 18 |
+
throwError "failed to compile pattern matching, inductive type expected{indentExpr type}"
|
| 19 |
+
|
| 20 |
+
def getInductiveUniverseAndParams (type : Expr) : MetaM (List Level Γ Array Expr) := do
|
| 21 |
+
let type β whnfD type
|
| 22 |
+
matchConstInduct type.getAppFn (fun _ => throwInductiveTypeExpected type) fun val us =>
|
| 23 |
+
let Iargs := type.getAppArgs
|
| 24 |
+
let params := Iargs.extract 0 val.numParams
|
| 25 |
+
pure (us, params)
|
| 26 |
+
|
| 27 |
+
private def mkEqAndProof (lhs rhs : Expr) : MetaM (Expr Γ Expr) := do
|
| 28 |
+
let lhsType β inferType lhs
|
| 29 |
+
let rhsType β inferType rhs
|
| 30 |
+
let u β getLevel lhsType
|
| 31 |
+
if (β isDefEq lhsType rhsType) then
|
| 32 |
+
pure (mkApp3 (mkConst ``Eq [u]) lhsType lhs rhs, mkApp2 (mkConst ``Eq.refl [u]) lhsType lhs)
|
| 33 |
+
else
|
| 34 |
+
pure (mkApp4 (mkConst ``HEq [u]) lhsType lhs rhsType rhs, mkApp2 (mkConst ``HEq.refl [u]) lhsType lhs)
|
| 35 |
+
|
| 36 |
+
partial def withNewEqs (targets targetsNew : Array Expr) (k : Array Expr β Array Expr β MetaM Ξ±) : MetaM Ξ± :=
|
| 37 |
+
let rec loop (i : Nat) (newEqs : Array Expr) (newRefls : Array Expr) := do
|
| 38 |
+
if i < targets.size then
|
| 39 |
+
let (newEqType, newRefl) β mkEqAndProof targets[i]! targetsNew[i]!
|
| 40 |
+
withLocalDeclD `h newEqType fun newEq => do
|
| 41 |
+
loop (i+1) (newEqs.push newEq) (newRefls.push newRefl)
|
| 42 |
+
else
|
| 43 |
+
k newEqs newRefls
|
| 44 |
+
loop 0 #[] #[]
|
| 45 |
+
|
| 46 |
+
def generalizeTargetsEq (mvarId : MVarId) (motiveType : Expr) (targets : Array Expr) : MetaM MVarId :=
|
| 47 |
+
mvarId.withContext do
|
| 48 |
+
mvarId.checkNotAssigned `generalizeTargets
|
| 49 |
+
let (typeNew, eqRefls) β
|
| 50 |
+
forallTelescopeReducing motiveType fun targetsNew _ => do
|
| 51 |
+
unless targetsNew.size β₯ targets.size do
|
| 52 |
+
throwError "invalid number of targets #{targets.size}, motive only takes #{targetsNew.size}"
|
| 53 |
+
let targetsNewAtomic := targetsNew[*...targets.size]
|
| 54 |
+
withNewEqs targets targetsNewAtomic fun eqs eqRefls => do
|
| 55 |
+
let typeNew β mvarId.getType
|
| 56 |
+
let typeNew β mkForallFVars eqs typeNew
|
| 57 |
+
let typeNew β mkForallFVars targetsNewAtomic typeNew
|
| 58 |
+
pure (typeNew, eqRefls)
|
| 59 |
+
let mvarNew β mkFreshExprSyntheticOpaqueMVar typeNew (β mvarId.getTag)
|
| 60 |
+
mvarId.assign (mkAppN (mkAppN mvarNew targets) eqRefls)
|
| 61 |
+
pure mvarNew.mvarId!
|
| 62 |
+
|
| 63 |
+
structure GeneralizeIndicesSubgoal where
|
| 64 |
+
mvarId : MVarId
|
| 65 |
+
indicesFVarIds : Array FVarId
|
| 66 |
+
fvarId : FVarId
|
| 67 |
+
numEqs : Nat
|
| 68 |
+
|
| 69 |
+
/--
|
| 70 |
+
Given a metavariable `mvarId` representing the goal
|
| 71 |
+
```
|
| 72 |
+
Ctx |- T
|
| 73 |
+
```
|
| 74 |
+
and an expression `e : I A j`, where `I A j` is an inductive datatype where `A` are parameters,
|
| 75 |
+
and `j` the indices. Generate the goal
|
| 76 |
+
```
|
| 77 |
+
Ctx, j' : J, h' : I A j' |- j == j' -> e == h' -> T
|
| 78 |
+
```
|
| 79 |
+
Remark: `(j == j' -> e == h')` is a "telescopic" equality.
|
| 80 |
+
Remark: `j` is sequence of terms, and `j'` a sequence of free variables.
|
| 81 |
+
The result contains the fields
|
| 82 |
+
- `mvarId`: the new goal
|
| 83 |
+
- `indicesFVarIds`: `j'` ids
|
| 84 |
+
- `fvarId`: `h'` id
|
| 85 |
+
- `numEqs`: number of equations in the target
|
| 86 |
+
|
| 87 |
+
If `varName?` is not none, it is used to name `h'`.
|
| 88 |
+
-/
|
| 89 |
+
def generalizeIndices' (mvarId : MVarId) (e : Expr) (varName? : Option Name := none) : MetaM GeneralizeIndicesSubgoal :=
|
| 90 |
+
mvarId.withContext do
|
| 91 |
+
let lctx β getLCtx
|
| 92 |
+
let localInsts β getLocalInstances
|
| 93 |
+
mvarId.checkNotAssigned `generalizeIndices
|
| 94 |
+
let type β whnfD (β inferType e)
|
| 95 |
+
type.withApp fun f args => matchConstInduct f (fun _ => throwTacticEx `generalizeIndices mvarId "inductive type expected") fun val _ => do
|
| 96 |
+
unless val.numIndices > 0 do throwTacticEx `generalizeIndices mvarId "indexed inductive type expected"
|
| 97 |
+
unless args.size == val.numIndices + val.numParams do throwTacticEx `generalizeIndices mvarId "ill-formed inductive datatype"
|
| 98 |
+
let indices := args.extract (args.size - val.numIndices) args.size
|
| 99 |
+
let IA := mkAppN f (args.extract 0 val.numParams) -- `I A`
|
| 100 |
+
let IAType β inferType IA
|
| 101 |
+
forallTelescopeReducing IAType fun newIndices _ => do
|
| 102 |
+
let newType := mkAppN IA newIndices
|
| 103 |
+
let varName β if let some varName := varName? then pure varName else mkFreshUserName `x
|
| 104 |
+
withLocalDeclD varName newType fun h' =>
|
| 105 |
+
withNewEqs indices newIndices fun newEqs newRefls => do
|
| 106 |
+
let (newEqType, newRefl) β mkEqAndProof e h'
|
| 107 |
+
let newRefls := newRefls.push newRefl
|
| 108 |
+
withLocalDeclD `h newEqType fun newEq => do
|
| 109 |
+
let newEqs := newEqs.push newEq
|
| 110 |
+
/- auxType `forall (j' : J) (h' : I A j'), j == j' -> h == h' -> target -/
|
| 111 |
+
let target β mvarId.getType
|
| 112 |
+
let tag β mvarId.getTag
|
| 113 |
+
let auxType β mkForallFVars newEqs target
|
| 114 |
+
let auxType β mkForallFVars #[h'] auxType
|
| 115 |
+
let auxType β mkForallFVars newIndices auxType
|
| 116 |
+
let newMVar β mkFreshExprMVarAt lctx localInsts auxType MetavarKind.syntheticOpaque tag
|
| 117 |
+
/- assign mvarId := newMVar indices h refls -/
|
| 118 |
+
mvarId.assign (mkAppN (mkApp (mkAppN newMVar indices) e) newRefls)
|
| 119 |
+
let (indicesFVarIds, newMVarId) β newMVar.mvarId!.introNP newIndices.size
|
| 120 |
+
let (fvarId, newMVarId) β newMVarId.intro1P
|
| 121 |
+
return {
|
| 122 |
+
mvarId := newMVarId,
|
| 123 |
+
indicesFVarIds := indicesFVarIds,
|
| 124 |
+
fvarId := fvarId,
|
| 125 |
+
numEqs := newEqs.size
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
/--
|
| 129 |
+
Similar to `generalizeTargets` but customized for the `casesOn` motive.
|
| 130 |
+
Given a metavariable `mvarId` representing the
|
| 131 |
+
```
|
| 132 |
+
Ctx, h : I A j, D |- T
|
| 133 |
+
```
|
| 134 |
+
where `fvarId` is `h`s id, and the type `I A j` is an inductive datatype where `A` are parameters,
|
| 135 |
+
and `j` the indices. Generate the goal
|
| 136 |
+
```
|
| 137 |
+
Ctx, h : I A j, D, j' : J, h' : I A j' |- j == j' -> h == h' -> T
|
| 138 |
+
```
|
| 139 |
+
Remark: `(j == j' -> h == h')` is a "telescopic" equality.
|
| 140 |
+
Remark: `j` is sequence of terms, and `j'` a sequence of free variables.
|
| 141 |
+
The result contains the fields
|
| 142 |
+
- `mvarId`: the new goal
|
| 143 |
+
- `indicesFVarIds`: `j'` ids
|
| 144 |
+
- `fvarId`: `h'` id
|
| 145 |
+
- `numEqs`: number of equations in the target -/
|
| 146 |
+
def generalizeIndices (mvarId : MVarId) (fvarId : FVarId) : MetaM GeneralizeIndicesSubgoal :=
|
| 147 |
+
mvarId.withContext do
|
| 148 |
+
let fvarDecl β fvarId.getDecl
|
| 149 |
+
generalizeIndices' mvarId fvarDecl.toExpr fvarDecl.userName
|
| 150 |
+
|
| 151 |
+
structure CasesSubgoal extends InductionSubgoal where
|
| 152 |
+
ctorName : Name
|
| 153 |
+
|
| 154 |
+
namespace Cases
|
| 155 |
+
|
| 156 |
+
structure Context where
|
| 157 |
+
inductiveVal : InductiveVal
|
| 158 |
+
casesOnVal : DefinitionVal
|
| 159 |
+
nminors : Nat := inductiveVal.ctors.length
|
| 160 |
+
majorDecl : LocalDecl
|
| 161 |
+
majorTypeFn : Expr
|
| 162 |
+
majorTypeArgs : Array Expr
|
| 163 |
+
majorTypeIndices : Array Expr := majorTypeArgs.extract (majorTypeArgs.size - inductiveVal.numIndices) majorTypeArgs.size
|
| 164 |
+
|
| 165 |
+
private def mkCasesContext? (majorFVarId : FVarId) : MetaM (Option Context) := do
|
| 166 |
+
let env β getEnv
|
| 167 |
+
if !env.contains `Eq || !env.contains `HEq then
|
| 168 |
+
pure none
|
| 169 |
+
else
|
| 170 |
+
let majorDecl β majorFVarId.getDecl
|
| 171 |
+
let majorType β whnf majorDecl.type
|
| 172 |
+
majorType.withApp fun f args => matchConstInduct f (fun _ => pure none) fun ival _ =>
|
| 173 |
+
if args.size != ival.numIndices + ival.numParams then pure none
|
| 174 |
+
else match env.find? (Name.mkStr ival.name "casesOn") with
|
| 175 |
+
| ConstantInfo.defnInfo cval =>
|
| 176 |
+
return some {
|
| 177 |
+
inductiveVal := ival,
|
| 178 |
+
casesOnVal := cval,
|
| 179 |
+
majorDecl := majorDecl,
|
| 180 |
+
majorTypeFn := f,
|
| 181 |
+
majorTypeArgs := args
|
| 182 |
+
}
|
| 183 |
+
| _ => pure none
|
| 184 |
+
|
| 185 |
+
/--
|
| 186 |
+
We say the major premise has independent indices IF
|
| 187 |
+
1- its type is *not* an indexed inductive family, OR
|
| 188 |
+
2- its type is an indexed inductive family, but all indices are distinct free variables, and
|
| 189 |
+
all local declarations different from the major and its indices do not depend on the indices.
|
| 190 |
+
-/
|
| 191 |
+
private def hasIndepIndices (ctx : Context) : MetaM Bool := do
|
| 192 |
+
if ctx.majorTypeIndices.isEmpty then
|
| 193 |
+
return true
|
| 194 |
+
else if ctx.majorTypeIndices.any fun idx => !idx.isFVar then
|
| 195 |
+
/- One of the indices is not a free variable. -/
|
| 196 |
+
return false
|
| 197 |
+
else if ctx.majorTypeIndices.size.any fun i _ => i.any fun j _ => ctx.majorTypeIndices[i] == ctx.majorTypeIndices[j] then
|
| 198 |
+
/- An index occurs more than once -/
|
| 199 |
+
return false
|
| 200 |
+
else
|
| 201 |
+
(β getLCtx).allM fun decl =>
|
| 202 |
+
pure (decl.fvarId == ctx.majorDecl.fvarId) <||> -- decl is the major
|
| 203 |
+
pure (ctx.majorTypeIndices.any (fun index => decl.fvarId == index.fvarId!)) <||> -- decl is one of the indices
|
| 204 |
+
findLocalDeclDependsOn decl (fun fvarId => ctx.majorTypeIndices.all fun idx => idx.fvarId! != fvarId) -- or does not depend on any index
|
| 205 |
+
|
| 206 |
+
private def elimAuxIndices (sβ : GeneralizeIndicesSubgoal) (sβ : Array CasesSubgoal) : MetaM (Array CasesSubgoal) :=
|
| 207 |
+
let indicesFVarIds := sβ.indicesFVarIds
|
| 208 |
+
sβ.mapM fun s => do
|
| 209 |
+
indicesFVarIds.foldlM (init := s) fun s indexFVarId =>
|
| 210 |
+
match s.subst.get indexFVarId with
|
| 211 |
+
| Expr.fvar indexFVarId' =>
|
| 212 |
+
(do let mvarId β s.mvarId.clear indexFVarId'; pure { s with mvarId := mvarId, subst := s.subst.erase indexFVarId })
|
| 213 |
+
<|>
|
| 214 |
+
(pure s)
|
| 215 |
+
| _ => pure s
|
| 216 |
+
|
| 217 |
+
/--
|
| 218 |
+
Convert `s` into an array of `CasesSubgoal`, by attaching the corresponding constructor name,
|
| 219 |
+
and adding the substitution `majorFVarId -> ctor_i us params fields` into each subgoal. -/
|
| 220 |
+
private def toCasesSubgoals (s : Array InductionSubgoal) (ctorNames : Array Name) (majorFVarId : FVarId) (us : List Level) (params : Array Expr)
|
| 221 |
+
: Array CasesSubgoal :=
|
| 222 |
+
s.mapIdx fun i s =>
|
| 223 |
+
let ctorName := ctorNames[i]!
|
| 224 |
+
let ctorApp := mkAppN (mkAppN (mkConst ctorName us) params) s.fields
|
| 225 |
+
let s := { s with subst := s.subst.insert majorFVarId ctorApp }
|
| 226 |
+
{ ctorName := ctorName,
|
| 227 |
+
toInductionSubgoal := s }
|
| 228 |
+
|
| 229 |
+
partial def unifyEqs? (numEqs : Nat) (mvarId : MVarId) (subst : FVarSubst) (caseName? : Option Name := none): MetaM (Option (MVarId Γ FVarSubst)) := withIncRecDepth do
|
| 230 |
+
if numEqs == 0 then
|
| 231 |
+
return some (mvarId, subst)
|
| 232 |
+
else
|
| 233 |
+
let (eqFVarId, mvarId) β mvarId.intro1
|
| 234 |
+
if let some { mvarId, subst, numNewEqs } β unifyEq? mvarId eqFVarId subst MVarId.acyclic caseName? then
|
| 235 |
+
unifyEqs? (numEqs - 1 + numNewEqs) mvarId subst caseName?
|
| 236 |
+
else
|
| 237 |
+
return none
|
| 238 |
+
|
| 239 |
+
private def unifyCasesEqs (numEqs : Nat) (subgoals : Array CasesSubgoal) : MetaM (Array CasesSubgoal) :=
|
| 240 |
+
subgoals.foldlM (init := #[]) fun subgoals s => do
|
| 241 |
+
match (β unifyEqs? numEqs s.mvarId s.subst s.ctorName) with
|
| 242 |
+
| none => pure subgoals
|
| 243 |
+
| some (mvarId, subst) =>
|
| 244 |
+
return subgoals.push { s with
|
| 245 |
+
mvarId := mvarId,
|
| 246 |
+
subst := subst,
|
| 247 |
+
fields := s.fields.map (subst.apply Β·)
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
private def inductionCasesOn (mvarId : MVarId) (majorFVarId : FVarId) (givenNames : Array AltVarNames) (ctx : Context)
|
| 251 |
+
(useNatCasesAuxOn : Bool := false) : MetaM (Array CasesSubgoal) := mvarId.withContext do
|
| 252 |
+
let majorType β inferType (mkFVar majorFVarId)
|
| 253 |
+
let (us, params) β getInductiveUniverseAndParams majorType
|
| 254 |
+
let mut casesOn := mkCasesOnName ctx.inductiveVal.name
|
| 255 |
+
if useNatCasesAuxOn && ctx.inductiveVal.name == ``Nat && (β getEnv).contains ``Nat.casesAuxOn then
|
| 256 |
+
casesOn := ``Nat.casesAuxOn
|
| 257 |
+
let ctors := ctx.inductiveVal.ctors.toArray
|
| 258 |
+
let s β mvarId.induction majorFVarId casesOn givenNames
|
| 259 |
+
return toCasesSubgoals s ctors majorFVarId us params
|
| 260 |
+
|
| 261 |
+
def cases (mvarId : MVarId) (majorFVarId : FVarId) (givenNames : Array AltVarNames := #[]) (useNatCasesAuxOn : Bool := false) : MetaM (Array CasesSubgoal) := do
|
| 262 |
+
try
|
| 263 |
+
mvarId.withContext do
|
| 264 |
+
mvarId.checkNotAssigned `cases
|
| 265 |
+
let context? β mkCasesContext? majorFVarId
|
| 266 |
+
match context? with
|
| 267 |
+
| none => throwTacticEx `cases mvarId "not applicable to the given hypothesis"
|
| 268 |
+
| some ctx =>
|
| 269 |
+
/- Remark: if caller does not need a `FVarSubst` (variable substitution), and `hasIndepIndices ctx` is true,
|
| 270 |
+
then we can also use the simple case. This is a minor optimization, and we currently do not even
|
| 271 |
+
allow callers to specify whether they want the `FVarSubst` or not. -/
|
| 272 |
+
if ctx.inductiveVal.numIndices == 0 then
|
| 273 |
+
-- Simple case
|
| 274 |
+
inductionCasesOn mvarId majorFVarId givenNames ctx (useNatCasesAuxOn := useNatCasesAuxOn)
|
| 275 |
+
else
|
| 276 |
+
let sβ β generalizeIndices mvarId majorFVarId
|
| 277 |
+
trace[Meta.Tactic.cases] "after generalizeIndices\n{MessageData.ofGoal sβ.mvarId}"
|
| 278 |
+
let sβ β inductionCasesOn sβ.mvarId sβ.fvarId givenNames ctx
|
| 279 |
+
let sβ β elimAuxIndices sβ sβ
|
| 280 |
+
unifyCasesEqs sβ.numEqs sβ
|
| 281 |
+
catch ex =>
|
| 282 |
+
throwNestedTacticEx `cases ex
|
| 283 |
+
|
| 284 |
+
end Cases
|
| 285 |
+
|
| 286 |
+
/--
|
| 287 |
+
Apply `casesOn` using the free variable `majorFVarId` as the major premise (aka discriminant).
|
| 288 |
+
`givenNames` contains user-facing names for each alternative.
|
| 289 |
+
|
| 290 |
+
- `useNatCasesAuxOn` is a temporary hack for the `rcases` family of tactics.
|
| 291 |
+
Do not use it, as it is subject to removal.
|
| 292 |
+
It enables using `Nat.casesAuxOn` instead of `Nat.casesOn`,
|
| 293 |
+
which causes case splits on `n : Nat` to be represented as `0` and `n' + 1` rather than as `Nat.zero` and `Nat.succ n'`.
|
| 294 |
+
-/
|
| 295 |
+
def _root_.Lean.MVarId.cases (mvarId : MVarId) (majorFVarId : FVarId) (givenNames : Array AltVarNames := #[]) (useNatCasesAuxOn : Bool := false) : MetaM (Array CasesSubgoal) :=
|
| 296 |
+
Cases.cases mvarId majorFVarId givenNames (useNatCasesAuxOn := useNatCasesAuxOn)
|
| 297 |
+
|
| 298 |
+
/--
|
| 299 |
+
Keep applying `cases` on any hypothesis that satisfies `p`.
|
| 300 |
+
-/
|
| 301 |
+
def _root_.Lean.MVarId.casesRec (mvarId : MVarId) (p : LocalDecl β MetaM Bool) : MetaM (List MVarId) :=
|
| 302 |
+
saturate mvarId fun mvarId =>
|
| 303 |
+
mvarId.withContext do
|
| 304 |
+
for localDecl in (β getLCtx) do
|
| 305 |
+
if (β p localDecl) then
|
| 306 |
+
let r? β observing? do
|
| 307 |
+
let r β mvarId.cases localDecl.fvarId
|
| 308 |
+
return r.toList.map (Β·.mvarId)
|
| 309 |
+
if r?.isSome then
|
| 310 |
+
return r?
|
| 311 |
+
return none
|
| 312 |
+
|
| 313 |
+
/--
|
| 314 |
+
Applies `cases` (recursively) to any hypothesis of the form `h : p β§ q`.
|
| 315 |
+
-/
|
| 316 |
+
def _root_.Lean.MVarId.casesAnd (mvarId : MVarId) : MetaM MVarId := do
|
| 317 |
+
let mvarIds β mvarId.casesRec fun localDecl => return (β instantiateMVars localDecl.type).isAppOfArity ``And 2
|
| 318 |
+
exactlyOne mvarIds
|
| 319 |
+
|
| 320 |
+
/--
|
| 321 |
+
Applies `cases` to any hypothesis of the form `h : r = s`.
|
| 322 |
+
-/
|
| 323 |
+
def _root_.Lean.MVarId.substEqs (mvarId : MVarId) : MetaM (Option MVarId) := do
|
| 324 |
+
let mvarIds β mvarId.casesRec fun localDecl => do
|
| 325 |
+
let type β instantiateMVars localDecl.type
|
| 326 |
+
return type.isEq || type.isHEq
|
| 327 |
+
ensureAtMostOne mvarIds
|
| 328 |
+
|
| 329 |
+
/-- Auxiliary structure for storing `byCases` tactic result. -/
|
| 330 |
+
structure ByCasesSubgoal where
|
| 331 |
+
mvarId : MVarId
|
| 332 |
+
fvarId : FVarId
|
| 333 |
+
|
| 334 |
+
private def toByCasesSubgoal (s : CasesSubgoal) : MetaM ByCasesSubgoal := do
|
| 335 |
+
let #[Expr.fvar fvarId ..] β pure s.fields | throwError "'byCases' tactic failed, unexpected new hypothesis"
|
| 336 |
+
return { mvarId := s.mvarId, fvarId }
|
| 337 |
+
|
| 338 |
+
/--
|
| 339 |
+
Split the goal in two subgoals: one containing the hypothesis `h : p` and another containing `h : Β¬ p`.
|
| 340 |
+
-/
|
| 341 |
+
def _root_.Lean.MVarId.byCases (mvarId : MVarId) (p : Expr) (hName : Name := `h) : MetaM (ByCasesSubgoal Γ ByCasesSubgoal) := do
|
| 342 |
+
let mvarId β mvarId.assert `hByCases (mkOr p (mkNot p)) (mkEM p)
|
| 343 |
+
let (fvarId, mvarId) β mvarId.intro1
|
| 344 |
+
let #[sβ, sβ] β mvarId.cases fvarId #[{ varNames := [hName] }, { varNames := [hName] }] |
|
| 345 |
+
throwError "'byCases' tactic failed, unexpected number of subgoals"
|
| 346 |
+
return ((β toByCasesSubgoal sβ), (β toByCasesSubgoal sβ))
|
| 347 |
+
|
| 348 |
+
/--
|
| 349 |
+
Given `dec : Decidable p`, split the goal in two subgoals: one containing the hypothesis `h : p` and another containing `h : Β¬ p`.
|
| 350 |
+
-/
|
| 351 |
+
def _root_.Lean.MVarId.byCasesDec (mvarId : MVarId) (p : Expr) (dec : Expr) (hName : Name := `h) : MetaM (ByCasesSubgoal Γ ByCasesSubgoal) := do
|
| 352 |
+
let mvarId β mvarId.assert `hByCases (mkApp (mkConst ``Decidable) p) dec
|
| 353 |
+
let (fvarId, mvarId) β mvarId.intro1
|
| 354 |
+
let #[sβ, sβ] β mvarId.cases fvarId #[{ varNames := [hName] }, { varNames := [hName] }] |
|
| 355 |
+
throwError "'byCasesDec' tactic failed, unexpected number of subgoals"
|
| 356 |
+
-- We flip `sβ` and `sβ` because `isFalse` is the first constructor.
|
| 357 |
+
return ((β toByCasesSubgoal sβ), (β toByCasesSubgoal sβ))
|
| 358 |
+
|
| 359 |
+
builtin_initialize registerTraceClass `Meta.Tactic.cases
|
| 360 |
+
|
| 361 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Cleanup.lean
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.CollectFVars
|
| 8 |
+
import Lean.Meta.Tactic.Clear
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta
|
| 11 |
+
|
| 12 |
+
private partial def cleanupCore (mvarId : MVarId) (toPreserve : Array FVarId) (indirectProps : Bool) : MetaM MVarId := do
|
| 13 |
+
mvarId.withContext do
|
| 14 |
+
mvarId.checkNotAssigned `cleanup
|
| 15 |
+
let used β collectUsed |>.run' (false, {})
|
| 16 |
+
let mut lctx β getLCtx
|
| 17 |
+
for localDecl in lctx do
|
| 18 |
+
unless used.contains localDecl.fvarId do
|
| 19 |
+
lctx := lctx.erase localDecl.fvarId
|
| 20 |
+
let localInsts := (β getLocalInstances).filter fun inst => used.contains inst.fvar.fvarId!
|
| 21 |
+
let mvarNew β mkFreshExprMVarAt lctx localInsts (β instantiateMVars (β mvarId.getType)) .syntheticOpaque (β mvarId.getTag)
|
| 22 |
+
mvarId.assign mvarNew
|
| 23 |
+
return mvarNew.mvarId!
|
| 24 |
+
where
|
| 25 |
+
addUsedFVars (e : Expr) : StateRefT (Bool Γ FVarIdSet) MetaM Unit := do
|
| 26 |
+
let (_, s) β (β instantiateMVars e).collectFVars |>.run {}
|
| 27 |
+
for fvarId in s.fvarSet do
|
| 28 |
+
addUsedFVar fvarId
|
| 29 |
+
|
| 30 |
+
addDeps (fvarId : FVarId) : StateRefT (Bool Γ FVarIdSet) MetaM Unit := do
|
| 31 |
+
let localDecl β fvarId.getDecl
|
| 32 |
+
addUsedFVars localDecl.type
|
| 33 |
+
if let some val := localDecl.value? then
|
| 34 |
+
addUsedFVars val
|
| 35 |
+
|
| 36 |
+
addUsedFVar (fvarId : FVarId) : StateRefT (Bool Γ FVarIdSet) MetaM Unit := do
|
| 37 |
+
unless (β get).2.contains fvarId do
|
| 38 |
+
modify fun (_, s) => (true, s.insert fvarId)
|
| 39 |
+
addDeps fvarId
|
| 40 |
+
|
| 41 |
+
/-- We include `p` in the used-set, if `p` is a proposition that contains a `x` that is in the used-set. -/
|
| 42 |
+
collectPropsStep : StateRefT (Bool Γ FVarIdSet) MetaM Unit := do
|
| 43 |
+
let usedSet := (β get).2
|
| 44 |
+
for localDecl in (β getLCtx) do
|
| 45 |
+
if (β isProp localDecl.type) then
|
| 46 |
+
if (β dependsOnPred localDecl.type usedSet.contains) then
|
| 47 |
+
addUsedFVar localDecl.fvarId
|
| 48 |
+
|
| 49 |
+
collectProps : StateRefT (Bool Γ FVarIdSet) MetaM Unit := do
|
| 50 |
+
modify fun s => (false, s.2)
|
| 51 |
+
collectPropsStep
|
| 52 |
+
if (β get).1 then
|
| 53 |
+
collectProps
|
| 54 |
+
|
| 55 |
+
collectUsed : StateRefT (Bool Γ FVarIdSet) MetaM FVarIdSet := do
|
| 56 |
+
addUsedFVars (β instantiateMVars (β mvarId.getType))
|
| 57 |
+
toPreserve.forM addUsedFVar
|
| 58 |
+
if indirectProps then collectProps
|
| 59 |
+
return (β get).2
|
| 60 |
+
|
| 61 |
+
/--
|
| 62 |
+
Auxiliary tactic for cleaning the local context. It removes local declarations (aka hypotheses) that are *not* relevant.
|
| 63 |
+
We say a variable `x` is "relevant" if
|
| 64 |
+
- It occurs in the `toPreserve` array, or
|
| 65 |
+
- It occurs in the target type, or
|
| 66 |
+
- There is a relevant variable `y` that depends on `x`, or
|
| 67 |
+
- If `indirectProps` is true, the type of `x` is a proposition and it depends on a relevant variable `y`.
|
| 68 |
+
|
| 69 |
+
By default, `toPreserve := #[]` and `indirectProps := true`. These settings are used in the mathlib tactic `extract_goal`
|
| 70 |
+
to give the user more control over which variables to include.
|
| 71 |
+
-/
|
| 72 |
+
abbrev _root_.Lean.MVarId.cleanup (mvarId : MVarId) (toPreserve : Array FVarId := #[]) (indirectProps : Bool := true) : MetaM MVarId := do
|
| 73 |
+
cleanupCore mvarId toPreserve indirectProps
|
| 74 |
+
|
| 75 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Clear.lean
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Util
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta
|
| 10 |
+
|
| 11 |
+
/--
|
| 12 |
+
Erase the given free variable from the goal `mvarId`.
|
| 13 |
+
-/
|
| 14 |
+
def _root_.Lean.MVarId.clear (mvarId : MVarId) (fvarId : FVarId) : MetaM MVarId :=
|
| 15 |
+
mvarId.withContext do
|
| 16 |
+
mvarId.checkNotAssigned `clear
|
| 17 |
+
let lctx β getLCtx
|
| 18 |
+
unless lctx.contains fvarId do
|
| 19 |
+
throwTacticEx `clear mvarId m!"unknown variable '{mkFVar fvarId}'"
|
| 20 |
+
let tag β mvarId.getTag
|
| 21 |
+
lctx.forM fun localDecl => do
|
| 22 |
+
unless localDecl.fvarId == fvarId do
|
| 23 |
+
if (β localDeclDependsOn localDecl fvarId) then
|
| 24 |
+
throwTacticEx `clear mvarId m!"variable '{localDecl.toExpr}' depends on '{mkFVar fvarId}'"
|
| 25 |
+
let mvarDecl β mvarId.getDecl
|
| 26 |
+
if (β exprDependsOn mvarDecl.type fvarId) then
|
| 27 |
+
throwTacticEx `clear mvarId m!"target depends on '{mkFVar fvarId}'"
|
| 28 |
+
let lctx := lctx.erase fvarId
|
| 29 |
+
let localInsts β getLocalInstances
|
| 30 |
+
let localInsts := match localInsts.findFinIdx? fun localInst => localInst.fvar.fvarId! == fvarId with
|
| 31 |
+
| none => localInsts
|
| 32 |
+
| some idx => localInsts.eraseIdx idx
|
| 33 |
+
let newMVar β mkFreshExprMVarAt lctx localInsts mvarDecl.type MetavarKind.syntheticOpaque tag
|
| 34 |
+
mvarId.assign newMVar
|
| 35 |
+
pure newMVar.mvarId!
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
/--
|
| 39 |
+
Try to erase the given free variable from the goal `mvarId`. It is no-op if the free variable
|
| 40 |
+
cannot be erased due to forward dependencies.
|
| 41 |
+
-/
|
| 42 |
+
def _root_.Lean.MVarId.tryClear (mvarId : MVarId) (fvarId : FVarId) : MetaM MVarId :=
|
| 43 |
+
mvarId.clear fvarId <|> pure mvarId
|
| 44 |
+
|
| 45 |
+
/--
|
| 46 |
+
Try to clear the given fvars from the local context.
|
| 47 |
+
|
| 48 |
+
The fvars must be given in the order they appear in the local context.
|
| 49 |
+
|
| 50 |
+
See also `tryClearMany'` which takes care of reordering internally,
|
| 51 |
+
and returns the cleared hypotheses along with the new goal.
|
| 52 |
+
-/
|
| 53 |
+
def _root_.Lean.MVarId.tryClearMany (mvarId : MVarId) (fvarIds : Array FVarId) : MetaM MVarId := do
|
| 54 |
+
fvarIds.foldrM (init := mvarId) fun fvarId mvarId => mvarId.tryClear fvarId
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
Try to clear the given fvars from the local context. Returns the new goal and
|
| 58 |
+
the hypotheses that were cleared.
|
| 59 |
+
|
| 60 |
+
Does not require the `hyps` to be given in the order in which they
|
| 61 |
+
appear in the local context.
|
| 62 |
+
-/
|
| 63 |
+
def _root_.Lean.MVarId.tryClearMany' (goal : MVarId) (fvarIds : Array FVarId) :
|
| 64 |
+
MetaM (MVarId Γ Array FVarId) :=
|
| 65 |
+
goal.withContext do
|
| 66 |
+
let fvarIds := (β getLCtx).sortFVarsByContextOrder fvarIds
|
| 67 |
+
fvarIds.foldrM (init := (goal, Array.mkEmpty fvarIds.size))
|
| 68 |
+
fun h (goal, cleared) => do
|
| 69 |
+
let goal' β goal.tryClear h
|
| 70 |
+
let cleared := if goal == goal' then cleared else cleared.push h
|
| 71 |
+
return (goal', cleared)
|
| 72 |
+
|
| 73 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Congr.lean
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.CongrTheorems
|
| 8 |
+
import Lean.Meta.Tactic.Assert
|
| 9 |
+
import Lean.Meta.Tactic.Apply
|
| 10 |
+
import Lean.Meta.Tactic.Clear
|
| 11 |
+
import Lean.Meta.Tactic.Refl
|
| 12 |
+
import Lean.Meta.Tactic.Assumption
|
| 13 |
+
|
| 14 |
+
namespace Lean
|
| 15 |
+
open Meta
|
| 16 |
+
|
| 17 |
+
/--
|
| 18 |
+
Preprocessor before applying congruence theorem.
|
| 19 |
+
Tries to close new goals using `Eq.refl`, `HEq.refl`, and `assumption`.
|
| 20 |
+
It also tries to apply `heq_of_eq`.
|
| 21 |
+
-/
|
| 22 |
+
def MVarId.congrPre (mvarId : MVarId) : MetaM (Option MVarId) := do
|
| 23 |
+
let mvarId β mvarId.heqOfEq
|
| 24 |
+
try mvarId.refl; return none catch _ => pure ()
|
| 25 |
+
try mvarId.hrefl; return none catch _ => pure ()
|
| 26 |
+
if (β mvarId.assumptionCore) then return none
|
| 27 |
+
return some mvarId
|
| 28 |
+
|
| 29 |
+
/--
|
| 30 |
+
Asserts the given congruence theorem as fresh hypothesis, and then applies it.
|
| 31 |
+
Return the `fvarId` for the new hypothesis and the new subgoals.
|
| 32 |
+
-/
|
| 33 |
+
private def applyCongrThm? (mvarId : MVarId) (congrThm : CongrTheorem) : MetaM (List MVarId) := do
|
| 34 |
+
let mvarId β mvarId.assert (β mkFreshUserName `h_congr_thm) congrThm.type congrThm.proof
|
| 35 |
+
let (fvarId, mvarId) β mvarId.intro1P
|
| 36 |
+
let mvarIds β mvarId.apply (mkFVar fvarId) { synthAssignedInstances := false }
|
| 37 |
+
mvarIds.mapM fun mvarId => mvarId.tryClear fvarId
|
| 38 |
+
|
| 39 |
+
/--
|
| 40 |
+
Try to apply a `simp` congruence theorem.
|
| 41 |
+
-/
|
| 42 |
+
def MVarId.congr? (mvarId : MVarId) : MetaM (Option (List MVarId)) :=
|
| 43 |
+
mvarId.withContext do commitWhenSomeNoEx? do
|
| 44 |
+
mvarId.checkNotAssigned `congr
|
| 45 |
+
let target β mvarId.getType'
|
| 46 |
+
let some (_, lhs, _) := target.eq? | return none
|
| 47 |
+
let lhs := lhs.cleanupAnnotations
|
| 48 |
+
unless lhs.isApp do return none
|
| 49 |
+
let some congrThm β mkCongrSimp? lhs.getAppFn (subsingletonInstImplicitRhs := false) | return none
|
| 50 |
+
applyCongrThm? mvarId congrThm
|
| 51 |
+
|
| 52 |
+
/--
|
| 53 |
+
Try to apply a `hcongr` congruence theorem, and then tries to close resulting goals
|
| 54 |
+
using `Eq.refl`, `HEq.refl`, and assumption.
|
| 55 |
+
-/
|
| 56 |
+
def MVarId.hcongr? (mvarId : MVarId) : MetaM (Option (List MVarId)) := do
|
| 57 |
+
commitWhenSomeNoEx? do
|
| 58 |
+
mvarId.checkNotAssigned `congr
|
| 59 |
+
let mvarId β mvarId.eqOfHEq
|
| 60 |
+
mvarId.withContext do
|
| 61 |
+
let target β mvarId.getType'
|
| 62 |
+
let some (_, lhs, _, _) := target.heq? | return none
|
| 63 |
+
let lhs := lhs.cleanupAnnotations
|
| 64 |
+
unless lhs.isApp do return none
|
| 65 |
+
let congrThm β mkHCongr lhs.getAppFn
|
| 66 |
+
applyCongrThm? mvarId congrThm
|
| 67 |
+
|
| 68 |
+
/--
|
| 69 |
+
Try to apply `implies_congr`.
|
| 70 |
+
-/
|
| 71 |
+
def MVarId.congrImplies? (mvarId : MVarId) : MetaM (Option (List MVarId)) :=
|
| 72 |
+
observing? do
|
| 73 |
+
let mvarIdβ :: mvarIdβ :: _ β mvarId.apply (β mkConstWithFreshMVarLevels ``implies_congr) | throwError "unexpected number of goals"
|
| 74 |
+
return [mvarIdβ, mvarIdβ]
|
| 75 |
+
|
| 76 |
+
/--
|
| 77 |
+
Given a goal of the form `β’ f as = f bs`, `β’ (p β q) = (p' β q')`, or `β’ f as β f bs`, try to apply congruence.
|
| 78 |
+
It takes proof irrelevance into account, and the fact that `Decidable p` is a subsingleton.
|
| 79 |
+
-/
|
| 80 |
+
def MVarId.congrCore (mvarId : MVarId) : MetaM (List MVarId) := do
|
| 81 |
+
if let some mvarIds β mvarId.congr? then
|
| 82 |
+
pure mvarIds
|
| 83 |
+
else if let some mvarIds β mvarId.hcongr? then
|
| 84 |
+
pure mvarIds
|
| 85 |
+
else if let some mvarIds β mvarId.congrImplies? then
|
| 86 |
+
pure mvarIds
|
| 87 |
+
else
|
| 88 |
+
throwTacticEx `congr mvarId "failed to apply congruence"
|
| 89 |
+
|
| 90 |
+
/--
|
| 91 |
+
Given a goal of the form `β’ f as = f bs`, `β’ (p β q) = (p' β q')`, or `β’ f as β f bs`, try to apply congruence.
|
| 92 |
+
It takes proof irrelevance into account, and the fact that `Decidable p` is a subsingleton.
|
| 93 |
+
|
| 94 |
+
* Applies `congr` recursively up to depth `depth`.
|
| 95 |
+
* If `closePre := true`, it will attempt to close new goals
|
| 96 |
+
using `Eq.refl`, `HEq.refl`, and `assumption` with reducible transparency.
|
| 97 |
+
* If `closePost := true`, it will try again on goals on which `congr` failed to make progress
|
| 98 |
+
with default transparency.
|
| 99 |
+
-/
|
| 100 |
+
def MVarId.congrN (mvarId : MVarId) (depth : Nat := 1000000) (closePre := true) (closePost := true) : MetaM (List MVarId) := do
|
| 101 |
+
let (_, s) β go depth mvarId |>.run #[]
|
| 102 |
+
return s.toList
|
| 103 |
+
where
|
| 104 |
+
post (mvarId : MVarId) : StateRefT (Array MVarId) MetaM Unit := do
|
| 105 |
+
if closePost && (β getTransparency) != .reducible then
|
| 106 |
+
if let some mvarId β mvarId.congrPre then
|
| 107 |
+
modify (Β·.push mvarId)
|
| 108 |
+
else
|
| 109 |
+
modify (Β·.push mvarId)
|
| 110 |
+
|
| 111 |
+
go (n : Nat) (mvarId : MVarId) : StateRefT (Array MVarId) MetaM Unit := do
|
| 112 |
+
if let some mvarId β if closePre then withReducible mvarId.congrPre else pure mvarId then
|
| 113 |
+
match n with
|
| 114 |
+
| 0 => post mvarId
|
| 115 |
+
| n+1 =>
|
| 116 |
+
let some mvarIds β observing? (m := MetaM) mvarId.congrCore
|
| 117 |
+
| post mvarId
|
| 118 |
+
mvarIds.forM (go n)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Constructor.lean
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Check
|
| 8 |
+
import Lean.Meta.Tactic.Util
|
| 9 |
+
import Lean.Meta.Tactic.Apply
|
| 10 |
+
|
| 11 |
+
namespace Lean.Meta
|
| 12 |
+
|
| 13 |
+
/--
|
| 14 |
+
When the goal `mvarId` type is an inductive datatype,
|
| 15 |
+
`constructor` calls `apply` with the first matching constructor.
|
| 16 |
+
-/
|
| 17 |
+
def _root_.Lean.MVarId.constructor (mvarId : MVarId) (cfg : ApplyConfig := {}) : MetaM (List MVarId) := do
|
| 18 |
+
mvarId.withContext do
|
| 19 |
+
mvarId.checkNotAssigned `constructor
|
| 20 |
+
let target β mvarId.getType'
|
| 21 |
+
matchConstInduct target.getAppFn
|
| 22 |
+
(fun _ => throwTacticEx `constructor mvarId "target is not an inductive datatype")
|
| 23 |
+
fun ival us => do
|
| 24 |
+
for ctor in ival.ctors do
|
| 25 |
+
try
|
| 26 |
+
return β mvarId.apply (Lean.mkConst ctor us) cfg
|
| 27 |
+
catch _ =>
|
| 28 |
+
pure ()
|
| 29 |
+
throwTacticEx `constructor mvarId "no applicable constructor found"
|
| 30 |
+
|
| 31 |
+
def _root_.Lean.MVarId.existsIntro (mvarId : MVarId) (w : Expr) : MetaM MVarId := do
|
| 32 |
+
mvarId.withContext do
|
| 33 |
+
mvarId.checkNotAssigned `exists
|
| 34 |
+
let target β mvarId.getType'
|
| 35 |
+
matchConstStructure target.getAppFn
|
| 36 |
+
(fun _ => throwTacticEx `exists mvarId "target is not an inductive datatype with one constructor")
|
| 37 |
+
fun _ us cval => do
|
| 38 |
+
if cval.numFields < 2 then
|
| 39 |
+
throwTacticEx `exists mvarId "constructor must have at least two fields"
|
| 40 |
+
let ctor := mkAppN (Lean.mkConst cval.name us) target.getAppArgs[*...cval.numParams]
|
| 41 |
+
let ctorType β inferType ctor
|
| 42 |
+
let (mvars, _, _) β forallMetaTelescopeReducing ctorType (some (cval.numFields-2))
|
| 43 |
+
let f := mkAppN ctor mvars
|
| 44 |
+
checkApp f w
|
| 45 |
+
let [mvarId] β mvarId.apply <| mkApp f w
|
| 46 |
+
| throwTacticEx `exists mvarId "unexpected number of subgoals"
|
| 47 |
+
pure mvarId
|
| 48 |
+
|
| 49 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Contradiction.lean
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.MatchUtil
|
| 8 |
+
import Lean.Meta.Tactic.Assumption
|
| 9 |
+
import Lean.Meta.Tactic.Cases
|
| 10 |
+
import Lean.Meta.Tactic.Apply
|
| 11 |
+
|
| 12 |
+
namespace Lean.Meta
|
| 13 |
+
|
| 14 |
+
structure Contradiction.Config where
|
| 15 |
+
useDecide : Bool := true
|
| 16 |
+
/-- Check whether any of the hypotheses is an empty type. -/
|
| 17 |
+
emptyType : Bool := true
|
| 18 |
+
/-- When checking for empty types, `searchFuel` specifies the number of goals visited. -/
|
| 19 |
+
searchFuel : Nat := 16
|
| 20 |
+
/-- Support for hypotheses such as
|
| 21 |
+
```
|
| 22 |
+
h : (x y : Nat) (ys : List Nat) β x = 0 β y::ys = [a, b, c] β False
|
| 23 |
+
```
|
| 24 |
+
This kind of hypotheses appear when proving conditional equation theorems for match expressions. -/
|
| 25 |
+
genDiseq : Bool := false
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
Try to close the current goal by looking for a proof of `False` nested in a `False.Elim` application in the target.
|
| 29 |
+
Return `true` iff the goal has been closed.
|
| 30 |
+
-/
|
| 31 |
+
private def nestedFalseElim (mvarId : MVarId) : MetaM Bool := do
|
| 32 |
+
let target β mvarId.getType
|
| 33 |
+
if let some falseElim := target.find? fun e => e.isAppOfArity ``False.elim 2 && !e.appArg!.hasLooseBVars then
|
| 34 |
+
let falseProof := falseElim.appArg!
|
| 35 |
+
mvarId.assign (β mkFalseElim (β mvarId.getType) falseProof)
|
| 36 |
+
return true
|
| 37 |
+
else
|
| 38 |
+
return false
|
| 39 |
+
|
| 40 |
+
-- We only consider inductives with no constructors and indexed families
|
| 41 |
+
private def isElimEmptyInductiveCandidate (fvarId : FVarId) : MetaM Bool := do
|
| 42 |
+
let type β whnfD (β fvarId.getType)
|
| 43 |
+
matchConstInduct type.getAppFn (fun _ => pure false) fun info _ => do
|
| 44 |
+
return info.ctors.length == 0 || info.numIndices > 0
|
| 45 |
+
|
| 46 |
+
namespace ElimEmptyInductive
|
| 47 |
+
|
| 48 |
+
abbrev M := StateRefT Nat MetaM
|
| 49 |
+
|
| 50 |
+
instance : MonadBacktrack SavedState M where
|
| 51 |
+
saveState := Meta.saveState
|
| 52 |
+
restoreState s := s.restore
|
| 53 |
+
|
| 54 |
+
partial def elim (mvarId : MVarId) (fvarId : FVarId) : M Bool := do
|
| 55 |
+
if (β get) == 0 then
|
| 56 |
+
trace[Meta.Tactic.contradiction] "elimEmptyInductive out-of-fuel"
|
| 57 |
+
return false
|
| 58 |
+
modify (Β· - 1)
|
| 59 |
+
-- We only consider inductives with no constructors and indexed families
|
| 60 |
+
commitWhen do
|
| 61 |
+
let subgoals β try mvarId.cases fvarId catch ex => trace[Meta.Tactic.contradiction] "{ex.toMessageData}"; return false
|
| 62 |
+
trace[Meta.Tactic.contradiction] "elimEmptyInductive, number subgoals: {subgoals.size}"
|
| 63 |
+
for subgoal in subgoals do
|
| 64 |
+
-- If one of the fields is uninhabited, then we are done
|
| 65 |
+
let found β subgoal.mvarId.withContext do
|
| 66 |
+
for field in subgoal.fields do
|
| 67 |
+
let field := subgoal.subst.apply field
|
| 68 |
+
if field.isFVar then
|
| 69 |
+
if (β isElimEmptyInductiveCandidate field.fvarId!) then
|
| 70 |
+
if (β elim subgoal.mvarId field.fvarId!) then
|
| 71 |
+
return true
|
| 72 |
+
return false
|
| 73 |
+
unless found do
|
| 74 |
+
return false
|
| 75 |
+
return true
|
| 76 |
+
|
| 77 |
+
end ElimEmptyInductive
|
| 78 |
+
|
| 79 |
+
private def elimEmptyInductive (mvarId : MVarId) (fvarId : FVarId) (fuel : Nat) : MetaM Bool := do
|
| 80 |
+
mvarId.withContext do
|
| 81 |
+
if (β isElimEmptyInductiveCandidate fvarId) then
|
| 82 |
+
commitWhen do
|
| 83 |
+
ElimEmptyInductive.elim (β mvarId.exfalso) fvarId |>.run' fuel
|
| 84 |
+
else
|
| 85 |
+
return false
|
| 86 |
+
|
| 87 |
+
/--
|
| 88 |
+
See `Simp.isEqnThmHypothesis`
|
| 89 |
+
-/
|
| 90 |
+
private abbrev isGenDiseq (e : Expr) : Bool :=
|
| 91 |
+
Simp.isEqnThmHypothesis e
|
| 92 |
+
|
| 93 |
+
/--
|
| 94 |
+
Given `e` s.t. `isGenDiseq e`, generate a bit-mask `mask` s.t. `mask[i] = true` iff
|
| 95 |
+
the `i`-th binder is an equality without forward dependencies.
|
| 96 |
+
|
| 97 |
+
See `processGenDiseq`
|
| 98 |
+
-/
|
| 99 |
+
def mkGenDiseqMask (e : Expr) : Array Bool :=
|
| 100 |
+
go e #[]
|
| 101 |
+
where
|
| 102 |
+
go (e : Expr) (acc : Array Bool) : Array Bool :=
|
| 103 |
+
match e with
|
| 104 |
+
| .forallE _ d b _ => go b (acc.push (!b.hasLooseBVar 0 && (d.isEq || d.isHEq)))
|
| 105 |
+
| _ => acc
|
| 106 |
+
|
| 107 |
+
/--
|
| 108 |
+
Close goal if `localDecl` is a "generalized disequality". Example:
|
| 109 |
+
```
|
| 110 |
+
h : (x y : Nat) (ys : List Nat) β x = 0 β y::ys = [a, b, c] β False
|
| 111 |
+
```
|
| 112 |
+
This kind of hypotheses is created when we generate conditional equations for match expressions.
|
| 113 |
+
-/
|
| 114 |
+
private def processGenDiseq (mvarId : MVarId) (localDecl : LocalDecl) : MetaM Bool := do
|
| 115 |
+
assert! isGenDiseq localDecl.type
|
| 116 |
+
let val? β withNewMCtxDepth do
|
| 117 |
+
let (args, _, _) β forallMetaTelescope localDecl.type
|
| 118 |
+
let mask := mkGenDiseqMask localDecl.type
|
| 119 |
+
for arg in args, useRefl in mask do
|
| 120 |
+
if useRefl then
|
| 121 |
+
/- Remark: we should not try to use `refl` for equalities that have forward dependencies because
|
| 122 |
+
they correspond to constructor fields. We did not use to have this extra test, and this method failed
|
| 123 |
+
to close the following goal.
|
| 124 |
+
```
|
| 125 |
+
...
|
| 126 |
+
ns' : NEList String
|
| 127 |
+
h' : NEList.notUno ns' = true
|
| 128 |
+
: β (ns : NEList String) (h : NEList.notUno ns = true), Value.lam (Lambda.mk ns' h') = Value.lam (Lambda.mk ns h) β False
|
| 129 |
+
β’ h_1 l a = h_2 v
|
| 130 |
+
|
| 131 |
+
```
|
| 132 |
+
-/
|
| 133 |
+
if let some (_, lhs, _) β matchEq? (β inferType arg) then
|
| 134 |
+
unless (β isDefEq arg (β mkEqRefl lhs)) do
|
| 135 |
+
return none
|
| 136 |
+
if let some (_, lhs, _, _) β matchHEq? (β inferType arg) then
|
| 137 |
+
unless (β isDefEq arg (β mkHEqRefl lhs)) do
|
| 138 |
+
return none
|
| 139 |
+
let falseProof β instantiateMVars (mkAppN localDecl.toExpr args)
|
| 140 |
+
if (β hasAssignableMVar falseProof) then
|
| 141 |
+
return none
|
| 142 |
+
return some (β mkFalseElim (β mvarId.getType) falseProof)
|
| 143 |
+
if let some val := val? then
|
| 144 |
+
mvarId.assign val
|
| 145 |
+
return true
|
| 146 |
+
else
|
| 147 |
+
return false
|
| 148 |
+
|
| 149 |
+
/--
|
| 150 |
+
Return `true` if goal `mvarId` has contradictory hypotheses.
|
| 151 |
+
See `MVarId.contradiction` for the list of tests performed by this method.
|
| 152 |
+
-/
|
| 153 |
+
def _root_.Lean.MVarId.contradictionCore (mvarId : MVarId) (config : Contradiction.Config) : MetaM Bool := do
|
| 154 |
+
mvarId.withContext do
|
| 155 |
+
mvarId.checkNotAssigned `contradiction
|
| 156 |
+
if (β nestedFalseElim mvarId) then
|
| 157 |
+
return true
|
| 158 |
+
for localDecl in (β getLCtx) do
|
| 159 |
+
unless localDecl.isImplementationDetail do
|
| 160 |
+
-- (h : Β¬ p) (h' : p)
|
| 161 |
+
if let some p β matchNot? localDecl.type then
|
| 162 |
+
if let some pFVarId β findLocalDeclWithType? p then
|
| 163 |
+
-- We use `False.elim` because `p`'s type may be Type
|
| 164 |
+
mvarId.assign (β mkFalseElim (β mvarId.getType) (mkApp localDecl.toExpr (mkFVar pFVarId)))
|
| 165 |
+
return true
|
| 166 |
+
-- (h : x β x)
|
| 167 |
+
if let some (_, lhs, rhs) β matchNe? localDecl.type then
|
| 168 |
+
if (β isDefEq lhs rhs) then
|
| 169 |
+
mvarId.assign (β mkAbsurd (β mvarId.getType) (β mkEqRefl lhs) localDecl.toExpr)
|
| 170 |
+
return true
|
| 171 |
+
let mut isEq := false
|
| 172 |
+
-- (h : ctorβ ... = ctorβ ...)
|
| 173 |
+
if let some (_, lhs, rhs) β matchEq? localDecl.type then
|
| 174 |
+
isEq := true
|
| 175 |
+
if let some lhsCtor β matchConstructorApp? lhs then
|
| 176 |
+
if let some rhsCtor β matchConstructorApp? rhs then
|
| 177 |
+
if lhsCtor.name != rhsCtor.name then
|
| 178 |
+
mvarId.assign (β mkNoConfusion (β mvarId.getType) localDecl.toExpr)
|
| 179 |
+
return true
|
| 180 |
+
let mut isHEq := false
|
| 181 |
+
-- (h : ctorβ ... β ctorβ ...)
|
| 182 |
+
if let some (Ξ±, lhs, Ξ², rhs) β matchHEq? localDecl.type then
|
| 183 |
+
isHEq := true
|
| 184 |
+
if let some lhsCtor β matchConstructorApp? lhs then
|
| 185 |
+
if let some rhsCtor β matchConstructorApp? rhs then
|
| 186 |
+
if lhsCtor.name != rhsCtor.name then
|
| 187 |
+
if (β isDefEq Ξ± Ξ²) then
|
| 188 |
+
mvarId.assign (β mkNoConfusion (β mvarId.getType) (β mkEqOfHEq localDecl.toExpr))
|
| 189 |
+
return true
|
| 190 |
+
-- (h : p) s.t. `decide p` evaluates to `false`
|
| 191 |
+
if config.useDecide && !localDecl.type.hasFVar then
|
| 192 |
+
let type β instantiateMVars localDecl.type
|
| 193 |
+
if !type.hasMVar && !type.hasFVar then
|
| 194 |
+
try
|
| 195 |
+
let d β mkDecide localDecl.type
|
| 196 |
+
let r β withDefault <| whnf d
|
| 197 |
+
if r.isConstOf ``false then
|
| 198 |
+
let hn := mkAppN (mkConst ``of_decide_eq_false) <| d.getAppArgs.push (β mkEqRefl d)
|
| 199 |
+
mvarId.assign (β mkAbsurd (β mvarId.getType) localDecl.toExpr hn)
|
| 200 |
+
return true
|
| 201 |
+
catch _ =>
|
| 202 |
+
pure ()
|
| 203 |
+
-- "generalized" diseq
|
| 204 |
+
if config.genDiseq && isGenDiseq localDecl.type then
|
| 205 |
+
if (β processGenDiseq mvarId localDecl) then
|
| 206 |
+
return true
|
| 207 |
+
-- (h : <empty-inductive-type>)
|
| 208 |
+
if config.emptyType && !isEq && !isHEq then
|
| 209 |
+
-- We do not use `elimEmptyInductive` for equality, since `cases h` produces a huge proof
|
| 210 |
+
-- when `(h : 10000 = 10001)`. TODO: `cases` add a threshold at `cases`
|
| 211 |
+
if (β elimEmptyInductive mvarId localDecl.fvarId config.searchFuel) then
|
| 212 |
+
return true
|
| 213 |
+
return false
|
| 214 |
+
|
| 215 |
+
/--
|
| 216 |
+
Try to close the goal using "contradictions" such as
|
| 217 |
+
- Contradictory hypotheses `hβ : p` and `hβ : Β¬ p`.
|
| 218 |
+
- Contradictory disequality `h : x β x`.
|
| 219 |
+
- Contradictory equality between different constructors, e.g., `h : List.nil = List.cons x xs`.
|
| 220 |
+
- Empty inductive types, e.g., `x : Fin 0`.
|
| 221 |
+
- Decidable propositions that evaluate to false, i.e., a hypothesis `h : p` s.t. `decide p` reduces to `false`.
|
| 222 |
+
This is only tried if `Config.useDecide = true`.
|
| 223 |
+
|
| 224 |
+
Throw exception if goal failed to be closed.
|
| 225 |
+
-/
|
| 226 |
+
def _root_.Lean.MVarId.contradiction (mvarId : MVarId) (config : Contradiction.Config := {}) : MetaM Unit :=
|
| 227 |
+
unless (β mvarId.contradictionCore config) do
|
| 228 |
+
throwTacticEx `contradiction mvarId
|
| 229 |
+
|
| 230 |
+
builtin_initialize registerTraceClass `Meta.Tactic.contradiction
|
| 231 |
+
|
| 232 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Delta.lean
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Transform
|
| 8 |
+
import Lean.Meta.Tactic.Replace
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta
|
| 11 |
+
|
| 12 |
+
def delta? (e : Expr) (p : Name β Bool := fun _ => true) : CoreM (Option Expr) :=
|
| 13 |
+
matchConst e.getAppFn (fun _ => return none) fun fInfo fLvls => do
|
| 14 |
+
if p fInfo.name && fInfo.hasValue && fInfo.levelParams.length == fLvls.length then
|
| 15 |
+
let f β instantiateValueLevelParams fInfo fLvls
|
| 16 |
+
return some (f.betaRev e.getAppRevArgs (useZeta := true))
|
| 17 |
+
else
|
| 18 |
+
return none
|
| 19 |
+
|
| 20 |
+
/-- Low-level delta expansion. It is used to implement equation lemmas and elimination principles for recursive definitions. -/
|
| 21 |
+
def deltaExpand (e : Expr) (p : Name β Bool) : CoreM Expr :=
|
| 22 |
+
Core.transform e fun e => do
|
| 23 |
+
match (β delta? e p) with
|
| 24 |
+
| some e' => return .visit e'
|
| 25 |
+
| none => return .continue
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
Delta expand declarations that satisfy `p` at `mvarId` type.
|
| 29 |
+
-/
|
| 30 |
+
def _root_.Lean.MVarId.deltaTarget (mvarId : MVarId) (p : Name β Bool) : MetaM MVarId :=
|
| 31 |
+
mvarId.withContext do
|
| 32 |
+
mvarId.checkNotAssigned `delta
|
| 33 |
+
mvarId.change (β deltaExpand (β mvarId.getType) p) (checkDefEq := false)
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
Delta expand declarations that satisfy `p` at `fvarId` type.
|
| 37 |
+
-/
|
| 38 |
+
def _root_.Lean.MVarId.deltaLocalDecl (mvarId : MVarId) (fvarId : FVarId) (p : Name β Bool) : MetaM MVarId :=
|
| 39 |
+
mvarId.withContext do
|
| 40 |
+
mvarId.checkNotAssigned `delta
|
| 41 |
+
mvarId.changeLocalDecl fvarId (β deltaExpand (β fvarId.getType) p) (checkDefEq := false)
|
| 42 |
+
|
| 43 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/ElimInfo.lean
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Basic
|
| 8 |
+
import Lean.Meta.Check
|
| 9 |
+
import Lean.ScopedEnvExtension
|
| 10 |
+
|
| 11 |
+
namespace Lean.Meta
|
| 12 |
+
|
| 13 |
+
structure ElimAltInfo where
|
| 14 |
+
name : Name
|
| 15 |
+
/-- A declaration corresponding to the inductive constructor.
|
| 16 |
+
(For custom recursors, the alternatives correspond to parameter names in the
|
| 17 |
+
recursor, so we may not have a declaration to point to.)
|
| 18 |
+
This is used for go-to-definition on the alternative name. -/
|
| 19 |
+
declName? : Option Name
|
| 20 |
+
numFields : Nat
|
| 21 |
+
/-- If `provesMotive := true`, then this alternative has `motive` as its conclusion.
|
| 22 |
+
Only for those alternatives the `induction` tactic should introduce reverted hypotheses. -/
|
| 23 |
+
provesMotive : Bool
|
| 24 |
+
deriving Repr, Inhabited
|
| 25 |
+
|
| 26 |
+
/--
|
| 27 |
+
Information about an eliminator as used by `induction` or `cases`.
|
| 28 |
+
|
| 29 |
+
Created from an expression by `getElimInfo`. This typically contains level metavariables that
|
| 30 |
+
are instantiated as we go (e.g. in `addImplicitTargets`), so this is single use.
|
| 31 |
+
-/
|
| 32 |
+
structure ElimInfo where
|
| 33 |
+
elimExpr : Expr
|
| 34 |
+
elimType : Expr
|
| 35 |
+
motivePos : Nat
|
| 36 |
+
targetsPos : Array Nat := #[]
|
| 37 |
+
altsInfo : Array ElimAltInfo := #[]
|
| 38 |
+
/--
|
| 39 |
+
Extra arguments to the motive, after the targets, that are instantiated with
|
| 40 |
+
non-atomic expressions in the goal
|
| 41 |
+
-/
|
| 42 |
+
numComplexMotiveArgs : Nat
|
| 43 |
+
deriving Repr, Inhabited
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
/-- Given the type `t` of an alternative, determines the number of parameters
|
| 47 |
+
(.forall and .let)-bound, and whether the conclusion is a `motive`-application. -/
|
| 48 |
+
def altArity (motive : Expr) (n : Nat) : Expr β Nat Γ Bool
|
| 49 |
+
| .forallE _ _ b _ => altArity motive (n+1) b
|
| 50 |
+
| .letE _ _ _ b _ => altArity motive (n+1) b
|
| 51 |
+
| conclusion => (n, conclusion.getAppFn == motive)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def getElimExprInfo (elimExpr : Expr) (baseDeclName? : Option Name := none) : MetaM ElimInfo := do
|
| 55 |
+
let elimType β inferType elimExpr
|
| 56 |
+
trace[Elab.induction] "eliminator {indentExpr elimExpr}\nhas type{indentExpr elimType}"
|
| 57 |
+
forallTelescopeReducing elimType fun xs type => type.withApp fun motive motiveArgs => do
|
| 58 |
+
unless motive.isFVar do
|
| 59 |
+
throwError "expected resulting type of eliminator to be an application of one of its parameters (the motive):{indentExpr type}"
|
| 60 |
+
let targets := motiveArgs.takeWhile (Β·.isFVar)
|
| 61 |
+
let complexMotiveArgs := motiveArgs[targets.size...*]
|
| 62 |
+
let motiveType β inferType motive
|
| 63 |
+
forallTelescopeReducing motiveType fun motiveParams motiveResultType => do
|
| 64 |
+
unless motiveParams.size == motiveArgs.size do
|
| 65 |
+
throwError "expected {motiveArgs.size} parameters at motive type, got {motiveParams.size}:{indentExpr motiveType}"
|
| 66 |
+
unless motiveResultType.isSort do
|
| 67 |
+
throwError "motive result type must be a sort{indentExpr motiveType}"
|
| 68 |
+
let some motivePos β pure (xs.idxOf? motive) |
|
| 69 |
+
throwError "unexpected eliminator type{indentExpr elimType}"
|
| 70 |
+
let targetsPos β targets.mapM fun target => do
|
| 71 |
+
match xs.idxOf? target with
|
| 72 |
+
| none => throwError "unexpected eliminator type{indentExpr elimType}"
|
| 73 |
+
| some targetPos => pure targetPos
|
| 74 |
+
let mut altsInfo := #[]
|
| 75 |
+
let env β getEnv
|
| 76 |
+
for h : i in [:xs.size] do
|
| 77 |
+
let x := xs[i]
|
| 78 |
+
if x != motive && !targets.contains x then
|
| 79 |
+
let xDecl β x.fvarId!.getDecl
|
| 80 |
+
if xDecl.binderInfo.isExplicit then
|
| 81 |
+
let (numFields, provesMotive) := altArity motive 0 xDecl.type
|
| 82 |
+
let name := xDecl.userName
|
| 83 |
+
let declName? := do
|
| 84 |
+
let base β baseDeclName?
|
| 85 |
+
let altDeclName := base ++ name
|
| 86 |
+
if env.contains altDeclName then some altDeclName else none
|
| 87 |
+
altsInfo := altsInfo.push { name, declName?, numFields, provesMotive }
|
| 88 |
+
pure { elimExpr, elimType, motivePos, targetsPos, altsInfo, numComplexMotiveArgs := complexMotiveArgs.size }
|
| 89 |
+
|
| 90 |
+
def getElimInfo (elimName : Name) (baseDeclName? : Option Name := none) : MetaM ElimInfo := do
|
| 91 |
+
getElimExprInfo (β mkConstWithFreshMVarLevels elimName) baseDeclName?
|
| 92 |
+
|
| 93 |
+
/--
|
| 94 |
+
Eliminators/recursors may have implicit targets. For builtin recursors, all indices are implicit targets.
|
| 95 |
+
Given an eliminator and the sequence of explicit targets, this methods returns a new sequence containing
|
| 96 |
+
implicit and explicit targets.
|
| 97 |
+
-/
|
| 98 |
+
partial def addImplicitTargets (elimInfo : ElimInfo) (targets : Array Expr) : MetaM (Array Expr) := do
|
| 99 |
+
let (implicitMVars, targets) β collect elimInfo.elimType 0 0 #[] #[]
|
| 100 |
+
for mvar in implicitMVars do
|
| 101 |
+
unless β mvar.isAssigned do
|
| 102 |
+
let name := (βmvar.getDecl).userName
|
| 103 |
+
if name.isAnonymous || name.hasMacroScopes then
|
| 104 |
+
throwError "failed to infer implicit target"
|
| 105 |
+
else
|
| 106 |
+
throwError "failed to infer implicit target {(βmvar.getDecl).userName}"
|
| 107 |
+
targets.mapM instantiateMVars
|
| 108 |
+
where
|
| 109 |
+
collect (type : Expr) (argIdx targetIdx : Nat) (implicits : Array MVarId) (targets' : Array Expr) :
|
| 110 |
+
MetaM (Array MVarId Γ Array Expr) := do
|
| 111 |
+
match (οΏ½οΏ½ whnfD type) with
|
| 112 |
+
| Expr.forallE n d b bi =>
|
| 113 |
+
if elimInfo.targetsPos.contains argIdx then
|
| 114 |
+
if bi.isExplicit then
|
| 115 |
+
unless targetIdx < targets.size do
|
| 116 |
+
throwError "insufficient number of targets for '{elimInfo.elimExpr}'"
|
| 117 |
+
let target := targets[targetIdx]!
|
| 118 |
+
let targetType β inferType target
|
| 119 |
+
unless (β isDefEq d targetType) do
|
| 120 |
+
throwError "target{indentExpr target}\n{β mkHasTypeButIsExpectedMsg targetType d}"
|
| 121 |
+
collect (b.instantiate1 target) (argIdx+1) (targetIdx+1) implicits (targets'.push target)
|
| 122 |
+
else
|
| 123 |
+
let implicitTarget β mkFreshExprMVar (type? := d) (userName := n)
|
| 124 |
+
collect (b.instantiate1 implicitTarget) (argIdx+1) targetIdx (implicits.push implicitTarget.mvarId!) (targets'.push implicitTarget)
|
| 125 |
+
else
|
| 126 |
+
collect (b.instantiate1 (β mkFreshExprMVar d)) (argIdx+1) targetIdx implicits targets'
|
| 127 |
+
| _ =>
|
| 128 |
+
unless targetIdx = targets.size do
|
| 129 |
+
throwError "extra targets for '{elimInfo.elimExpr}'"
|
| 130 |
+
return (implicits, targets')
|
| 131 |
+
|
| 132 |
+
structure CustomEliminator where
|
| 133 |
+
induction : Bool
|
| 134 |
+
typeNames : Array Name
|
| 135 |
+
elimName : Name -- NB: Do not store the ElimInfo, it can contain MVars
|
| 136 |
+
deriving Inhabited, Repr
|
| 137 |
+
|
| 138 |
+
structure CustomEliminators where
|
| 139 |
+
map : SMap (Bool Γ Array Name) Name := {}
|
| 140 |
+
deriving Inhabited, Repr
|
| 141 |
+
|
| 142 |
+
def addCustomEliminatorEntry (es : CustomEliminators) (e : CustomEliminator) : CustomEliminators :=
|
| 143 |
+
match es with
|
| 144 |
+
| { map := map } => { map := map.insert (e.induction, e.typeNames) e.elimName }
|
| 145 |
+
|
| 146 |
+
builtin_initialize customEliminatorExt : SimpleScopedEnvExtension CustomEliminator CustomEliminators β
|
| 147 |
+
registerSimpleScopedEnvExtension {
|
| 148 |
+
initial := {}
|
| 149 |
+
addEntry := addCustomEliminatorEntry
|
| 150 |
+
finalizeImport := fun { map := map } => { map := map.switch }
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
def mkCustomEliminator (elimName : Name) (induction : Bool) : MetaM CustomEliminator := do
|
| 154 |
+
let elimInfo β getElimInfo elimName
|
| 155 |
+
let info β getConstInfo elimName
|
| 156 |
+
forallTelescopeReducing info.type fun xs _ => do
|
| 157 |
+
let mut typeNames := #[]
|
| 158 |
+
for hi : i in [:elimInfo.targetsPos.size] do
|
| 159 |
+
let targetPos := elimInfo.targetsPos[i]
|
| 160 |
+
let x := xs[targetPos]!
|
| 161 |
+
/- Return true if there is another target that depends on `x`. -/
|
| 162 |
+
let isImplicitTarget : MetaM Bool := do
|
| 163 |
+
for hj : j in [i+1:elimInfo.targetsPos.size] do
|
| 164 |
+
let y := xs[elimInfo.targetsPos[j]]!
|
| 165 |
+
let yType β inferType y
|
| 166 |
+
if (β dependsOn yType x.fvarId!) then
|
| 167 |
+
return true
|
| 168 |
+
return false
|
| 169 |
+
/- We should only use explicit targets when creating the key for the `CustomEliminators` map.
|
| 170 |
+
See test `tests/lean/run/eliminatorImplicitTargets.lean`. -/
|
| 171 |
+
unless (β isImplicitTarget) do
|
| 172 |
+
let xType β inferType x
|
| 173 |
+
let .const typeName .. := xType.getAppFn | throwError "unexpected eliminator target type{indentExpr xType}"
|
| 174 |
+
typeNames := typeNames.push typeName
|
| 175 |
+
return { induction, typeNames, elimName }
|
| 176 |
+
|
| 177 |
+
def addCustomEliminator (declName : Name) (attrKind : AttributeKind) (induction : Bool) : MetaM Unit := do
|
| 178 |
+
let e β mkCustomEliminator declName induction
|
| 179 |
+
customEliminatorExt.add e attrKind
|
| 180 |
+
|
| 181 |
+
/--
|
| 182 |
+
Registers a custom eliminator for the `induction` tactic.
|
| 183 |
+
|
| 184 |
+
Whenever the types of the targets in an `induction` call matches a custom eliminator, it is used
|
| 185 |
+
instead of the recursor. This can be useful for redefining the default eliminator to a more useful
|
| 186 |
+
one.
|
| 187 |
+
|
| 188 |
+
Example:
|
| 189 |
+
```lean example
|
| 190 |
+
structure Three where
|
| 191 |
+
val : Fin 3
|
| 192 |
+
|
| 193 |
+
example (x : Three) (p : Three β Prop) : p x := by
|
| 194 |
+
induction x
|
| 195 |
+
-- val : Fin 3 β’ p β¨valβ©
|
| 196 |
+
|
| 197 |
+
@[induction_eliminator, elab_as_elim]
|
| 198 |
+
def Three.myRec {motive : Three β Sort u}
|
| 199 |
+
(zero : motive β¨0β©) (one : motive β¨1β©) (two : motive β¨2β©) :
|
| 200 |
+
β x, motive x
|
| 201 |
+
| β¨0β© => zero | β¨1β© => one | β¨2β© => two
|
| 202 |
+
|
| 203 |
+
example (x : Three) (p : Three β Prop) : p x := by
|
| 204 |
+
induction x
|
| 205 |
+
-- β’ p β¨0β©
|
| 206 |
+
-- β’ p β¨1β©
|
| 207 |
+
-- β’ p β¨2β©
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
`@[cases_eliminator]` works similarly for the `cases` tactic.
|
| 211 |
+
-/
|
| 212 |
+
@[builtin_doc]
|
| 213 |
+
builtin_initialize
|
| 214 |
+
registerBuiltinAttribute {
|
| 215 |
+
name := `induction_eliminator
|
| 216 |
+
descr := "custom `rec`-like eliminator for the `induction` tactic"
|
| 217 |
+
add := fun declName _ attrKind => do
|
| 218 |
+
discard <| addCustomEliminator declName attrKind (induction := true) |>.run {} {}
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/--
|
| 222 |
+
Registers a custom eliminator for the `cases` tactic.
|
| 223 |
+
|
| 224 |
+
Whenever the types of the targets in an `cases` call matches a custom eliminator, it is used
|
| 225 |
+
instead of the `casesOn` eliminator. This can be useful for redefining the default eliminator to a
|
| 226 |
+
more useful one.
|
| 227 |
+
|
| 228 |
+
Example:
|
| 229 |
+
```lean example
|
| 230 |
+
structure Three where
|
| 231 |
+
val : Fin 3
|
| 232 |
+
|
| 233 |
+
example (x : Three) (p : Three β Prop) : p x := by
|
| 234 |
+
cases x
|
| 235 |
+
-- val : Fin 3 β’ p β¨valβ©
|
| 236 |
+
|
| 237 |
+
@[cases_eliminator, elab_as_elim]
|
| 238 |
+
def Three.myRec {motive : Three β Sort u}
|
| 239 |
+
(zero : motive β¨0β©) (one : motive β¨1β©) (two : motive β¨2β©) :
|
| 240 |
+
β x, motive x
|
| 241 |
+
| οΏ½οΏ½0β© => zero | β¨1β© => one | β¨2β© => two
|
| 242 |
+
|
| 243 |
+
example (x : Three) (p : Three β Prop) : p x := by
|
| 244 |
+
cases x
|
| 245 |
+
-- β’ p β¨0β©
|
| 246 |
+
-- β’ p β¨1β©
|
| 247 |
+
-- β’ p β¨2β©
|
| 248 |
+
```
|
| 249 |
+
|
| 250 |
+
`@[induction_eliminator]` works similarly for the `induction` tactic.
|
| 251 |
+
-/
|
| 252 |
+
@[builtin_doc]
|
| 253 |
+
builtin_initialize
|
| 254 |
+
registerBuiltinAttribute {
|
| 255 |
+
name := `cases_eliminator
|
| 256 |
+
descr := "custom `casesOn`-like eliminator for the `cases` tactic"
|
| 257 |
+
add := fun declName _ attrKind => do
|
| 258 |
+
discard <| addCustomEliminator declName attrKind (induction := false) |>.run {} {}
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
/-- Gets all the eliminators defined using the `@[induction_eliminator]` and `@[cases_eliminator]` attributes. -/
|
| 262 |
+
def getCustomEliminators : CoreM CustomEliminators := do
|
| 263 |
+
return customEliminatorExt.getState (β getEnv)
|
| 264 |
+
|
| 265 |
+
/--
|
| 266 |
+
Gets an eliminator appropriate for the provided array of targets.
|
| 267 |
+
If `induction` is `true` then returns a matching eliminator defined using the `@[induction_eliminator]` attribute
|
| 268 |
+
and otherwise returns one defined using the `@[cases_eliminator]` attribute.
|
| 269 |
+
|
| 270 |
+
The `@[induction_eliminator]` attribute is for the `induction` tactic
|
| 271 |
+
and the `@[cases_eliminator]` attribute is for the `cases` tactic.
|
| 272 |
+
-/
|
| 273 |
+
def getCustomEliminator? (targets : Array Expr) (induction : Bool) : MetaM (Option Name) := do
|
| 274 |
+
let mut key := #[]
|
| 275 |
+
for target in targets do
|
| 276 |
+
let targetType := (β instantiateMVars (β inferType target)).headBeta
|
| 277 |
+
let .const declName .. := targetType.getAppFn | return none
|
| 278 |
+
key := key.push declName
|
| 279 |
+
return customEliminatorExt.getState (β getEnv) |>.map.find? (induction, key)
|
| 280 |
+
|
| 281 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/ExposeNames.lean
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Util
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta
|
| 10 |
+
|
| 11 |
+
/-- Returns a copy of the local context in which all declarations have clear, accessible names. -/
|
| 12 |
+
private def getLCtxWithExposedNames : MetaM LocalContext := do
|
| 13 |
+
let mut map : Std.HashMap Name FVarId := {}
|
| 14 |
+
let mut toRename := #[]
|
| 15 |
+
let mut lctx β getLCtx
|
| 16 |
+
for localDecl in lctx do
|
| 17 |
+
let userName := localDecl.userName
|
| 18 |
+
if userName.hasMacroScopes then
|
| 19 |
+
toRename := toRename.push localDecl.fvarId
|
| 20 |
+
else
|
| 21 |
+
if let some fvarId := map[userName]? then
|
| 22 |
+
-- Variable has been shadowed
|
| 23 |
+
toRename := toRename.push fvarId
|
| 24 |
+
map := map.insert userName localDecl.fvarId
|
| 25 |
+
if toRename.isEmpty then
|
| 26 |
+
return lctx
|
| 27 |
+
let mut next : Std.HashMap Name Nat := {}
|
| 28 |
+
-- Remark: Shadowed variables may be inserted later.
|
| 29 |
+
toRename := toRename.qsort fun fvarIdβ fvarIdβ =>
|
| 30 |
+
(lctx.get! fvarIdβ).index < (lctx.get! fvarIdβ).index
|
| 31 |
+
for fvarId in toRename do
|
| 32 |
+
let localDecl := lctx.get! fvarId
|
| 33 |
+
let mut baseName := localDecl.userName
|
| 34 |
+
if baseName.hasMacroScopes then
|
| 35 |
+
baseName := baseName.eraseMacroScopes
|
| 36 |
+
if baseName == `x || baseName == `a then
|
| 37 |
+
if (β isProp localDecl.type) then
|
| 38 |
+
baseName := `h
|
| 39 |
+
let mut userName := baseName
|
| 40 |
+
let mut i := next[baseName]?.getD 0
|
| 41 |
+
repeat
|
| 42 |
+
if !map.contains userName then
|
| 43 |
+
break
|
| 44 |
+
i := i + 1
|
| 45 |
+
userName := baseName.appendIndexAfter i
|
| 46 |
+
next := next.insert baseName i
|
| 47 |
+
map := map.insert userName fvarId
|
| 48 |
+
lctx := lctx.modifyLocalDecl fvarId (Β·.setUserName userName)
|
| 49 |
+
return lctx
|
| 50 |
+
|
| 51 |
+
/--
|
| 52 |
+
Creates a new goal whose local context has been "exposed" so that every local declaration has a clear, accessible name.
|
| 53 |
+
If no local declarations require renaming, the original goal is returned unchanged.
|
| 54 |
+
-/
|
| 55 |
+
def _root_.Lean.MVarId.exposeNames (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
|
| 56 |
+
mvarId.checkNotAssigned `expose_names
|
| 57 |
+
let lctx β getLCtxWithExposedNames
|
| 58 |
+
let mvarNew β mkFreshExprMVarAt lctx (β getLocalInstances) (β mvarId.getType) .syntheticOpaque (β mvarId.getTag)
|
| 59 |
+
mvarId.assign mvarNew
|
| 60 |
+
return mvarNew.mvarId!
|
| 61 |
+
|
| 62 |
+
/-- Creates a temporary local context where all names are exposed, and executes `k` -/
|
| 63 |
+
def withExposedNames (k : MetaM Ξ±) : MetaM Ξ± := do
|
| 64 |
+
withNewMCtxDepth <| withLCtx (β getLCtxWithExposedNames) (β getLocalInstances) k
|
| 65 |
+
|
| 66 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Ext.lean
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Gabriel Ebner. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Gabriel Ebner, Mario Carneiro
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Data.Array.InsertionSort
|
| 8 |
+
import Lean.Meta.DiscrTree
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta.Ext
|
| 11 |
+
|
| 12 |
+
/-!
|
| 13 |
+
### Environment extension for `ext` theorems
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
/-- Information about an extensionality theorem, stored in the environment extension. -/
|
| 17 |
+
structure ExtTheorem where
|
| 18 |
+
/-- Declaration name of the extensionality theorem. -/
|
| 19 |
+
declName : Name
|
| 20 |
+
/-- Priority of the extensionality theorem. -/
|
| 21 |
+
priority : Nat
|
| 22 |
+
/--
|
| 23 |
+
Key in the discrimination tree,
|
| 24 |
+
for the type in which the extensionality theorem holds.
|
| 25 |
+
-/
|
| 26 |
+
keys : Array DiscrTree.Key
|
| 27 |
+
deriving Inhabited, Repr, BEq, Hashable
|
| 28 |
+
|
| 29 |
+
/-- The state of the `ext` extension environment -/
|
| 30 |
+
structure ExtTheorems where
|
| 31 |
+
/-- The tree of `ext` extensions. -/
|
| 32 |
+
tree : DiscrTree ExtTheorem := {}
|
| 33 |
+
/-- Erased `ext`s via `attribute [-ext]`. -/
|
| 34 |
+
erased : PHashSet Name := {}
|
| 35 |
+
deriving Inhabited
|
| 36 |
+
|
| 37 |
+
/-- The environment extension to track `@[ext]` theorems. -/
|
| 38 |
+
builtin_initialize extExtension :
|
| 39 |
+
SimpleScopedEnvExtension ExtTheorem ExtTheorems β
|
| 40 |
+
registerSimpleScopedEnvExtension {
|
| 41 |
+
addEntry := fun { tree, erased } thm =>
|
| 42 |
+
{ tree := tree.insertCore thm.keys thm, erased := erased.erase thm.declName }
|
| 43 |
+
initial := {}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
/-- Gets the list of `@[ext]` theorems corresponding to the key `ty`,
|
| 47 |
+
ordered from high priority to low. -/
|
| 48 |
+
@[inline] def getExtTheorems (ty : Expr) : MetaM (Array ExtTheorem) := do
|
| 49 |
+
let extTheorems := extExtension.getState (β getEnv)
|
| 50 |
+
let arr β extTheorems.tree.getMatch ty
|
| 51 |
+
let erasedArr := arr.filter fun thm => !extTheorems.erased.contains thm.declName
|
| 52 |
+
-- Using insertion sort because it is stable and the list of matches should be mostly sorted.
|
| 53 |
+
-- Most ext theorems have default priority.
|
| 54 |
+
return erasedArr.insertionSort (Β·.priority < Β·.priority) |>.reverse
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
Erases a name marked `ext` by adding it to the state's `erased` field and
|
| 58 |
+
removing it from the state's list of `Entry`s.
|
| 59 |
+
|
| 60 |
+
This is triggered by `attribute [-ext] name`.
|
| 61 |
+
-/
|
| 62 |
+
def ExtTheorems.eraseCore (d : ExtTheorems) (declName : Name) : ExtTheorems :=
|
| 63 |
+
{ d with erased := d.erased.insert declName }
|
| 64 |
+
|
| 65 |
+
/-- Returns `true` if `d` contains theorem with name `declName`. -/
|
| 66 |
+
def ExtTheorems.contains (d : ExtTheorems) (declName : Name) : Bool :=
|
| 67 |
+
d.tree.containsValueP (Β·.declName == declName) && !d.erased.contains declName
|
| 68 |
+
|
| 69 |
+
/-- Returns `true` if `declName` is tagged with `[ext]` attribute. -/
|
| 70 |
+
def isExtTheorem (declName : Name) : CoreM Bool := do
|
| 71 |
+
let extTheorems := extExtension.getState (β getEnv)
|
| 72 |
+
return extTheorems.contains declName
|
| 73 |
+
|
| 74 |
+
/--
|
| 75 |
+
Erases a name marked as a `ext` attribute.
|
| 76 |
+
Check that it does in fact have the `ext` attribute by making sure it names a `ExtTheorem`
|
| 77 |
+
found somewhere in the state's tree, and is not erased.
|
| 78 |
+
-/
|
| 79 |
+
def ExtTheorems.erase [Monad m] [MonadError m] (d : ExtTheorems) (declName : Name) :
|
| 80 |
+
m ExtTheorems := do
|
| 81 |
+
unless d.contains declName do
|
| 82 |
+
throwError "'{declName}' does not have [ext] attribute"
|
| 83 |
+
return d.eraseCore declName
|
| 84 |
+
|
| 85 |
+
end Lean.Meta.Ext
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FVarSubst.lean
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Mario Carneiro
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.AssocList
|
| 8 |
+
import Lean.Expr
|
| 9 |
+
import Lean.LocalContext
|
| 10 |
+
import Lean.Util.ReplaceExpr
|
| 11 |
+
|
| 12 |
+
namespace Lean.Meta
|
| 13 |
+
/--
|
| 14 |
+
Some tactics substitute hypotheses with expressions.
|
| 15 |
+
We track these substitutions using `FVarSubst`.
|
| 16 |
+
It is just a mapping from the original FVarId (internal) name
|
| 17 |
+
to an expression. The free variables occurring in the expression must
|
| 18 |
+
be defined in the new goal. -/
|
| 19 |
+
structure FVarSubst where
|
| 20 |
+
map : AssocList FVarId Expr := {}
|
| 21 |
+
deriving Inhabited
|
| 22 |
+
|
| 23 |
+
namespace FVarSubst
|
| 24 |
+
|
| 25 |
+
def empty : FVarSubst := {}
|
| 26 |
+
|
| 27 |
+
def isEmpty (s : FVarSubst) : Bool :=
|
| 28 |
+
s.map.isEmpty
|
| 29 |
+
|
| 30 |
+
def contains (s : FVarSubst) (fvarId : FVarId) : Bool :=
|
| 31 |
+
s.map.contains fvarId
|
| 32 |
+
|
| 33 |
+
/-- Add entry `fvarId |-> v` to `s` if `s` does not contain an entry for `fvarId`. -/
|
| 34 |
+
def insert (s : FVarSubst) (fvarId : FVarId) (v : Expr) : FVarSubst :=
|
| 35 |
+
if s.contains fvarId then s
|
| 36 |
+
else
|
| 37 |
+
let map := s.map.mapVal fun e => e.replaceFVarId fvarId v;
|
| 38 |
+
{ map := map.insertNew fvarId v }
|
| 39 |
+
|
| 40 |
+
def erase (s : FVarSubst) (fvarId : FVarId) : FVarSubst :=
|
| 41 |
+
{ map := s.map.erase fvarId }
|
| 42 |
+
|
| 43 |
+
def find? (s : FVarSubst) (fvarId : FVarId) : Option Expr :=
|
| 44 |
+
s.map.find? fvarId
|
| 45 |
+
|
| 46 |
+
def get (s : FVarSubst) (fvarId : FVarId) : Expr :=
|
| 47 |
+
match s.map.find? fvarId with
|
| 48 |
+
| none => mkFVar fvarId -- it has not been replaced
|
| 49 |
+
| some v => v
|
| 50 |
+
|
| 51 |
+
/-- Given `e`, for each `(x => v)` in `s` replace `x` with `v` in `e` -/
|
| 52 |
+
def apply (s : FVarSubst) (e : Expr) : Expr :=
|
| 53 |
+
if s.map.isEmpty then e
|
| 54 |
+
else if !e.hasFVar then e
|
| 55 |
+
else e.replace fun e => match e with
|
| 56 |
+
| Expr.fvar fvarId => match s.map.find? fvarId with
|
| 57 |
+
| none => e
|
| 58 |
+
| some v => v
|
| 59 |
+
| _ => none
|
| 60 |
+
|
| 61 |
+
def domain (s : FVarSubst) : List FVarId :=
|
| 62 |
+
s.map.foldl (init := []) fun r k _ => k :: r
|
| 63 |
+
|
| 64 |
+
def any (p : FVarId β Expr β Bool) (s : FVarSubst) : Bool :=
|
| 65 |
+
s.map.any p
|
| 66 |
+
|
| 67 |
+
/--
|
| 68 |
+
Constructs a substitution consisting of `s` followed by `t`.
|
| 69 |
+
This satisfies `(s.append t).apply e = t.apply (s.apply e)`
|
| 70 |
+
-/
|
| 71 |
+
def append (s t : FVarSubst) : FVarSubst :=
|
| 72 |
+
s.1.foldl (fun s' k v => s'.insert k (t.apply v)) t
|
| 73 |
+
|
| 74 |
+
end FVarSubst
|
| 75 |
+
end Meta
|
| 76 |
+
|
| 77 |
+
def LocalDecl.applyFVarSubst (s : Meta.FVarSubst) : LocalDecl β LocalDecl
|
| 78 |
+
| LocalDecl.cdecl i id n t bi k => LocalDecl.cdecl i id n (s.apply t) bi k
|
| 79 |
+
| LocalDecl.ldecl i id n t v nd k => LocalDecl.ldecl i id n (s.apply t) (s.apply v) nd k
|
| 80 |
+
|
| 81 |
+
abbrev Expr.applyFVarSubst (s : Meta.FVarSubst) (e : Expr) : Expr :=
|
| 82 |
+
s.apply e
|
| 83 |
+
|
| 84 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunInd.lean
ADDED
|
@@ -0,0 +1,1715 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Joachim Breitner
|
| 5 |
+
-/
|
| 6 |
+
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Meta.Basic
|
| 9 |
+
import Lean.Meta.Match.MatcherApp.Transform
|
| 10 |
+
import Lean.Meta.Check
|
| 11 |
+
import Lean.Meta.Tactic.Subst
|
| 12 |
+
import Lean.Meta.Injective -- for elimOptParam
|
| 13 |
+
import Lean.Meta.ArgsPacker
|
| 14 |
+
import Lean.Meta.PProdN
|
| 15 |
+
import Lean.Elab.PreDefinition.WF.Eqns
|
| 16 |
+
import Lean.Elab.PreDefinition.Structural.Eqns
|
| 17 |
+
import Lean.Elab.PreDefinition.Structural.IndGroupInfo
|
| 18 |
+
import Lean.Elab.PreDefinition.Structural.FindRecArg
|
| 19 |
+
import Lean.Elab.Command
|
| 20 |
+
import Lean.Meta.Tactic.ElimInfo
|
| 21 |
+
import Lean.Meta.Tactic.FunIndInfo
|
| 22 |
+
|
| 23 |
+
/-!
|
| 24 |
+
This module contains code to derive, from the definition of a recursive function (structural or
|
| 25 |
+
well-founded, possibly mutual), a **functional induction principle** tailored to proofs about that
|
| 26 |
+
function(s).
|
| 27 |
+
|
| 28 |
+
For example from:
|
| 29 |
+
```
|
| 30 |
+
def ackermann : Nat β Nat β Nat
|
| 31 |
+
| 0, m => m + 1
|
| 32 |
+
| n+1, 0 => ackermann n 1
|
| 33 |
+
| n+1, m+1 => ackermann n (ackermann (n + 1) m)
|
| 34 |
+
```
|
| 35 |
+
we get
|
| 36 |
+
```
|
| 37 |
+
ackermann.induct (motive : Nat β Nat β Prop) (case1 : β (m : Nat), motive 0 m)
|
| 38 |
+
(case2 : β (n : Nat), motive n 1 β motive (Nat.succ n) 0)
|
| 39 |
+
(case3 : β (n m : Nat), motive (n + 1) m β motive n (ackermann (n + 1) m) β motive (Nat.succ n) (Nat.succ m))
|
| 40 |
+
(x x : Nat) : motive x x
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
## Specification
|
| 44 |
+
|
| 45 |
+
The functional induction principle takes the same fixed parameters as the function, and
|
| 46 |
+
the motive takes the same non-fixed parameters as the original function.
|
| 47 |
+
|
| 48 |
+
For each branch of the original function, there is a case in the induction principle.
|
| 49 |
+
Here "branch" roughly corresponds to tail-call positions: branches of top-level
|
| 50 |
+
`if`-`then`-`else` and of `match` expressions.
|
| 51 |
+
|
| 52 |
+
For every recursive call in that branch, an induction hypothesis asserting the
|
| 53 |
+
motive for the arguments of the recursive call is provided.
|
| 54 |
+
If the recursive call is under binders and it, or its proof of termination,
|
| 55 |
+
depend on the bound values, then these become assumptions of the inductive
|
| 56 |
+
hypothesis.
|
| 57 |
+
|
| 58 |
+
Additionally, the local context of the branch (e.g. the condition of an
|
| 59 |
+
if-then-else; a let-binding, a have-binding) is provided as assumptions in the
|
| 60 |
+
corresponding induction case, if they are likely to be useful (as determined
|
| 61 |
+
by `MVarId.cleanup`).
|
| 62 |
+
|
| 63 |
+
Mutual recursion is supported and results in multiple motives.
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
## Implementation overview (well-founded recursion)
|
| 67 |
+
|
| 68 |
+
For a non-mutual, unary function `foo` (or else for the `_unary` function), we
|
| 69 |
+
|
| 70 |
+
1. expect its definition to be of the form
|
| 71 |
+
```
|
| 72 |
+
def foo := fun xβ β¦ xβ (y : a) => WellFounded.fix (fun y' oldIH => body) y
|
| 73 |
+
```
|
| 74 |
+
where `xα΅’β¦` are the fixed parameter prefix and `y` is the varying parameter of
|
| 75 |
+
the function.
|
| 76 |
+
|
| 77 |
+
2. From this structure we derive the type of the motive, and start assembling the induction
|
| 78 |
+
principle:
|
| 79 |
+
```
|
| 80 |
+
def foo.induct := fun xβ β¦ xβ (motive : (y : a) β Prop) =>
|
| 81 |
+
fix (fun y' newIH => T[body])
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
3. The first phase, transformation `T1[body]` (implemented in `buildInductionBody`)
|
| 85 |
+
mirrors the branching structure of `foo`, i.e. replicates `dite` and some matcher applications,
|
| 86 |
+
while adjusting their motive. It also unfolds calls to `oldIH` and collects induction hypotheses
|
| 87 |
+
in conditions (see below).
|
| 88 |
+
|
| 89 |
+
In particular, when translating a `match` it is prepared to recognize the idiom
|
| 90 |
+
as introduced by `mkFix` via `Lean.Meta.MatcherApp.addArg?`, which refines the type of `oldIH`
|
| 91 |
+
throughout the match. The transformation will replace `oldIH` with `newIH` here.
|
| 92 |
+
```
|
| 93 |
+
T[(match (motive := fun oldIH => β¦) y with | β¦ => fun oldIH' => body) oldIH]
|
| 94 |
+
==> (match (motive := fun newIH => β¦) y with | β¦ => fun newIH' => T[body]) newIH
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
In addition, the information gathered from the match is preserved, so that when performing the
|
| 98 |
+
proof by induction, the user can reliably enter the right case. To achieve this
|
| 99 |
+
|
| 100 |
+
* the matcher is replaced by its splitter, which brings extra assumptions into scope when
|
| 101 |
+
patterns are overlapping (using `matcherApp.transform (useSplitter := true)`)
|
| 102 |
+
* simple discriminants that are mentioned in the goal (i.e plain parameters) are instantiated
|
| 103 |
+
in the goal.
|
| 104 |
+
* for discriminants that are not instantiated that way, equalities connecting the discriminant
|
| 105 |
+
to the instantiation are added (just as if the user wrote `match h : x with β¦`)
|
| 106 |
+
* also, simple discriminants (`FVars`) are remembered as `toClear`, as they are unlikely to
|
| 107 |
+
provide useful context, and are redundant given the context that comes from the pattern match.
|
| 108 |
+
|
| 109 |
+
4. When a tail position (no more branching) is found, function `buildInductionCase` assembles the
|
| 110 |
+
type of the case: a fresh `MVar` asserts the current goal, unwanted values from the local context
|
| 111 |
+
are cleared, and the current `body` is searched for recursive calls using `foldAndCollect`,
|
| 112 |
+
which are then asserted as inductive hyptheses in the `MVar`.
|
| 113 |
+
|
| 114 |
+
5. The function `foldAndCollect` walks the term and performs two operations:
|
| 115 |
+
|
| 116 |
+
* collects the induction hypotheses for the current case (with proofs).
|
| 117 |
+
* recovering the recursive calls
|
| 118 |
+
|
| 119 |
+
So when it encounters a saturated application of `oldIH arg proof`, it
|
| 120 |
+
* returns `f arg` and
|
| 121 |
+
* remembers the expression `newIH arg proof : motive x` as an inductive hypothesis.
|
| 122 |
+
|
| 123 |
+
Since `arg` and `proof` can contain further recursive calls, they are folded there as well.
|
| 124 |
+
This assumes that the termination proof `proof` works nevertheless.
|
| 125 |
+
|
| 126 |
+
Again, `foldAndCollect` may encounter the `Lean.Meta.Matcherapp.addArg?` idiom, and again it
|
| 127 |
+
threads `newIH` through, replacing the extra argument. The resulting type of this induction
|
| 128 |
+
hypothesis is now itself a `match` statement (cf. `Lean.Meta.MatcherApp.inferMatchType`)
|
| 129 |
+
|
| 130 |
+
The termination proof of `foo` may have abstracted over some proofs; these proofs must be
|
| 131 |
+
transferred, so auxiliary lemmas are unfolded if needed.
|
| 132 |
+
|
| 133 |
+
7. After this construction, the MVars introduced by `buildInductionCase` are turned into parameters.
|
| 134 |
+
|
| 135 |
+
The resulting term then becomes `foo.induct` at its inferred type.
|
| 136 |
+
|
| 137 |
+
## Implementation overview (mutual/non-unary well-founded recursion)
|
| 138 |
+
|
| 139 |
+
If `foo` is not unary and/or part of a mutual reduction, then the induction theorem for `foo._unary`
|
| 140 |
+
(i.e. the unary non-mutual recursion function produced by the equation compiler)
|
| 141 |
+
of the form
|
| 142 |
+
```
|
| 143 |
+
foo._unary.induct : {motive : (a β' b) β' c β Prop} β
|
| 144 |
+
(case1 : β β¦, motive (PSum.inl (x,y)) β β¦) β β¦ β
|
| 145 |
+
(x : (a β' b) β' c) β motive x
|
| 146 |
+
```
|
| 147 |
+
will first in `unpackMutualInduction` be turned into a joint induction theorem of the form
|
| 148 |
+
```
|
| 149 |
+
foo.mutual_induct : {motive1 : a β b β Prop} {motive2 : c β Prop} β
|
| 150 |
+
(case1 : β β¦, motive1 x y β β¦) β β¦ β
|
| 151 |
+
((x : a) β (y : b) β motive1 x y) β§ ((z : c) β motive2 z)
|
| 152 |
+
```
|
| 153 |
+
where all the `PSum`/`PSigma` encoding has been resolved. This theorem is attached to the
|
| 154 |
+
name of the first function in the mutual group, like the `._unary` definition.
|
| 155 |
+
|
| 156 |
+
Finally, in `deriveUnpackedInduction`, for each of the functions in the mutual group, a simple
|
| 157 |
+
projection yields the final `foo.induct` theorem:
|
| 158 |
+
```
|
| 159 |
+
foo.induct : {motive1 : a β b β Prop} {motive2 : c β Prop} β
|
| 160 |
+
(case1 : β β¦, motive1 x y β β¦) β β¦ β
|
| 161 |
+
(x : a) β (y : b) β motive1 x y
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
## Implementation overview (structural recursion)
|
| 165 |
+
|
| 166 |
+
When handling structural recursion, the overall approach is the same, with the following
|
| 167 |
+
differences:
|
| 168 |
+
|
| 169 |
+
* Instead of `WellFounded.fix` we look for a `.brecOn` application, using `isBRecOnRecursor`
|
| 170 |
+
|
| 171 |
+
Despite its name, this function does *not* recognize the `.brecOn` of inductive *predicates*,
|
| 172 |
+
which we also do not support at this point.
|
| 173 |
+
|
| 174 |
+
* The elaboration of structurally recursive function can handle extra arguments. We keep the
|
| 175 |
+
`motive` parameters in the original order.
|
| 176 |
+
|
| 177 |
+
## Unfolding principles
|
| 178 |
+
|
| 179 |
+
The code can also create a variant of the induction/cases principles that automatically unfolds
|
| 180 |
+
the function application. It's motive abstracts over the function call, so for the ackermann
|
| 181 |
+
function one gets
|
| 182 |
+
|
| 183 |
+
```
|
| 184 |
+
ackermann.fun_cases_unfolding
|
| 185 |
+
(motive : Nat β Nat β Nat β Prop)
|
| 186 |
+
(case1 : β (m : Nat), motive 0 m (m + 1))
|
| 187 |
+
(case2 : β (n : Nat), motive n.succ 0 (ackermann n 1))
|
| 188 |
+
(case3 : β (n m : Nat), motive n.succ m.succ (ackermann n (ackermann (n + 1) m)))
|
| 189 |
+
(xβ xβΒΉ : Nat) : motive xβ xβΒΉ (ackermann xβ xβΒΉ)
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
To implement this, in the initial goal `motive x (ackermann x)` of `buildInductionBody` we unfold the
|
| 193 |
+
function definition, and then reduce is as we go into match, ite or let expressions, using the
|
| 194 |
+
`withRewrittenMotive` function.
|
| 195 |
+
|
| 196 |
+
This gives us great control over the reduction, for example to move `let` expressions to the context
|
| 197 |
+
simultaneously.
|
| 198 |
+
|
| 199 |
+
The combinators passed to `withRewrittenMotive` are forgiving, so when `unfolding := false`, or when
|
| 200 |
+
something goes wrong, one still gets a useful induction principle, just maybe with the function
|
| 201 |
+
not fully simplified.
|
| 202 |
+
-/
|
| 203 |
+
|
| 204 |
+
namespace Lean.Tactic.FunInd
|
| 205 |
+
|
| 206 |
+
open Lean Elab Meta
|
| 207 |
+
|
| 208 |
+
def lambdaTelescope1 {n} [MonadControlT MetaM n] [MonadError n] [MonadNameGenerator n] [Monad n] {Ξ±} (e : Expr) (k : FVarId β Expr β n Ξ±) : n Ξ± := do
|
| 209 |
+
lambdaBoundedTelescope e 1 fun xs body => do
|
| 210 |
+
unless xs.size == 1 do
|
| 211 |
+
throwError "lambdaTelescope1: expected lambda, got {e}"
|
| 212 |
+
k xs[0]!.fvarId! body
|
| 213 |
+
|
| 214 |
+
/-- There are multiple variants of this function around in the code base, maybe unify at some point. -/
|
| 215 |
+
private def elimTypeAnnotations (type : Expr) : CoreM Expr := do
|
| 216 |
+
Core.transform type fun e =>
|
| 217 |
+
if e.isOptParam || e.isAutoParam then
|
| 218 |
+
return .visit e.appFn!.appArg!
|
| 219 |
+
else if e.isOutParam || e.isSemiOutParam then
|
| 220 |
+
return .visit e.appArg!
|
| 221 |
+
else
|
| 222 |
+
return .continue
|
| 223 |
+
|
| 224 |
+
/--
|
| 225 |
+
A monad to help collecting inductive hypothesis.
|
| 226 |
+
|
| 227 |
+
In `foldAndCollect` it's a writer monad (with variants of the `local` combinator),
|
| 228 |
+
and in `buildInductionBody` it is more of a reader monad, with inductive hypotheses
|
| 229 |
+
being passed down (hence the `ask` and `branch` combinator).
|
| 230 |
+
-/
|
| 231 |
+
abbrev M := StateT (Array Expr) MetaM
|
| 232 |
+
|
| 233 |
+
namespace M
|
| 234 |
+
|
| 235 |
+
variable {Ξ± : Type}
|
| 236 |
+
|
| 237 |
+
def run (act : M Ξ±) : MetaM (Ξ± Γ Array Expr) := StateT.run act #[]
|
| 238 |
+
def eval (act : M Ξ±) : MetaM Ξ± := do return (β run act).1
|
| 239 |
+
def exec (act : M Ξ±) : MetaM (Array Expr) := do return (β run act).2
|
| 240 |
+
|
| 241 |
+
def tell (x : Expr) : M Unit := fun xs => pure ((), xs.push x)
|
| 242 |
+
|
| 243 |
+
def localM (f : Array Expr β MetaM (Array Expr)) (act : M Ξ±) : M Ξ± := fun xs => do
|
| 244 |
+
let n := xs.size
|
| 245 |
+
let (b, xs') β act xs
|
| 246 |
+
pure (b, xs'[*...n] ++ (β f xs'[n...*]))
|
| 247 |
+
|
| 248 |
+
def localMapM (f : Expr β MetaM Expr) (act : M Ξ±) : M Ξ± :=
|
| 249 |
+
localM (Β·.mapM f) act
|
| 250 |
+
|
| 251 |
+
def ask : M (Array Expr) := get
|
| 252 |
+
|
| 253 |
+
def branch (act : M Ξ±) : M Ξ± :=
|
| 254 |
+
localM (fun _ => pure #[]) act
|
| 255 |
+
|
| 256 |
+
end M
|
| 257 |
+
|
| 258 |
+
/--
|
| 259 |
+
The `foldAndCollect` function performs two operations together:
|
| 260 |
+
|
| 261 |
+
* it fold recursive calls: applications (and projectsions) of `oldIH` in `e` correspond to
|
| 262 |
+
recursive calls, so this function rewrites that back to recursive calls
|
| 263 |
+
* it collects induction hypotheses: after replacing `oldIH` with `newIH`, applications thereof
|
| 264 |
+
are valuable as induction hypotheses for the cases.
|
| 265 |
+
|
| 266 |
+
For well-founded recursion (unary, non-mutual by construction) the terms are rather simple: they
|
| 267 |
+
are `oldIH arg proof`, and can be rewritten to `f arg` resp. `newIH arg proof`. But for
|
| 268 |
+
structural recursion this can be a more complicted mix of function applications (due to reflexive
|
| 269 |
+
data types or extra function arguments) and `PProd` projections (due to the below construction and
|
| 270 |
+
mutual function packing), and the main function argument isn't even present.
|
| 271 |
+
|
| 272 |
+
To avoid having to think about this, we apply a nice trick:
|
| 273 |
+
|
| 274 |
+
We compositionally replace `oldIH` with `newIH`. This likely changes the result type, so when
|
| 275 |
+
re-assembling we have to be supple (mainly around `PProd.fst`/`PProd.snd`). As we re-assemble
|
| 276 |
+
the term we check if it has type `motive xs..`. If it has, then know we have just found and
|
| 277 |
+
rewritten a recursive call, and this type nicely provides us the arguments `xs`. So at this point
|
| 278 |
+
we store the rewritten expression as a new induction hypothesis (using `M.tell`) and rewrite to
|
| 279 |
+
`f xs..`, which now again has the same type as the original term, and the further re-assembly should
|
| 280 |
+
work. Half this logic is in the `isRecCall` parameter.
|
| 281 |
+
|
| 282 |
+
If this process fails weβll get weird type errors (caught later on). We'll see if we need to
|
| 283 |
+
improve the errors, for example by passing down a flag whether we expect the same type (and no
|
| 284 |
+
occurrences of `newIH`), or whether we are in βsupple modeβ, and catch it earlier if the rewriting
|
| 285 |
+
fails.
|
| 286 |
+
-/
|
| 287 |
+
partial def foldAndCollect (oldIH newIH : FVarId) (isRecCall : Expr β Option Expr) (e : Expr) : M Expr := do
|
| 288 |
+
unless e.containsFVar oldIH do
|
| 289 |
+
return e
|
| 290 |
+
withTraceNode `Meta.FunInd (pure m!"{exceptEmoji Β·} foldAndCollect ({mkFVar oldIH} β {mkFVar newIH})::{indentExpr e}") do
|
| 291 |
+
|
| 292 |
+
let e' β id do
|
| 293 |
+
if let some matcherApp β matchMatcherApp? e (alsoCasesOn := true) then
|
| 294 |
+
if matcherApp.remaining.size == 1 && matcherApp.remaining[0]!.isFVarOf oldIH then
|
| 295 |
+
-- We do different things to the matcher when folding recursive calls and when
|
| 296 |
+
-- collecting inductive hypotheses. Therefore we do it separately,
|
| 297 |
+
-- dropping to `MetaM` in between, and using `M.eval`/`M.exec` as appropriate
|
| 298 |
+
-- We could try to do it in one pass by breaking up the `matcherApp.transform`
|
| 299 |
+
-- abstraction.
|
| 300 |
+
|
| 301 |
+
-- To collect the IHs, we collect them in each branch, and combine
|
| 302 |
+
-- them to a type-leve match
|
| 303 |
+
let ihMatcherApp' β liftM <| matcherApp.transform
|
| 304 |
+
(onParams := fun e => M.eval <| foldAndCollect oldIH newIH isRecCall e)
|
| 305 |
+
(onMotive := fun xs _body => do
|
| 306 |
+
-- Remove the old IH that was added in mkFix
|
| 307 |
+
let eType β newIH.getType
|
| 308 |
+
let eTypeAbst β matcherApp.discrs.size.foldRevM (init := eType) fun i _ eTypeAbst => do
|
| 309 |
+
let motiveArg := xs[i]!
|
| 310 |
+
let discr := matcherApp.discrs[i]
|
| 311 |
+
let eTypeAbst β kabstract eTypeAbst discr
|
| 312 |
+
return eTypeAbst.instantiate1 motiveArg
|
| 313 |
+
|
| 314 |
+
-- Will later be overridden with a type thatβs itself a match
|
| 315 |
+
-- statement and the inferred alt types
|
| 316 |
+
let dummyGoal := mkConst ``True []
|
| 317 |
+
mkArrow eTypeAbst dummyGoal)
|
| 318 |
+
(onAlt := fun _altIdx altType alt => do
|
| 319 |
+
lambdaTelescope1 alt fun oldIH' alt => do
|
| 320 |
+
forallBoundedTelescope altType (some 1) fun newIH' _goal' => do
|
| 321 |
+
let #[newIH'] := newIH' | unreachable!
|
| 322 |
+
let altIHs β M.exec <| foldAndCollect oldIH' newIH'.fvarId! isRecCall alt
|
| 323 |
+
let altIH β PProdN.mk 0 altIHs
|
| 324 |
+
mkLambdaFVars #[newIH'] altIH)
|
| 325 |
+
(onRemaining := fun _ => pure #[mkFVar newIH])
|
| 326 |
+
let ihMatcherApp'' β ihMatcherApp'.inferMatchType
|
| 327 |
+
M.tell ihMatcherApp''.toExpr
|
| 328 |
+
|
| 329 |
+
-- When folding the calls we don't want to remove the extra arg to the matcher
|
| 330 |
+
-- that was introduced in the translation
|
| 331 |
+
let matcherApp' β liftM <| matcherApp.transform
|
| 332 |
+
(onParams := fun e => M.eval <| foldAndCollect oldIH newIH isRecCall e)
|
| 333 |
+
(onMotive := fun _motiveArgs motiveBody => do
|
| 334 |
+
let some (_extra, body) := motiveBody.arrow? | throwError "motive not an arrow"
|
| 335 |
+
M.eval (foldAndCollect oldIH newIH isRecCall body))
|
| 336 |
+
(onAlt := fun _altIdx altType alt => do
|
| 337 |
+
lambdaTelescope1 alt fun oldIH' alt => do
|
| 338 |
+
-- We don't have suitable newIH around here, but we don't care since
|
| 339 |
+
-- we just want to fold calls. So lets create a fake one.
|
| 340 |
+
-- (We cannot use oldIH as that would run into the sanity checks that we could
|
| 341 |
+
-- replace all of them)
|
| 342 |
+
withLocalDeclD `fakeNewIH (β inferType (mkFVar oldIH')) fun fakeNewIH =>
|
| 343 |
+
M.eval (foldAndCollect oldIH' fakeNewIH.fvarId! isRecCall alt))
|
| 344 |
+
(onRemaining := fun _ => pure #[])
|
| 345 |
+
return matcherApp'.toExpr
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
if e.getAppArgs.any (Β·.isFVarOf oldIH) then
|
| 349 |
+
-- Sometimes Fix.lean abstracts over oldIH in a proof definition.
|
| 350 |
+
-- So beta-reduce that definition. We need to look through theorems here!
|
| 351 |
+
if let some e' β withTransparency .all do unfoldDefinition? e then
|
| 352 |
+
return β foldAndCollect oldIH newIH isRecCall e'
|
| 353 |
+
else
|
| 354 |
+
throwError "foldAndCollect: cannot reduce application of {e.getAppFn} in:{indentExpr e} "
|
| 355 |
+
|
| 356 |
+
match e with
|
| 357 |
+
| .app e1 e2 =>
|
| 358 |
+
pure <|.app (β foldAndCollect oldIH newIH isRecCall e1) (β foldAndCollect oldIH newIH isRecCall e2)
|
| 359 |
+
|
| 360 |
+
| .lam n t body bi =>
|
| 361 |
+
let t' β foldAndCollect oldIH newIH isRecCall t
|
| 362 |
+
withLocalDecl n bi t' fun x => do
|
| 363 |
+
M.localMapM (mkLambdaFVars (usedOnly := true) #[x] Β·) do
|
| 364 |
+
let body' β foldAndCollect oldIH newIH isRecCall (body.instantiate1 x)
|
| 365 |
+
mkLambdaFVars #[x] body'
|
| 366 |
+
|
| 367 |
+
| .forallE n t body bi =>
|
| 368 |
+
let t' β foldAndCollect oldIH newIH isRecCall t
|
| 369 |
+
withLocalDecl n bi t' fun x => do
|
| 370 |
+
M.localMapM (mkLambdaFVars (usedOnly := true) #[x] Β·) do
|
| 371 |
+
let body' β foldAndCollect oldIH newIH isRecCall (body.instantiate1 x)
|
| 372 |
+
mkForallFVars #[x] body'
|
| 373 |
+
|
| 374 |
+
| .letE n t v b nondep =>
|
| 375 |
+
let t' β foldAndCollect oldIH newIH isRecCall t
|
| 376 |
+
let v' β foldAndCollect oldIH newIH isRecCall v
|
| 377 |
+
withLetDecl n t' v' (nondep := nondep) fun x => do
|
| 378 |
+
M.localMapM (mkLetFVars (usedLetOnly := true) (generalizeNondepLet := false) #[x] Β·) do
|
| 379 |
+
let b' β foldAndCollect oldIH newIH isRecCall (b.instantiate1 x)
|
| 380 |
+
mkLetFVars (generalizeNondepLet := false) #[x] b'
|
| 381 |
+
|
| 382 |
+
| .mdata m b =>
|
| 383 |
+
pure <| .mdata m (β foldAndCollect oldIH newIH isRecCall b)
|
| 384 |
+
|
| 385 |
+
-- These projections can be type changing (to And), so re-infer their type arguments
|
| 386 |
+
| .proj ``PProd 0 e => mkPProdFstM (β foldAndCollect oldIH newIH isRecCall e)
|
| 387 |
+
| .proj ``PProd 1 e => mkPProdSndM (β foldAndCollect oldIH newIH isRecCall e)
|
| 388 |
+
|
| 389 |
+
| .proj t i e =>
|
| 390 |
+
pure <| .proj t i (β foldAndCollect oldIH newIH isRecCall e)
|
| 391 |
+
|
| 392 |
+
| .sort .. | .lit .. | .const .. | .mvar .. | .bvar .. =>
|
| 393 |
+
unreachable! -- cannot contain free variables, so early exit above kicks in
|
| 394 |
+
|
| 395 |
+
| .fvar fvar =>
|
| 396 |
+
assert! fvar == oldIH
|
| 397 |
+
pure <| mkFVar newIH
|
| 398 |
+
|
| 399 |
+
-- Now see if the type of the expression we are building is a motive application.
|
| 400 |
+
-- If it is we want to replace it with the corresponding function application,
|
| 401 |
+
-- and remember the expression as a IH to be used in an inductive case.
|
| 402 |
+
|
| 403 |
+
if e'.containsFVar oldIH then
|
| 404 |
+
throwError "Failed to eliminate {mkFVar oldIH} from:{indentExpr e'}"
|
| 405 |
+
|
| 406 |
+
let eType β whnf (β inferType e')
|
| 407 |
+
if let .some call := isRecCall eType then
|
| 408 |
+
M.tell (β mkExpectedTypeHint e' eType)
|
| 409 |
+
return call
|
| 410 |
+
|
| 411 |
+
return e'
|
| 412 |
+
|
| 413 |
+
-- Because of term duplications we might encounter the same IH multiple times.
|
| 414 |
+
-- We deduplicate them (by type, not proof term) here.
|
| 415 |
+
-- This could be improved and catch cases where the same IH is used in different contexts.
|
| 416 |
+
-- (Cf. `assignSubsumed` in `WF.Fix`)
|
| 417 |
+
def deduplicateIHs (vals : Array Expr) : MetaM (Array Expr) := do
|
| 418 |
+
let mut vals' := #[]
|
| 419 |
+
let mut types := #[]
|
| 420 |
+
for v in vals do
|
| 421 |
+
let t β inferType v
|
| 422 |
+
unless types.contains t do
|
| 423 |
+
vals' := vals'.push v
|
| 424 |
+
types := types.push t
|
| 425 |
+
return vals'
|
| 426 |
+
|
| 427 |
+
def assertIHs (vals : Array Expr) (mvarid : MVarId) : MetaM MVarId := do
|
| 428 |
+
let mut mvarid := mvarid
|
| 429 |
+
for v in vals.reverse, i in [0:vals.size] do
|
| 430 |
+
mvarid β mvarid.assert (.mkSimple s!"ih{i+1}") (β inferType v) v
|
| 431 |
+
return mvarid
|
| 432 |
+
|
| 433 |
+
/--
|
| 434 |
+
Goal cleanup:
|
| 435 |
+
Substitutes equations (with `substVar`) to remove superfluous variables, and clears unused
|
| 436 |
+
let bindings.
|
| 437 |
+
|
| 438 |
+
Substitutes from the outside in so that the inner-bound variable name wins, but does a first pass
|
| 439 |
+
looking only at variables with names with macro scope, so that preferably they disappear.
|
| 440 |
+
|
| 441 |
+
Careful to only touch the context after the motives (given by the index) as the motive could depend
|
| 442 |
+
on anything before, and `substVar` would happily drop equations about these fixed parameters.
|
| 443 |
+
-/
|
| 444 |
+
partial def cleanupAfter (mvarId : MVarId) (index : Nat) : MetaM MVarId := do
|
| 445 |
+
let mvarId β go mvarId index true
|
| 446 |
+
let mvarId β go mvarId index false
|
| 447 |
+
return mvarId
|
| 448 |
+
where
|
| 449 |
+
go (mvarId : MVarId) (index : Nat) (firstPass : Bool) : MetaM MVarId := do
|
| 450 |
+
if let some mvarId β cleanupAfter? mvarId index firstPass then
|
| 451 |
+
go mvarId index firstPass
|
| 452 |
+
else
|
| 453 |
+
return mvarId
|
| 454 |
+
|
| 455 |
+
allHeqToEq (mvarId : MVarId) (index : Nat) : MetaM MVarId :=
|
| 456 |
+
mvarId.withContext do
|
| 457 |
+
let mut mvarId := mvarId
|
| 458 |
+
for localDecl in (β getLCtx) do
|
| 459 |
+
if localDecl.index > index then
|
| 460 |
+
let (_, mvarId') β heqToEq mvarId localDecl.fvarId
|
| 461 |
+
mvarId := mvarId'
|
| 462 |
+
return mvarId
|
| 463 |
+
|
| 464 |
+
cleanupAfter? (mvarId : MVarId) (index : Nat) (firstPass : Bool) : MetaM (Option MVarId) := do
|
| 465 |
+
mvarId.withContext do
|
| 466 |
+
for localDecl in (β getLCtx) do
|
| 467 |
+
if localDecl.index > index && (!firstPass || localDecl.userName.hasMacroScopes) then
|
| 468 |
+
if localDecl.isLet then
|
| 469 |
+
if β Meta.isProp localDecl.type then
|
| 470 |
+
if let some mvarId' β observing? <| mvarId.clearValue localDecl.fvarId then
|
| 471 |
+
return some mvarId'
|
| 472 |
+
else
|
| 473 |
+
continue
|
| 474 |
+
if let some mvarId' β observing? <| mvarId.clear localDecl.fvarId then
|
| 475 |
+
return some mvarId'
|
| 476 |
+
if let some mvarId' β substVar? mvarId localDecl.fvarId then
|
| 477 |
+
-- After substituting, some HEq might turn into Eqs, and we want to be able to substitute
|
| 478 |
+
-- them as well
|
| 479 |
+
let mvarId' β allHeqToEq mvarId' index
|
| 480 |
+
return some mvarId'
|
| 481 |
+
return none
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
/--
|
| 485 |
+
Second helper monad collecting the cases as mvars
|
| 486 |
+
-/
|
| 487 |
+
abbrev M2 Ξ± := StateT (Array MVarId) M Ξ±
|
| 488 |
+
|
| 489 |
+
def M2.run {Ξ±} (act : M2 Ξ±) : MetaM (Ξ± Γ Array MVarId) :=
|
| 490 |
+
M.eval (StateT.run (s := #[]) act)
|
| 491 |
+
|
| 492 |
+
def M2.branch {Ξ±} (act : M2 Ξ±) : M2 Ξ± :=
|
| 493 |
+
controlAt M fun runInBase => M.branch (runInBase act)
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
/-- Base case of `buildInductionBody`: Construct a case for the final induction hypthesis. -/
|
| 497 |
+
def buildInductionCase (oldIH newIH : FVarId) (isRecCall : Expr β Option Expr) (toErase toClear : Array FVarId)
|
| 498 |
+
(goal : Expr) (e : Expr) : M2 Expr := do
|
| 499 |
+
withTraceNode `Meta.FunInd (pure m!"{exceptEmoji Β·} buildInductionCase:{indentExpr e}") do
|
| 500 |
+
let _e' β foldAndCollect oldIH newIH isRecCall e
|
| 501 |
+
let IHs : Array Expr β M.ask
|
| 502 |
+
let IHs β deduplicateIHs IHs
|
| 503 |
+
|
| 504 |
+
withErasedFVars toErase do
|
| 505 |
+
let mvar β mkFreshExprSyntheticOpaqueMVar goal (tag := `hyp)
|
| 506 |
+
let mvarId := mvar.mvarId!
|
| 507 |
+
let mvarId β assertIHs IHs mvarId
|
| 508 |
+
trace[Meta.FunInd] "Goal before cleanup:{mvarId}"
|
| 509 |
+
let (mvarId, _) β mvarId.tryClearMany' toClear
|
| 510 |
+
modify (Β·.push mvarId)
|
| 511 |
+
let mvar β instantiateMVars mvar
|
| 512 |
+
pure mvar
|
| 513 |
+
|
| 514 |
+
/--
|
| 515 |
+
Like `mkLambdaFVars (usedOnly := true)`, but
|
| 516 |
+
|
| 517 |
+
* silently skips expression in `xs` that are not `.isFVar`
|
| 518 |
+
* returns a mask (same size as `xs`) indicating which variables have been abstracted
|
| 519 |
+
(`true` means was abstracted).
|
| 520 |
+
|
| 521 |
+
The result `r` can be applied with `r.beta (maskArray mask args)`.
|
| 522 |
+
|
| 523 |
+
We use this when generating the functional induction principle to refine the goal through a `match`,
|
| 524 |
+
here `xs` are the discriminants of the `match`.
|
| 525 |
+
We do not expect non-trivial discriminants to appear in the goal (and if they do, the user will
|
| 526 |
+
get a helpful equality into the context).
|
| 527 |
+
-/
|
| 528 |
+
def mkLambdaFVarsMasked (xs : Array Expr) (e : Expr) : MetaM (Array Bool Γ Expr) := do
|
| 529 |
+
let mut e := e
|
| 530 |
+
let mut xs := xs
|
| 531 |
+
let mut mask := #[]
|
| 532 |
+
while ! xs.isEmpty do
|
| 533 |
+
let discr := xs.back!
|
| 534 |
+
if discr.isFVar && e.containsFVar discr.fvarId! then
|
| 535 |
+
e β mkLambdaFVars #[discr] e
|
| 536 |
+
mask := mask.push true
|
| 537 |
+
else
|
| 538 |
+
mask := mask.push false
|
| 539 |
+
xs := xs.pop
|
| 540 |
+
return (mask.reverse, e)
|
| 541 |
+
|
| 542 |
+
/-- `maskArray mask xs` keeps those `x` where the corresponding entry in `mask` is `true` -/
|
| 543 |
+
def maskArray {Ξ±} (mask : Array Bool) (xs : Array Ξ±) : Array Ξ± := Id.run do
|
| 544 |
+
let mut ys := #[]
|
| 545 |
+
for b in mask, x in xs do
|
| 546 |
+
if b then ys := ys.push x
|
| 547 |
+
return ys
|
| 548 |
+
|
| 549 |
+
/--
|
| 550 |
+
Inverse of `maskArray`:
|
| 551 |
+
```
|
| 552 |
+
zipMaskedArray mask (maskArray (mask.map not) xs) (maskArray mask xs) == xs
|
| 553 |
+
```
|
| 554 |
+
-/
|
| 555 |
+
def zipMaskedArray {Ξ±} (mask : Array Bool) (xs ys : Array Ξ±) : Array Ξ± := Id.run do
|
| 556 |
+
let mut i := 0
|
| 557 |
+
let mut j := 0
|
| 558 |
+
let mut zs := #[]
|
| 559 |
+
for b in mask do
|
| 560 |
+
if b then
|
| 561 |
+
if h : j < ys.size then
|
| 562 |
+
zs := zs.push ys[j]
|
| 563 |
+
j := j + 1
|
| 564 |
+
else
|
| 565 |
+
panic! "zipMaskedArray: not enough elements in ys"
|
| 566 |
+
else
|
| 567 |
+
if h : i < xs.size then
|
| 568 |
+
zs := zs.push xs[i]
|
| 569 |
+
i := i + 1
|
| 570 |
+
else
|
| 571 |
+
panic! "zipMaskedArray: not enough elements in xs"
|
| 572 |
+
return zs
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
/--
|
| 576 |
+
Applies `rw` to `goal`, passes the rewritten `goal'` to `k` (which should return an expression of
|
| 577 |
+
type `goal'`), and wraps that using the proof from `rw`.
|
| 578 |
+
-/
|
| 579 |
+
def withRewrittenMotive (goal : Expr) (rw : Expr β MetaM Simp.Result) (k : Expr β M2 Expr) : M2 Expr := do
|
| 580 |
+
let r β rw goal
|
| 581 |
+
let e β k r.expr
|
| 582 |
+
r.mkEqMPR e
|
| 583 |
+
|
| 584 |
+
def inLastArg (rw : Expr β MetaM Simp.Result) (goal : Expr) : MetaM Simp.Result := do
|
| 585 |
+
match goal with
|
| 586 |
+
| .app goalFn arg =>
|
| 587 |
+
let r β rw arg
|
| 588 |
+
Simp.mkCongrArg goalFn r
|
| 589 |
+
| _ =>
|
| 590 |
+
return { expr := goal }
|
| 591 |
+
|
| 592 |
+
/--
|
| 593 |
+
If `goal` is of the form `motive a b e`, applies `rw` to `e`, passes the simplified
|
| 594 |
+
`goal'` to `k` (which should return an expression of type `goal'`), and rewrites that term
|
| 595 |
+
accordingly.
|
| 596 |
+
-/
|
| 597 |
+
def withRewrittenMotiveArg (goal : Expr) (rw : Expr β MetaM Simp.Result) (k : Expr β M2 Expr) : M2 Expr := do
|
| 598 |
+
withRewrittenMotive goal (inLastArg rw) k
|
| 599 |
+
|
| 600 |
+
/--
|
| 601 |
+
Use to write inside the packed motives used for mutual structural recursion.
|
| 602 |
+
-/
|
| 603 |
+
partial def inProdLambdaLastArg (rw : Expr β MetaM Simp.Result) (goal : Expr) : MetaM Simp.Result := do
|
| 604 |
+
match_expr goal with
|
| 605 |
+
| PProd.mk _ _ goal1 goal2 =>
|
| 606 |
+
let r1 β inProdLambdaLastArg rw goal1
|
| 607 |
+
let r2 β inProdLambdaLastArg rw goal2
|
| 608 |
+
let f := goal.appFn!.appFn!
|
| 609 |
+
Simp.mkCongr (β Simp.mkCongrArg f r1) r2
|
| 610 |
+
| _ =>
|
| 611 |
+
lambdaTelescope goal fun xs goal => do
|
| 612 |
+
let r β inLastArg rw goal
|
| 613 |
+
r.addLambdas xs
|
| 614 |
+
|
| 615 |
+
def rwIfWith (hc : Expr) (e : Expr) : MetaM Simp.Result := do
|
| 616 |
+
match_expr e with
|
| 617 |
+
| ite@ite Ξ± c h t f =>
|
| 618 |
+
let us := ite.constLevels!
|
| 619 |
+
if (β isDefEq c (β inferType hc)) then
|
| 620 |
+
return {
|
| 621 |
+
expr := t
|
| 622 |
+
proof? := (mkAppN (mkConst ``if_pos us) #[c, h, hc, Ξ±, t, f])
|
| 623 |
+
}
|
| 624 |
+
if (β isDefEq (mkNot c) (β inferType hc)) then
|
| 625 |
+
return {
|
| 626 |
+
expr := f
|
| 627 |
+
proof? := (mkAppN (mkConst ``if_neg us) #[c, h, hc, Ξ±, t, f])
|
| 628 |
+
}
|
| 629 |
+
return { expr := e}
|
| 630 |
+
| dite@dite Ξ± c h t f =>
|
| 631 |
+
let us := dite.constLevels!
|
| 632 |
+
if (β isDefEq c (β inferType hc)) then
|
| 633 |
+
return {
|
| 634 |
+
expr := t.beta #[hc]
|
| 635 |
+
proof? := (mkAppN (mkConst ``dif_pos us) #[c, h, hc, Ξ±, t, f])
|
| 636 |
+
}
|
| 637 |
+
if (β isDefEq (mkNot c) (β inferType hc)) then
|
| 638 |
+
return {
|
| 639 |
+
expr := f.beta #[hc]
|
| 640 |
+
proof? := (mkAppN (mkConst ``dif_neg us) #[c, h, hc, Ξ±, t, f])
|
| 641 |
+
}
|
| 642 |
+
return { expr := e }
|
| 643 |
+
| cond@cond Ξ± c t f =>
|
| 644 |
+
let us := cond.constLevels!
|
| 645 |
+
if (β isDefEq (β inferType hc) (β mkEq c (mkConst ``Bool.true))) then
|
| 646 |
+
return {
|
| 647 |
+
expr := t
|
| 648 |
+
proof? := (mkAppN (mkConst ``Bool.cond_pos us) #[Ξ±, c, t, f, hc])
|
| 649 |
+
}
|
| 650 |
+
if (β isDefEq (β inferType hc) (β mkEq c (mkConst ``Bool.false))) then
|
| 651 |
+
return {
|
| 652 |
+
expr := f
|
| 653 |
+
proof? := (mkAppN (mkConst ``Bool.cond_neg us) #[Ξ±, c, t, f, hc])
|
| 654 |
+
}
|
| 655 |
+
return { expr := e }
|
| 656 |
+
| _ =>
|
| 657 |
+
return { expr := e }
|
| 658 |
+
|
| 659 |
+
def rwLetWith (h : Expr) (e : Expr) : MetaM Simp.Result := do
|
| 660 |
+
if e.isLet then
|
| 661 |
+
if (β isDefEq e.letValue! h) then
|
| 662 |
+
return { expr := e.letBody!.instantiate1 h }
|
| 663 |
+
return { expr := e }
|
| 664 |
+
|
| 665 |
+
def rwMData (e : Expr) : MetaM Simp.Result := do
|
| 666 |
+
return { expr := e.consumeMData }
|
| 667 |
+
|
| 668 |
+
def rwFun (names : Array Name) (e : Expr) : MetaM Simp.Result := do
|
| 669 |
+
e.withApp fun f xs => do
|
| 670 |
+
if let some name := names.find? f.isConstOf then
|
| 671 |
+
let some unfoldThm β getUnfoldEqnFor? name (nonRec := true)
|
| 672 |
+
| return { expr := e }
|
| 673 |
+
let h := mkAppN (mkConst unfoldThm f.constLevels!) xs
|
| 674 |
+
let some (_, _, rhs) := (β inferType h).eq?
|
| 675 |
+
| throwError "Not an equality: {h}"
|
| 676 |
+
return { expr := rhs, proof? := h }
|
| 677 |
+
else
|
| 678 |
+
return { expr := e }
|
| 679 |
+
|
| 680 |
+
def rwMatcher (altIdx : Nat) (e : Expr) : MetaM Simp.Result := do
|
| 681 |
+
if e.isAppOf ``PSum.casesOn || e.isAppOf ``PSigma.casesOn then
|
| 682 |
+
let mut e := e
|
| 683 |
+
while true do
|
| 684 |
+
if let some e' β reduceRecMatcher? e then
|
| 685 |
+
e := e'.headBeta
|
| 686 |
+
else
|
| 687 |
+
let e' := e.headBeta
|
| 688 |
+
if e != e' then
|
| 689 |
+
e := e'
|
| 690 |
+
else
|
| 691 |
+
break
|
| 692 |
+
return { expr := e }
|
| 693 |
+
else
|
| 694 |
+
unless (β isMatcherApp e) do
|
| 695 |
+
trace[Meta.FunInd] "Not a matcher application:{indentExpr e}"
|
| 696 |
+
return { expr := e }
|
| 697 |
+
let matcherDeclName := e.getAppFn.constName!
|
| 698 |
+
let eqns β Match.genMatchCongrEqns matcherDeclName
|
| 699 |
+
unless altIdx < eqns.size do
|
| 700 |
+
trace[Meta.FunInd] "When trying to reduce arm {altIdx}, only {eqns.size} equations for {.ofConstName matcherDeclName}"
|
| 701 |
+
return { expr := e }
|
| 702 |
+
let eqnThm := eqns[altIdx]!
|
| 703 |
+
try
|
| 704 |
+
withTraceNode `Meta.FunInd (pure m!"{exceptEmoji Β·} rewriting with {.ofConstName eqnThm} in{indentExpr e}") do
|
| 705 |
+
let eqProof := mkAppN (mkConst eqnThm e.getAppFn.constLevels!) e.getAppArgs
|
| 706 |
+
let (hyps, _, eqType) β forallMetaTelescope (β inferType eqProof)
|
| 707 |
+
trace[Meta.FunInd] "eqProof has type{indentExpr eqType}"
|
| 708 |
+
let proof := mkAppN eqProof hyps
|
| 709 |
+
let hyps := hyps.map (Β·.mvarId!)
|
| 710 |
+
let (isHeq, lhs, rhs) β do
|
| 711 |
+
if let some (_, lhs, _, rhs) := eqType.heq? then pure (true, lhs, rhs) else
|
| 712 |
+
if let some (_, lhs, rhs) := eqType.eq? then pure (false, lhs, rhs) else
|
| 713 |
+
throwError m!"Type of {.ofConstName eqnThm} is not an equality"
|
| 714 |
+
if !(β isDefEq e lhs) then
|
| 715 |
+
throwError m!"Left-hand side {lhs} of {.ofConstName eqnThm} does not apply to {e}"
|
| 716 |
+
/-
|
| 717 |
+
Here we instantiate the hypotheses of the congruence equation theorem
|
| 718 |
+
There are two sets of hypotheses to instantiate:
|
| 719 |
+
- `Eq` or `HEq` that relate the discriminants to the patterns
|
| 720 |
+
Solving these should instantiate the pattern variables.
|
| 721 |
+
- Overlap hypotheses (`isEqnThmHypothesis`)
|
| 722 |
+
With more book keeping we could maybe do this very precisely, knowing exactly
|
| 723 |
+
which facts provided by the splitter should go where, but it's tedious.
|
| 724 |
+
So for now let's use heuristics and try `assumption` and `rfl`.
|
| 725 |
+
-/
|
| 726 |
+
for h in hyps do
|
| 727 |
+
unless (β h.isAssigned) do
|
| 728 |
+
let hType β h.getType
|
| 729 |
+
if Simp.isEqnThmHypothesis hType then
|
| 730 |
+
-- Using unrestricted h.substVars here does not work well; it could
|
| 731 |
+
-- even introduce a dependency on the `oldIH` we want to eliminate
|
| 732 |
+
h.assumption <|> throwError "Failed to discharge {h}"
|
| 733 |
+
else if hType.isEq then
|
| 734 |
+
h.assumption <|> h.refl <|> throwError m!"Failed to resolve {h}"
|
| 735 |
+
else if hType.isHEq then
|
| 736 |
+
h.assumption <|> h.hrefl <|> throwError m!"Failed to resolve {h}"
|
| 737 |
+
let unassignedHyps β hyps.filterM fun h => return !(β h.isAssigned)
|
| 738 |
+
unless unassignedHyps.isEmpty do
|
| 739 |
+
throwError m!"Not all hypotheses of {.ofConstName eqnThm} could be discharged: {unassignedHyps}"
|
| 740 |
+
let rhs β instantiateMVars rhs
|
| 741 |
+
let proof β instantiateMVars proof
|
| 742 |
+
let proof β if isHeq then
|
| 743 |
+
try mkEqOfHEq proof
|
| 744 |
+
catch e => throwError m!"Could not un-HEq {proof}:{indentD e.toMessageData} "
|
| 745 |
+
else
|
| 746 |
+
pure proof
|
| 747 |
+
return {
|
| 748 |
+
expr := rhs
|
| 749 |
+
proof? := proof
|
| 750 |
+
}
|
| 751 |
+
catch ex =>
|
| 752 |
+
trace[Meta.FunInd] "Failed to apply {.ofConstName eqnThm}:{indentD ex.toMessageData}"
|
| 753 |
+
return { expr := e }
|
| 754 |
+
|
| 755 |
+
/--
|
| 756 |
+
Builds an expression of type `goal` by replicating the expression `e` into its tail-call-positions,
|
| 757 |
+
where it calls `buildInductionCase`. Collects the cases of the final induction hypothesis
|
| 758 |
+
as `MVars` as it goes.
|
| 759 |
+
-/
|
| 760 |
+
partial def buildInductionBody (toErase toClear : Array FVarId) (goal : Expr)
|
| 761 |
+
(oldIH newIH : FVarId) (isRecCall : Expr β Option Expr) (e : Expr) : M2 Expr := do
|
| 762 |
+
withTraceNode `Meta.FunInd
|
| 763 |
+
(pure m!"{exceptEmoji Β·} buildInductionBody: {oldIH.name} β {newIH.name}\ngoal: {goal}:{indentExpr e}") do
|
| 764 |
+
|
| 765 |
+
-- if-then-else cause case split:
|
| 766 |
+
match_expr e with
|
| 767 |
+
| ite _Ξ± c h t f =>
|
| 768 |
+
let c' β foldAndCollect oldIH newIH isRecCall c
|
| 769 |
+
let h' β foldAndCollect oldIH newIH isRecCall h
|
| 770 |
+
let t' β withLocalDecl `h .default c' fun h => M2.branch do
|
| 771 |
+
let t' β withRewrittenMotiveArg goal (rwIfWith h) fun goal' =>
|
| 772 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall t
|
| 773 |
+
mkLambdaFVars #[h] t'
|
| 774 |
+
let f' β withLocalDecl `h .default (mkNot c') fun h => M2.branch do
|
| 775 |
+
let f' β withRewrittenMotiveArg goal (rwIfWith h) fun goal' =>
|
| 776 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall f
|
| 777 |
+
mkLambdaFVars #[h] f'
|
| 778 |
+
let u β getLevel goal
|
| 779 |
+
return mkApp5 (mkConst ``dite [u]) goal c' h' t' f'
|
| 780 |
+
| dite _Ξ± c h t f =>
|
| 781 |
+
let c' β foldAndCollect oldIH newIH isRecCall c
|
| 782 |
+
let h' β foldAndCollect oldIH newIH isRecCall h
|
| 783 |
+
let t' β withLocalDecl `h .default c' fun h => M2.branch do
|
| 784 |
+
let t β instantiateLambda t #[h]
|
| 785 |
+
let t' β withRewrittenMotiveArg goal (rwIfWith h) fun goal' =>
|
| 786 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall t
|
| 787 |
+
mkLambdaFVars #[h] t'
|
| 788 |
+
let f' β withLocalDecl `h .default (mkNot c') fun h => M2.branch do
|
| 789 |
+
let f β instantiateLambda f #[h]
|
| 790 |
+
let f' β withRewrittenMotiveArg goal (rwIfWith h) fun goal' =>
|
| 791 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall f
|
| 792 |
+
mkLambdaFVars #[h] f'
|
| 793 |
+
let u β getLevel goal
|
| 794 |
+
return mkApp5 (mkConst ``dite [u]) goal c' h' t' f'
|
| 795 |
+
| cond _Ξ± c t f =>
|
| 796 |
+
let c' β foldAndCollect oldIH newIH isRecCall c
|
| 797 |
+
let t' β withLocalDecl `h .default (β mkEq c' (mkConst ``Bool.true)) fun h => M2.branch do
|
| 798 |
+
let t' β withRewrittenMotiveArg goal (rwIfWith h) fun goal' =>
|
| 799 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall t
|
| 800 |
+
mkLambdaFVars #[h] t'
|
| 801 |
+
let f' β withLocalDecl `h .default (β mkEq c' (mkConst ``Bool.false)) fun h => M2.branch do
|
| 802 |
+
let t' β withRewrittenMotiveArg goal (rwIfWith h) fun goal' =>
|
| 803 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall f
|
| 804 |
+
mkLambdaFVars #[h] t'
|
| 805 |
+
let u β getLevel goal
|
| 806 |
+
return mkApp4 (mkConst ``Bool.dcond [u]) goal c' t' f'
|
| 807 |
+
| _ =>
|
| 808 |
+
|
| 809 |
+
|
| 810 |
+
-- Check for unreachable cases. We look for the kind of expressions that `by contradiction`
|
| 811 |
+
-- produces
|
| 812 |
+
match_expr e with
|
| 813 |
+
| False.elim _ h => do
|
| 814 |
+
return β mkFalseElim goal h
|
| 815 |
+
| absurd _ _ hβ hβ => do
|
| 816 |
+
return β mkAbsurd goal hβ hβ
|
| 817 |
+
| _ => pure ()
|
| 818 |
+
if e.isApp && e.getAppFn.isConst && isNoConfusion (β getEnv) e.getAppFn.constName! then
|
| 819 |
+
let arity := (β inferType e.getAppFn).getNumHeadForalls -- crucially not reducing the noConfusionType in the type
|
| 820 |
+
let h := e.getArg! (arity - 1)
|
| 821 |
+
let hType β inferType h
|
| 822 |
+
-- The following duplicates a bit of code from the contradiction tactic, maybe worth extracting
|
| 823 |
+
-- into a common helper at some point
|
| 824 |
+
if let some (_, lhs, rhs) β matchEq? hType then
|
| 825 |
+
if let some lhsCtor β matchConstructorApp? lhs then
|
| 826 |
+
if let some rhsCtor β matchConstructorApp? rhs then
|
| 827 |
+
if lhsCtor.name != rhsCtor.name then
|
| 828 |
+
return (β mkNoConfusion goal h)
|
| 829 |
+
|
| 830 |
+
-- we look in to `PProd.mk`, as it occurs in the mutual structural recursion construction
|
| 831 |
+
match_expr goal with
|
| 832 |
+
| And goalβ goalβ => match_expr e with
|
| 833 |
+
| PProd.mk _Ξ± _Ξ² eβ eβ =>
|
| 834 |
+
let eβ' β buildInductionBody toErase toClear goalβ oldIH newIH isRecCall eβ
|
| 835 |
+
let eβ' β buildInductionBody toErase toClear goalβ oldIH newIH isRecCall eβ
|
| 836 |
+
return mkApp4 (.const ``And.intro []) goalβ goalβ eβ' eβ'
|
| 837 |
+
| _ =>
|
| 838 |
+
throwError "Goal is PProd, but expression is:{indentExpr e}"
|
| 839 |
+
| True =>
|
| 840 |
+
return .const ``True.intro []
|
| 841 |
+
| _ =>
|
| 842 |
+
|
| 843 |
+
-- match and casesOn application cause case splitting
|
| 844 |
+
if let some matcherApp β matchMatcherApp? e (alsoCasesOn := true) then
|
| 845 |
+
-- Calculate motive
|
| 846 |
+
let eType β newIH.getType
|
| 847 |
+
let motiveBody β mkArrow eType goal
|
| 848 |
+
let (mask, absMotiveBody) β mkLambdaFVarsMasked matcherApp.discrs motiveBody
|
| 849 |
+
|
| 850 |
+
-- A match that refines the parameter has been modified by `Fix.lean` to refine the IH,
|
| 851 |
+
-- so we need to replace that IH
|
| 852 |
+
if matcherApp.remaining.size == 1 && matcherApp.remaining[0]!.isFVarOf oldIH then
|
| 853 |
+
let matcherApp' β matcherApp.transform (useSplitter := true)
|
| 854 |
+
(addEqualities := true)
|
| 855 |
+
(onParams := (foldAndCollect oldIH newIH isRecCall Β·))
|
| 856 |
+
(onMotive := fun xs _body => pure (absMotiveBody.beta (maskArray mask xs)))
|
| 857 |
+
(onAlt := fun altIdx expAltType alt => M2.branch do
|
| 858 |
+
lambdaTelescope1 alt fun oldIH' alt => do
|
| 859 |
+
forallBoundedTelescope expAltType (some 1) fun newIH' goal' => do
|
| 860 |
+
let #[newIH'] := newIH' | unreachable!
|
| 861 |
+
let toErase' := toErase ++ #[oldIH', newIH'.fvarId!]
|
| 862 |
+
let toClear' := toClear ++ matcherApp.discrs.filterMap (Β·.fvarId?)
|
| 863 |
+
let alt' β withRewrittenMotiveArg goal' (rwMatcher altIdx) fun goal'' => do
|
| 864 |
+
-- logInfo m!"rwMatcher after {matcherApp.matcherName} on{indentExpr goal'}\nyields{indentExpr goal''}"
|
| 865 |
+
buildInductionBody toErase' toClear' goal'' oldIH' newIH'.fvarId! isRecCall alt
|
| 866 |
+
mkLambdaFVars #[newIH'] alt')
|
| 867 |
+
(onRemaining := fun _ => pure #[.fvar newIH])
|
| 868 |
+
return matcherApp'.toExpr
|
| 869 |
+
|
| 870 |
+
-- A match that does not refine the parameter, but that we still want to split into separate
|
| 871 |
+
-- cases
|
| 872 |
+
if matcherApp.remaining.isEmpty then
|
| 873 |
+
-- Calculate motive
|
| 874 |
+
let (mask, absMotiveBody) β mkLambdaFVarsMasked matcherApp.discrs goal
|
| 875 |
+
|
| 876 |
+
let matcherApp' β matcherApp.transform (useSplitter := true)
|
| 877 |
+
(addEqualities := true)
|
| 878 |
+
(onParams := (foldAndCollect oldIH newIH isRecCall Β·))
|
| 879 |
+
(onMotive := fun xs _body => pure (absMotiveBody.beta (maskArray mask xs)))
|
| 880 |
+
(onAlt := fun altIdx expAltType alt => M2.branch do
|
| 881 |
+
withRewrittenMotiveArg expAltType (rwMatcher altIdx) fun expAltType' =>
|
| 882 |
+
buildInductionBody toErase toClear expAltType' oldIH newIH isRecCall alt)
|
| 883 |
+
return matcherApp'.toExpr
|
| 884 |
+
|
| 885 |
+
-- we look through mdata
|
| 886 |
+
if e.isMData then
|
| 887 |
+
let b β withRewrittenMotiveArg goal (rwMData) fun goal' =>
|
| 888 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall e.mdataExpr!
|
| 889 |
+
return e.updateMData! b
|
| 890 |
+
|
| 891 |
+
if let .letE n t v b _ := e then
|
| 892 |
+
let t' β foldAndCollect oldIH newIH isRecCall t
|
| 893 |
+
let v' β foldAndCollect oldIH newIH isRecCall v
|
| 894 |
+
return β withLetDecl n t' v' fun x => M2.branch do
|
| 895 |
+
let b' β withRewrittenMotiveArg goal (rwLetWith x) fun goal' =>
|
| 896 |
+
buildInductionBody toErase toClear goal' oldIH newIH isRecCall (b.instantiate1 x)
|
| 897 |
+
mkLetFVars #[x] b'
|
| 898 |
+
|
| 899 |
+
-- Special case for traversing the PProdβed bodies in our encoding of structural mutual recursion
|
| 900 |
+
if let .lam n t b bi := e then
|
| 901 |
+
if goal.isForall then
|
| 902 |
+
let t' β foldAndCollect oldIH newIH isRecCall t
|
| 903 |
+
return β withLocalDecl n bi t' fun x => M2.branch do
|
| 904 |
+
let goal' β instantiateForall goal #[x]
|
| 905 |
+
let b' β buildInductionBody toErase toClear goal' oldIH newIH isRecCall (b.instantiate1 x)
|
| 906 |
+
mkLambdaFVars #[x] b'
|
| 907 |
+
|
| 908 |
+
liftM <| buildInductionCase oldIH newIH isRecCall toErase toClear goal e
|
| 909 |
+
|
| 910 |
+
/--
|
| 911 |
+
Given an expression `e` with metavariables `mvars`
|
| 912 |
+
* performs more cleanup:
|
| 913 |
+
* removes unused let-expressions after index `index`
|
| 914 |
+
* tries to substitute variables after index `index`
|
| 915 |
+
* lifts them to the current context by reverting all local declarations after index `index`
|
| 916 |
+
* introducing a local variable for each of the meta variable
|
| 917 |
+
* assigning that local variable to the mvar
|
| 918 |
+
* and finally lambda-abstracting over these new local variables.
|
| 919 |
+
|
| 920 |
+
This operation only works if the metavariables are independent from each other.
|
| 921 |
+
|
| 922 |
+
The resulting meta variable assignment is no longer valid (mentions out-of-scope
|
| 923 |
+
variables), so after this operations, terms that still mention these meta variables must not
|
| 924 |
+
be used anymore.
|
| 925 |
+
|
| 926 |
+
We are not using `mkLambdaFVars` on mvars directly, nor `abstractMVars`, as these at the moment
|
| 927 |
+
do not handle delayed assignments correctly.
|
| 928 |
+
-/
|
| 929 |
+
def abstractIndependentMVars (mvars : Array MVarId) (index : Nat) (e : Expr) : MetaM Expr := do
|
| 930 |
+
trace[Meta.FunInd] "abstractIndependentMVars, to revert after {index}, original mvars: {mvars}"
|
| 931 |
+
let mvars β mvars.mapM fun mvar => do
|
| 932 |
+
let mvar β cleanupAfter mvar index
|
| 933 |
+
mvar.withContext do
|
| 934 |
+
let fvarIds := (β getLCtx).foldl (init := #[]) (start := index+1) fun fvarIds decl => fvarIds.push decl.fvarId
|
| 935 |
+
let (_, mvar) β mvar.revert fvarIds
|
| 936 |
+
pure mvar
|
| 937 |
+
trace[Meta.FunInd] "abstractIndependentMVars, reverted mvars: {mvars}"
|
| 938 |
+
let names := Array.ofFn (n := mvars.size) fun β¨i,_β© => .mkSimple s!"case{i+1}"
|
| 939 |
+
let types β mvars.mapM MVarId.getType
|
| 940 |
+
Meta.withLocalDeclsDND (names.zip types) fun xs => do
|
| 941 |
+
for mvar in mvars, x in xs do
|
| 942 |
+
mvar.assign x
|
| 943 |
+
mkLambdaFVars xs (β instantiateMVars e)
|
| 944 |
+
|
| 945 |
+
/--
|
| 946 |
+
Given a unary definition `foo` defined via `WellFounded.fixF`, derive a suitable induction principle
|
| 947 |
+
`foo.induct` for it. See module doc for details.
|
| 948 |
+
-/
|
| 949 |
+
def deriveUnaryInduction (unfolding : Bool) (name : Name) : MetaM Name := do
|
| 950 |
+
let inductName := getFunInductName (unfolding := unfolding) name
|
| 951 |
+
realizeConst name inductName (doRealize inductName)
|
| 952 |
+
return inductName
|
| 953 |
+
where doRealize (inductName : Name) := do
|
| 954 |
+
let info β getConstInfoDefn name
|
| 955 |
+
let varNames β forallTelescope info.type fun xs _ => xs.mapM (Β·.fvarId!.getUserName)
|
| 956 |
+
|
| 957 |
+
-- Uses of WellFounded.fix can be partially applied. Here we eta-expand the body
|
| 958 |
+
-- to make sure that `target` indeed the last parameter
|
| 959 |
+
let e := info.value
|
| 960 |
+
let e β lambdaTelescope e fun params body => do
|
| 961 |
+
if body.isAppOfArity ``WellFounded.fix 5 then
|
| 962 |
+
forallBoundedTelescope (β inferType body) (some 1) fun xs _ => do
|
| 963 |
+
unless xs.size = 1 do
|
| 964 |
+
throwError "functional induction: Failed to eta-expand{indentExpr e}"
|
| 965 |
+
mkLambdaFVars (params ++ xs) (mkAppN body xs)
|
| 966 |
+
else
|
| 967 |
+
pure e
|
| 968 |
+
let (e', paramMask) β lambdaTelescope e fun params funBody => MatcherApp.withUserNames params varNames do
|
| 969 |
+
match_expr funBody with
|
| 970 |
+
| fix@WellFounded.fix Ξ± _motive rel wf body target =>
|
| 971 |
+
unless params.back! == target do
|
| 972 |
+
throwError "functional induction: expected the target as last parameter{indentExpr e}"
|
| 973 |
+
let fixedParamPerms := params.pop
|
| 974 |
+
let motiveType β
|
| 975 |
+
if unfolding then
|
| 976 |
+
withLocalDeclD `r (β instantiateForall info.type params) fun r =>
|
| 977 |
+
mkForallFVars #[target, r] (.sort 0)
|
| 978 |
+
else
|
| 979 |
+
mkForallFVars #[target] (.sort 0)
|
| 980 |
+
withLocalDeclD `motive motiveType fun motive => do
|
| 981 |
+
let fn := mkAppN (β mkConstWithLevelParams name) fixedParamPerms
|
| 982 |
+
let isRecCall : Expr β Option Expr := fun e =>
|
| 983 |
+
e.withApp fun f xs =>
|
| 984 |
+
if f.isFVarOf motive.fvarId! && xs.size > 0 then
|
| 985 |
+
mkApp fn xs[0]!
|
| 986 |
+
else
|
| 987 |
+
none
|
| 988 |
+
|
| 989 |
+
let motiveArg β
|
| 990 |
+
if unfolding then
|
| 991 |
+
let motiveArg := mkApp2 motive target (mkAppN (β mkConstWithLevelParams name) params)
|
| 992 |
+
mkLambdaFVars #[target] motiveArg
|
| 993 |
+
else
|
| 994 |
+
pure motive
|
| 995 |
+
let e' := .const ``WellFounded.fix [fix.constLevels![0]!, levelZero]
|
| 996 |
+
let e' := mkApp4 e' Ξ± motiveArg rel wf
|
| 997 |
+
check e'
|
| 998 |
+
let (body', mvars) β M2.run do
|
| 999 |
+
forallTelescope (β inferType e').bindingDomain! fun xs goal => do
|
| 1000 |
+
if xs.size β 2 then
|
| 1001 |
+
throwError "expected recursor argument to take 2 parameters, got {xs}" else
|
| 1002 |
+
let targets : Array Expr := xs[*...1]
|
| 1003 |
+
let genIH := xs[1]!
|
| 1004 |
+
let extraParams := xs[2...*]
|
| 1005 |
+
-- open body with the same arg
|
| 1006 |
+
let body β instantiateLambda body targets
|
| 1007 |
+
lambdaTelescope1 body fun oldIH body => do
|
| 1008 |
+
let body β instantiateLambda body extraParams
|
| 1009 |
+
let body' β withRewrittenMotiveArg goal (rwFun #[name]) fun goal => do
|
| 1010 |
+
buildInductionBody #[oldIH, genIH.fvarId!] #[] goal oldIH genIH.fvarId! isRecCall body
|
| 1011 |
+
if body'.containsFVar oldIH then
|
| 1012 |
+
throwError m!"Did not fully eliminate {mkFVar oldIH} from induction principle body:{indentExpr body}"
|
| 1013 |
+
mkLambdaFVars (targets.push genIH) (β mkLambdaFVars extraParams body')
|
| 1014 |
+
let e' := mkApp2 e' body' target
|
| 1015 |
+
let e' β mkLambdaFVars #[target] e'
|
| 1016 |
+
let e' β abstractIndependentMVars mvars (β motive.fvarId!.getDecl).index e'
|
| 1017 |
+
let e' β mkLambdaFVars #[motive] e'
|
| 1018 |
+
|
| 1019 |
+
-- We used to pass (usedOnly := false) below in the hope that the types of the
|
| 1020 |
+
-- induction principle match the type of the function better.
|
| 1021 |
+
-- But this leads to avoidable parameters that make functional induction strictly less
|
| 1022 |
+
-- useful (e.g. when the unused parameter mentions bound variables in the users' goal)
|
| 1023 |
+
let (paramMask, e') β mkLambdaFVarsMasked fixedParamPerms e'
|
| 1024 |
+
let e' β instantiateMVars e'
|
| 1025 |
+
return (e', paramMask)
|
| 1026 |
+
| _ =>
|
| 1027 |
+
if funBody.isAppOf ``WellFounded.fix then
|
| 1028 |
+
throwError "Function {name} defined via WellFounded.fix with unexpected arity {funBody.getAppNumArgs}:{indentExpr funBody}"
|
| 1029 |
+
else
|
| 1030 |
+
throwError "Function {name} not defined via WellFounded.fix:{indentExpr funBody}"
|
| 1031 |
+
|
| 1032 |
+
unless (β isTypeCorrect e') do
|
| 1033 |
+
logError m!"failed to derive a type-correct induction principle:{indentExpr e'}"
|
| 1034 |
+
check e'
|
| 1035 |
+
|
| 1036 |
+
let eTyp β inferType e'
|
| 1037 |
+
let eTyp β elimTypeAnnotations eTyp
|
| 1038 |
+
let eTyp β letToHave eTyp
|
| 1039 |
+
-- logInfo m!"eTyp: {eTyp}"
|
| 1040 |
+
let levelParams := (collectLevelParams {} eTyp).params
|
| 1041 |
+
-- Prune unused level parameters, preserving the original order
|
| 1042 |
+
let funUs := info.levelParams.toArray
|
| 1043 |
+
let usMask := funUs.map (levelParams.contains Β·)
|
| 1044 |
+
let us := maskArray usMask funUs |>.toList
|
| 1045 |
+
|
| 1046 |
+
addDecl <| Declaration.thmDecl
|
| 1047 |
+
{ name := inductName, levelParams := us, type := eTyp, value := e' }
|
| 1048 |
+
|
| 1049 |
+
setFunIndInfo {
|
| 1050 |
+
funName := name
|
| 1051 |
+
funIndName := inductName
|
| 1052 |
+
levelMask := usMask
|
| 1053 |
+
params := paramMask.map (cond Β· .param .dropped) ++ #[.target]
|
| 1054 |
+
}
|
| 1055 |
+
|
| 1056 |
+
/--
|
| 1057 |
+
Given a realizer for `foo.mutual_induct`, defines `foo.induct`, `bar.induct` etc.
|
| 1058 |
+
Used for well-founded and structural recursion.
|
| 1059 |
+
-/
|
| 1060 |
+
def projectMutualInduct (unfolding : Bool) (names : Array Name) (mutualInduct : MetaM Name) (finalizeFirstInd : MetaM Unit) : MetaM Unit := do
|
| 1061 |
+
for name in names, idx in [:names.size] do
|
| 1062 |
+
let inductName := getFunInductName (unfolding := unfolding) name
|
| 1063 |
+
realizeConst names[0]! inductName do
|
| 1064 |
+
let ci β getConstInfo (β mutualInduct)
|
| 1065 |
+
let levelParams := ci.levelParams
|
| 1066 |
+
let value β forallTelescope ci.type fun xs _body => do
|
| 1067 |
+
let value := .const ci.name (levelParams.map mkLevelParam)
|
| 1068 |
+
let value := mkAppN value xs
|
| 1069 |
+
let value β PProdN.projM names.size idx value
|
| 1070 |
+
mkLambdaFVars xs value
|
| 1071 |
+
let type β inferType value
|
| 1072 |
+
let type β letToHave type
|
| 1073 |
+
addDecl <| Declaration.thmDecl { name := inductName, levelParams, type, value }
|
| 1074 |
+
|
| 1075 |
+
if idx == 0 then finalizeFirstInd
|
| 1076 |
+
|
| 1077 |
+
/--
|
| 1078 |
+
For a (non-mutual!) definition of `name`, uses the `FunIndInfo` associated with the `unaryInduct` and
|
| 1079 |
+
derives the one for the n-ary function.
|
| 1080 |
+
-/
|
| 1081 |
+
def setNaryFunIndInfo (unfolding : Bool) (fixedParamPerms : FixedParamPerms) (funName : Name) (unaryInduct : Name) : MetaM Unit := do
|
| 1082 |
+
assert! fixedParamPerms.perms.size = 1 -- only non-mutual for now
|
| 1083 |
+
let funIndName := getFunInductName (unfolding := unfolding) funName
|
| 1084 |
+
unless funIndName = unaryInduct do
|
| 1085 |
+
let some unaryFunIndInfo β getFunIndInfoForInduct? unaryInduct
|
| 1086 |
+
| throwError "Expected {unaryInduct} to have FunIndInfo"
|
| 1087 |
+
let fixedParamPerm := fixedParamPerms.perms[0]!
|
| 1088 |
+
let mut params := #[]
|
| 1089 |
+
let mut j := 0
|
| 1090 |
+
for h : i in [:fixedParamPerm.size] do
|
| 1091 |
+
if fixedParamPerm[i].isSome then
|
| 1092 |
+
assert! j + 1 < unaryFunIndInfo.params.size
|
| 1093 |
+
params := params.push unaryFunIndInfo.params[j]!
|
| 1094 |
+
j := j + 1
|
| 1095 |
+
else
|
| 1096 |
+
params := params.push .target
|
| 1097 |
+
assert! j + 1 = unaryFunIndInfo.params.size
|
| 1098 |
+
|
| 1099 |
+
setFunIndInfo { unaryFunIndInfo with funName, funIndName, params }
|
| 1100 |
+
|
| 1101 |
+
/--
|
| 1102 |
+
In the type of `value`, reduces
|
| 1103 |
+
* Beta-redexes
|
| 1104 |
+
* `PSigma.casesOn (PSigma.mk a b) (fun x y => k x y) --> k a b`
|
| 1105 |
+
* `PSum.casesOn (PSum.inl x) kβ kβ --> kβ x`
|
| 1106 |
+
* `foo._unary (PSum.inl (PSigma.mk a b)) --> foo a b`
|
| 1107 |
+
and then wraps `value` in an appropriate type hint.
|
| 1108 |
+
|
| 1109 |
+
(The implementation is repetitive and verbose, and should be cleaned up when convenient.)
|
| 1110 |
+
-/
|
| 1111 |
+
def cleanPackedArgs (eqnInfo : WF.EqnInfo) (value : Expr) : MetaM Expr := do
|
| 1112 |
+
let type β inferType value
|
| 1113 |
+
let cleanType β Meta.transform type (skipConstInApp := true) (post := fun e => do
|
| 1114 |
+
-- Need to beta-reduce first
|
| 1115 |
+
let e' := e.headBeta
|
| 1116 |
+
if e' != e then
|
| 1117 |
+
return .visit e'
|
| 1118 |
+
|
| 1119 |
+
e.withApp fun f args => do
|
| 1120 |
+
-- Look for PSigma redexes
|
| 1121 |
+
if f.isConstOf ``PSigma.casesOn then
|
| 1122 |
+
if 5 β€ args.size then
|
| 1123 |
+
let scrut := args[3]!
|
| 1124 |
+
let k := args[4]!
|
| 1125 |
+
let extra := args[5...*]
|
| 1126 |
+
if scrut.isAppOfArity ``PSigma.mk 4 then
|
| 1127 |
+
let #[_, _, x, y] := scrut.getAppArgs | unreachable!
|
| 1128 |
+
let e' := (k.beta #[x, y]).beta extra
|
| 1129 |
+
return .visit e'
|
| 1130 |
+
-- Look for PSigma projection
|
| 1131 |
+
if f.isConstOf ``PSigma.fst then
|
| 1132 |
+
if h : 3 β€ args.size then
|
| 1133 |
+
let scrut := args[2]
|
| 1134 |
+
let extra := args[3...*]
|
| 1135 |
+
if scrut.isAppOfArity ``PSigma.mk 4 then
|
| 1136 |
+
let #[_, _, x, _y] := scrut.getAppArgs | unreachable!
|
| 1137 |
+
let e' := x.beta extra
|
| 1138 |
+
return .visit e'
|
| 1139 |
+
if f.isConstOf ``PSigma.snd then
|
| 1140 |
+
if h : 3 β€ args.size then
|
| 1141 |
+
let scrut := args[2]
|
| 1142 |
+
let extra := args[3...*]
|
| 1143 |
+
if scrut.isAppOfArity ``PSigma.mk 4 then
|
| 1144 |
+
let #[_, _, _x, y] := scrut.getAppArgs | unreachable!
|
| 1145 |
+
let e' := y.beta extra
|
| 1146 |
+
return .visit e'
|
| 1147 |
+
if f.isProj then
|
| 1148 |
+
let scrut := e.projExpr!
|
| 1149 |
+
if scrut.isAppOfArity ``PSigma.mk 4 then
|
| 1150 |
+
let #[_, _, x, y] := scrut.getAppArgs | unreachable!
|
| 1151 |
+
let e' := (if e.projIdx! = 0 then x else y).beta args
|
| 1152 |
+
return .visit e'
|
| 1153 |
+
|
| 1154 |
+
-- Look for PSum redexes
|
| 1155 |
+
if f.isConstOf ``PSum.casesOn then
|
| 1156 |
+
if 6 β€ args.size then
|
| 1157 |
+
let scrut := args[3]!
|
| 1158 |
+
let kβ := args[4]!
|
| 1159 |
+
let kβ := args[5]!
|
| 1160 |
+
let extra := args[6...*]
|
| 1161 |
+
if scrut.isAppOfArity ``PSum.inl 3 then
|
| 1162 |
+
let e' := (kβ.beta #[scrut.appArg!]).beta extra
|
| 1163 |
+
return .visit e'
|
| 1164 |
+
if scrut.isAppOfArity ``PSum.inr 3 then
|
| 1165 |
+
let e' := (kβ.beta #[scrut.appArg!]).beta extra
|
| 1166 |
+
return .visit e'
|
| 1167 |
+
-- Look for _unary redexes
|
| 1168 |
+
if f.isConstOf eqnInfo.declNameNonRec then
|
| 1169 |
+
if h : args.size β₯ eqnInfo.fixedParamPerms.numFixed + 1 then
|
| 1170 |
+
let xs := args[*...eqnInfo.fixedParamPerms.numFixed]
|
| 1171 |
+
let packedArg := args[eqnInfo.fixedParamPerms.numFixed]
|
| 1172 |
+
let extraArgs := args[eqnInfo.fixedParamPerms.numFixed<...*]
|
| 1173 |
+
let some (funIdx, ys) := eqnInfo.argsPacker.unpack packedArg
|
| 1174 |
+
| throwError "Unexpected packedArg:{indentExpr packedArg}"
|
| 1175 |
+
let args' := eqnInfo.fixedParamPerms.perms[funIdx]!.buildArgs xs ys
|
| 1176 |
+
let e' := .const eqnInfo.declNames[funIdx]! e.getAppFn.constLevels!
|
| 1177 |
+
let e' := mkAppN e' args'
|
| 1178 |
+
let e' := mkAppN e' extraArgs
|
| 1179 |
+
return .continue e'
|
| 1180 |
+
|
| 1181 |
+
return .continue e)
|
| 1182 |
+
mkExpectedTypeHint value cleanType
|
| 1183 |
+
|
| 1184 |
+
/--
|
| 1185 |
+
Retrieves `foo._unary.induct`, where the motive is a `PSigma`/`PSum` type, and
|
| 1186 |
+
unpacks it into a n-ary and (possibly) joint induction principle.
|
| 1187 |
+
-/
|
| 1188 |
+
def unpackMutualInduction (unfolding : Bool) (eqnInfo : WF.EqnInfo) : MetaM Name := do
|
| 1189 |
+
let inductName := if eqnInfo.declNames.size > 1 then
|
| 1190 |
+
getMutualInductName (unfolding := unfolding) eqnInfo.declNames[0]!
|
| 1191 |
+
else
|
| 1192 |
+
-- If there is no mutual recursion, we generate the `foo.induct` directly.
|
| 1193 |
+
getFunInductName (unfolding := unfolding) eqnInfo.declNames[0]!
|
| 1194 |
+
realizeConst eqnInfo.declNames[0]! inductName (doRealize inductName)
|
| 1195 |
+
return inductName
|
| 1196 |
+
where doRealize inductName := do
|
| 1197 |
+
let unaryInductName β deriveUnaryInduction (unfolding := unfolding) eqnInfo.declNameNonRec
|
| 1198 |
+
prependError m!"Cannot unpack functional cases principle {.ofConstName unaryInductName} (please report this issue)" do
|
| 1199 |
+
let ci β getConstInfo unaryInductName
|
| 1200 |
+
let us := ci.levelParams
|
| 1201 |
+
let value := .const ci.name (us.map mkLevelParam)
|
| 1202 |
+
let motivePos β forallTelescope ci.type fun xs concl => concl.withApp fun motive targets => do
|
| 1203 |
+
unless motive.isFVar && targets.size = (if unfolding then 2 else 1) && targets[0]!.isFVar do
|
| 1204 |
+
throwError "conclusion {concl} does not look like a packed motive application"
|
| 1205 |
+
let packedTarget := targets[0]!
|
| 1206 |
+
unless xs.back! == packedTarget do
|
| 1207 |
+
throwError "packed target not last argument to {unaryInductName}"
|
| 1208 |
+
let some motivePos := xs.findIdx? (Β· == motive)
|
| 1209 |
+
| throwError "could not find motive {motive} in {xs}"
|
| 1210 |
+
pure motivePos
|
| 1211 |
+
let value β forallBoundedTelescope ci.type motivePos fun params type => do
|
| 1212 |
+
let value := mkAppN value params
|
| 1213 |
+
eqnInfo.argsPacker.curryParam value type fun motives value type =>
|
| 1214 |
+
-- Bring the rest into scope
|
| 1215 |
+
forallTelescope type fun xs _concl => do
|
| 1216 |
+
let alts := xs.pop
|
| 1217 |
+
let value := mkAppN value alts
|
| 1218 |
+
let value β eqnInfo.argsPacker.curry value
|
| 1219 |
+
let value β mkLambdaFVars alts value
|
| 1220 |
+
let value β mkLambdaFVars motives value
|
| 1221 |
+
let value β mkLambdaFVars params value
|
| 1222 |
+
check value
|
| 1223 |
+
let value β cleanPackedArgs eqnInfo value
|
| 1224 |
+
return value
|
| 1225 |
+
|
| 1226 |
+
unless β isTypeCorrect value do
|
| 1227 |
+
logError m!"final term is type incorrect:{indentExpr value}"
|
| 1228 |
+
check value
|
| 1229 |
+
let type β inferType value
|
| 1230 |
+
let type β elimOptParam type
|
| 1231 |
+
let type β letToHave type
|
| 1232 |
+
|
| 1233 |
+
addDecl <| Declaration.thmDecl
|
| 1234 |
+
{ name := inductName, levelParams := ci.levelParams, type, value }
|
| 1235 |
+
|
| 1236 |
+
if eqnInfo.argsPacker.numFuncs = 1 then
|
| 1237 |
+
setNaryFunIndInfo (unfolding := unfolding) eqnInfo.fixedParamPerms eqnInfo.declNames[0]! unaryInductName
|
| 1238 |
+
|
| 1239 |
+
def withLetDecls {Ξ±} (name : Name) (ts : Array Expr) (es : Array Expr) (k : Array Expr β MetaM Ξ±) : MetaM Ξ± := do
|
| 1240 |
+
assert! es.size = ts.size
|
| 1241 |
+
go 0 #[]
|
| 1242 |
+
where
|
| 1243 |
+
go (i : Nat) (acc : Array Expr) := do
|
| 1244 |
+
if h : i < es.size then
|
| 1245 |
+
let n := if es.size = 1 then name else name.appendIndexAfter (i + 1)
|
| 1246 |
+
withLetDecl n ts[i]! es[i] fun x => go (i+1) (acc.push x)
|
| 1247 |
+
else
|
| 1248 |
+
k acc
|
| 1249 |
+
|
| 1250 |
+
/--
|
| 1251 |
+
Given a recursive definition `foo` defined via structural recursion, derive `foo.mutual_induct`,
|
| 1252 |
+
if needed, and `foo.induct` for all functions in the group.
|
| 1253 |
+
See module doc for details.
|
| 1254 |
+
-/
|
| 1255 |
+
def deriveInductionStructural (unfolding : Bool) (names : Array Name) (fixedParamPerms : FixedParamPerms) : MetaM Name := do
|
| 1256 |
+
let inductName :=
|
| 1257 |
+
if names.size = 1 then
|
| 1258 |
+
getFunInductName (unfolding := unfolding) names[0]!
|
| 1259 |
+
else
|
| 1260 |
+
getMutualInductName (unfolding := unfolding) names[0]!
|
| 1261 |
+
realizeConst names[0]! inductName (doRealize inductName)
|
| 1262 |
+
return inductName
|
| 1263 |
+
where doRealize inductName := do
|
| 1264 |
+
let infos β names.mapM getConstInfoDefn
|
| 1265 |
+
assert! infos.size > 0
|
| 1266 |
+
-- First open up the fixed parameters everywhere
|
| 1267 |
+
let (e', paramMask, motiveArities) β fixedParamPerms.perms[0]!.forallTelescope infos[0]!.type fun xs => do
|
| 1268 |
+
-- Now look at the body of an arbitrary of the functions (they are essentially the same
|
| 1269 |
+
-- up to the final projections)
|
| 1270 |
+
let body β fixedParamPerms.perms[0]!.instantiateLambda infos[0]!.value xs
|
| 1271 |
+
let body := body.eta
|
| 1272 |
+
|
| 1273 |
+
lambdaTelescope body fun ys body => do
|
| 1274 |
+
-- The body is of the form (brecOn β¦ ).2.2.1 extra1 extra2 etc; ignore the
|
| 1275 |
+
-- projection here
|
| 1276 |
+
let f' := body.getAppFn
|
| 1277 |
+
let body' := PProdN.stripProjs f'
|
| 1278 |
+
let f := body'.getAppFn
|
| 1279 |
+
let args := body'.getAppArgs ++ body.getAppArgs
|
| 1280 |
+
|
| 1281 |
+
let body := PProdN.stripProjs body
|
| 1282 |
+
let .const brecOnName us := f |
|
| 1283 |
+
throwError "{infos[0]!.name}: unexpected body:{indentExpr infos[0]!.value}\ninstantiated to{indentExpr body}"
|
| 1284 |
+
unless isBRecOnRecursor (β getEnv) brecOnName do
|
| 1285 |
+
throwError "{infos[0]!.name}: expected .brecOn application, found:{indentExpr body}"
|
| 1286 |
+
|
| 1287 |
+
let .str indName _ := brecOnName | unreachable!
|
| 1288 |
+
let indInfo β getConstInfoInduct indName
|
| 1289 |
+
|
| 1290 |
+
-- we have a `.brecOn` application, so now figure out the length of the fixed prefix
|
| 1291 |
+
-- we can use the recInfo for `.rec`, since `.brecOn` has a similar structure
|
| 1292 |
+
let recInfo β getConstInfoRec (mkRecName indName)
|
| 1293 |
+
if args.size < recInfo.numParams + recInfo.numMotives + recInfo.numIndices + 1 + recInfo.numMotives then
|
| 1294 |
+
throwError "insufficient arguments to .brecOn:{indentExpr body}"
|
| 1295 |
+
let brecOnArgs : Array Expr := args[*...recInfo.numParams]
|
| 1296 |
+
let _brecOnMotives : Array Expr := args[recInfo.numParams...(recInfo.numParams + recInfo.numMotives)]
|
| 1297 |
+
let brecOnTargets : Array Expr := args[(recInfo.numParams + recInfo.numMotives)...
|
| 1298 |
+
(recInfo.numParams + recInfo.numMotives + recInfo.numIndices + 1)]
|
| 1299 |
+
let brecOnMinors : Array Expr := args[(recInfo.numParams + recInfo.numMotives + recInfo.numIndices + 1)...
|
| 1300 |
+
(recInfo.numParams + recInfo.numMotives + recInfo.numIndices + 1 + recInfo.numMotives)]
|
| 1301 |
+
let brecOnExtras : Array Expr := args[(recInfo.numParams + recInfo.numMotives + recInfo.numIndices + 1 +
|
| 1302 |
+
recInfo.numMotives)...*]
|
| 1303 |
+
unless brecOnTargets.all (Β·.isFVar) do
|
| 1304 |
+
throwError "the indices and major argument of the brecOn application are not variables:{indentExpr body}"
|
| 1305 |
+
unless brecOnExtras.all (Β·.isFVar) do
|
| 1306 |
+
throwError "the extra arguments to the brecOn application are not variables:{indentExpr body}"
|
| 1307 |
+
let _ :: indLevels := us | throwError "Too few universe parameters in .brecOn application:{indentExpr body}"
|
| 1308 |
+
|
| 1309 |
+
let group : Structural.IndGroupInst := { Structural.IndGroupInfo.ofInductiveVal indInfo with
|
| 1310 |
+
levels := indLevels, params := brecOnArgs }
|
| 1311 |
+
|
| 1312 |
+
-- We also need to know the number of indices of each type former, including the auxiliary
|
| 1313 |
+
-- type formers that do not have IndInfo. We can read it off the motives types of the recursor.
|
| 1314 |
+
let numTypeFormerTargetss β do
|
| 1315 |
+
let aux := mkAppN (.const recInfo.name (0 :: group.levels)) group.params
|
| 1316 |
+
let motives β inferArgumentTypesN recInfo.numMotives aux
|
| 1317 |
+
motives.mapM fun motive =>
|
| 1318 |
+
forallTelescopeReducing motive fun xs _ => pure xs.size
|
| 1319 |
+
|
| 1320 |
+
-- Recreate the recArgInfos. Maybe more robust and simpler to store relevant parts in the EqnInfos?
|
| 1321 |
+
let recArgInfos β infos.mapIdxM fun funIdx info => do
|
| 1322 |
+
let some eqnInfo := Structural.eqnInfoExt.find? (β getEnv) info.name | throwError "{info.name} missing eqnInfo"
|
| 1323 |
+
let value β fixedParamPerms.perms[funIdx]!.instantiateLambda info.value xs
|
| 1324 |
+
let recArgInfo' β lambdaTelescope value fun ys _ =>
|
| 1325 |
+
let args := fixedParamPerms.perms[funIdx]!.buildArgs xs ys
|
| 1326 |
+
Structural.getRecArgInfo info.name fixedParamPerms.perms[funIdx]! args eqnInfo.recArgPos
|
| 1327 |
+
let #[recArgInfo] β Structural.argsInGroup group xs value #[recArgInfo']
|
| 1328 |
+
| throwError "Structural.argsInGroup did not return a recArgInfo"
|
| 1329 |
+
pure recArgInfo
|
| 1330 |
+
|
| 1331 |
+
let positions : Structural.Positions := .groupAndSort (Β·.indIdx) recArgInfos (Array.range indInfo.numTypeFormers)
|
| 1332 |
+
|
| 1333 |
+
-- Below we'll need the types of the motive arguments (brecOn argument order)
|
| 1334 |
+
let brecMotiveTypes β inferArgumentTypesN recInfo.numMotives (group.brecOn 0 0)
|
| 1335 |
+
trace[Meta.FunInd] m!"brecMotiveTypes: {brecMotiveTypes}"
|
| 1336 |
+
assert! brecMotiveTypes.size = positions.size
|
| 1337 |
+
|
| 1338 |
+
-- Remove the varying parameters from the environment
|
| 1339 |
+
withErasedFVars (ys.map (Β·.fvarId!)) do
|
| 1340 |
+
-- The brecOnArgs, brecOnMotives and brecOnMinor should still be valid in this
|
| 1341 |
+
-- context, and are the parts relevant for every function in the mutual group
|
| 1342 |
+
|
| 1343 |
+
-- Calculate the types of the induction motives (natural argument order) for each function
|
| 1344 |
+
let motiveTypes β infos.mapIdxM fun funIdx info => do
|
| 1345 |
+
let funType β fixedParamPerms.perms[funIdx]!.instantiateForall info.type xs
|
| 1346 |
+
forallBoundedTelescope funType (some (fixedParamPerms.perms[funIdx]!.size - xs.size)) fun ys rType => do
|
| 1347 |
+
if unfolding then
|
| 1348 |
+
withLocalDeclD `r rType fun r =>
|
| 1349 |
+
mkForallFVars (ys.push r) (.sort 0)
|
| 1350 |
+
else
|
| 1351 |
+
mkForallFVars ys (.sort 0)
|
| 1352 |
+
trace[Meta.FunInd] m!"motiveTypes: {motiveTypes}"
|
| 1353 |
+
let motiveArities β motiveTypes.mapM fun motiveType =>
|
| 1354 |
+
forallTelescope motiveType fun ys _ => pure ys.size
|
| 1355 |
+
let motiveNames := Array.ofFn (n := infos.size) fun β¨i, _β© =>
|
| 1356 |
+
if infos.size = 1 then .mkSimple "motive" else .mkSimple s!"motive_{i+1}"
|
| 1357 |
+
|
| 1358 |
+
withLocalDeclsDND (motiveNames.zip motiveTypes) fun motives => do
|
| 1359 |
+
|
| 1360 |
+
-- Prepare the `isRecCall` that recognizes recursive calls
|
| 1361 |
+
let isRecCall : Expr β Option Expr := fun e => do
|
| 1362 |
+
if let .some funIdx := motives.idxOf? e.getAppFn then
|
| 1363 |
+
if e.getAppNumArgs = motiveArities[funIdx]! then
|
| 1364 |
+
let info := infos[funIdx]!
|
| 1365 |
+
let args := if unfolding then e.getAppArgs.pop else e.getAppArgs
|
| 1366 |
+
let args := fixedParamPerms.perms[funIdx]!.buildArgs xs args
|
| 1367 |
+
return mkAppN (.const info.name (info.levelParams.map mkLevelParam)) args
|
| 1368 |
+
.none
|
| 1369 |
+
|
| 1370 |
+
-- Motives with parameters reordered, to put indices and major first,
|
| 1371 |
+
-- and (when unfolding) the result field instantiated
|
| 1372 |
+
let mut brecMotives := #[]
|
| 1373 |
+
for motive in motives, recArgInfo in recArgInfos, info in infos, funIdx in [:motives.size] do
|
| 1374 |
+
let brecMotive β forallTelescope (β inferType motive) fun ys _ => do
|
| 1375 |
+
let ys := if unfolding then ys.pop else ys
|
| 1376 |
+
let (indicesMajor, rest) := recArgInfo.pickIndicesMajor ys
|
| 1377 |
+
let motiveArg := mkAppN motive ys
|
| 1378 |
+
let motiveArg β if unfolding then
|
| 1379 |
+
let args := fixedParamPerms.perms[funIdx]!.buildArgs xs ys
|
| 1380 |
+
let fnCall := mkAppN (.const info.name (info.levelParams.map mkLevelParam)) args
|
| 1381 |
+
pure <| mkApp motiveArg fnCall
|
| 1382 |
+
else
|
| 1383 |
+
pure motiveArg
|
| 1384 |
+
mkLambdaFVars indicesMajor (β mkForallFVars rest motiveArg)
|
| 1385 |
+
brecMotives := brecMotives.push brecMotive
|
| 1386 |
+
|
| 1387 |
+
-- We need to pack these motives according to the `positions` assignment.
|
| 1388 |
+
let packedMotives β positions.mapMwith PProdN.packLambdas brecMotiveTypes brecMotives
|
| 1389 |
+
trace[Meta.FunInd] m!"packedMotives: {packedMotives}"
|
| 1390 |
+
|
| 1391 |
+
-- Now we can calculate the expected types of the minor arguments
|
| 1392 |
+
let minorTypes β inferArgumentTypesN recInfo.numMotives <|
|
| 1393 |
+
mkAppN (group.brecOn 0 0) (packedMotives ++ brecOnTargets)
|
| 1394 |
+
trace[Meta.FunInd] m!"minorTypes: {minorTypes}"
|
| 1395 |
+
-- So that we can transform them
|
| 1396 |
+
let (minors', mvars) β M2.run do
|
| 1397 |
+
let mut minors' := #[]
|
| 1398 |
+
for brecOnMinor in brecOnMinors, goal in minorTypes, numTargets in numTypeFormerTargetss do
|
| 1399 |
+
let minor' β forallTelescope goal fun xs goal => do
|
| 1400 |
+
unless xs.size β₯ numTargets do
|
| 1401 |
+
throwError ".brecOn argument has too few parameters, expected at least {numTargets}: {xs}"
|
| 1402 |
+
let targets : Array Expr := xs[*...numTargets]
|
| 1403 |
+
let genIH := xs[numTargets]!
|
| 1404 |
+
let extraParams := xs[numTargets<...*]
|
| 1405 |
+
-- open body with the same arg
|
| 1406 |
+
let body β instantiateLambda brecOnMinor targets
|
| 1407 |
+
lambdaTelescope1 body fun oldIH body => do
|
| 1408 |
+
trace[Meta.FunInd] "replacing {Expr.fvar oldIH} with {genIH}"
|
| 1409 |
+
let body β instantiateLambda body extraParams
|
| 1410 |
+
let body' β
|
| 1411 |
+
withRewrittenMotive goal (inProdLambdaLastArg (rwFun names)) fun goal' =>
|
| 1412 |
+
buildInductionBody #[oldIH, genIH.fvarId!] #[] goal' oldIH genIH.fvarId! isRecCall body
|
| 1413 |
+
if body'.containsFVar oldIH then
|
| 1414 |
+
throwError m!"Did not fully eliminate {mkFVar oldIH} from induction principle body:{indentExpr body}"
|
| 1415 |
+
mkLambdaFVars (targets.push genIH) (β mkLambdaFVars extraParams body')
|
| 1416 |
+
minors' := minors'.push minor'
|
| 1417 |
+
pure minors'
|
| 1418 |
+
trace[Meta.FunInd] "processed minors: {minors'}"
|
| 1419 |
+
|
| 1420 |
+
-- Now assemble the mutual_induct theorem
|
| 1421 |
+
-- Let-bind the transformed minors to avoid code duplication of possibly very large
|
| 1422 |
+
-- terms when we have mutual induction.
|
| 1423 |
+
let e' β withLetDecls `minor minorTypes minors' fun minors' => do
|
| 1424 |
+
let mut brecOnApps := #[]
|
| 1425 |
+
for info in infos, recArgInfo in recArgInfos, idx in [:infos.size] do
|
| 1426 |
+
-- Take care to pick the `ys` from the type, to get the variable names expected
|
| 1427 |
+
-- by the user, but use the value arity
|
| 1428 |
+
let arity β lambdaTelescope (β fixedParamPerms.perms[idx]!.instantiateLambda info.value xs) fun ys _ => pure ys.size
|
| 1429 |
+
let e β forallBoundedTelescope (β fixedParamPerms.perms[idx]!.instantiateForall info.type xs) arity fun ys _ => do
|
| 1430 |
+
let (indicesMajor, rest) := recArgInfo.pickIndicesMajor ys
|
| 1431 |
+
-- Find where in the function packing we are (TODO: abstract out)
|
| 1432 |
+
let some indIdx := positions.findIdx? (Β·.contains idx) | panic! "invalid positions"
|
| 1433 |
+
let some pos := positions.find? (Β·.contains idx) | panic! "invalid positions"
|
| 1434 |
+
let some packIdx := pos.findIdx? (Β· == idx) | panic! "invalid positions"
|
| 1435 |
+
let e := group.brecOn 0 indIdx
|
| 1436 |
+
let e := mkAppN e packedMotives
|
| 1437 |
+
let e := mkAppN e indicesMajor
|
| 1438 |
+
let e := mkAppN e minors'
|
| 1439 |
+
let e β PProdN.projM pos.size packIdx e
|
| 1440 |
+
let e := mkAppN e rest
|
| 1441 |
+
let e β mkLambdaFVars ys e
|
| 1442 |
+
trace[Meta.FunInd] "assembled call for {info.name}: {e}"
|
| 1443 |
+
pure e
|
| 1444 |
+
brecOnApps := brecOnApps.push e
|
| 1445 |
+
mkLetFVars minors' (β PProdN.mk 0 brecOnApps)
|
| 1446 |
+
let e' β abstractIndependentMVars mvars (β motives.back!.fvarId!.getDecl).index e'
|
| 1447 |
+
let e' β mkLambdaFVars motives e'
|
| 1448 |
+
|
| 1449 |
+
-- We used to pass (usedOnly := false) below in the hope that the types of the
|
| 1450 |
+
-- induction principle match the type of the function better.
|
| 1451 |
+
-- But this leads to avoidable parameters that make functional induction strictly less
|
| 1452 |
+
-- useful (e.g. when the unused parameter mentions bound variables in the users' goal)
|
| 1453 |
+
let (paramMask, e') β mkLambdaFVarsMasked xs e'
|
| 1454 |
+
let e' β instantiateMVars e'
|
| 1455 |
+
trace[Meta.FunInd] "complete body of mutual induction principle:{indentExpr e'}"
|
| 1456 |
+
pure (e', paramMask, motiveArities)
|
| 1457 |
+
|
| 1458 |
+
unless (β isTypeCorrect e') do
|
| 1459 |
+
logError m!"constructed induction principle is not type correct:{indentExpr e'}"
|
| 1460 |
+
check e'
|
| 1461 |
+
|
| 1462 |
+
let eTyp β inferType e'
|
| 1463 |
+
let eTyp β elimTypeAnnotations eTyp
|
| 1464 |
+
let eTyp β letToHave eTyp
|
| 1465 |
+
-- logInfo m!"eTyp: {eTyp}"
|
| 1466 |
+
let levelParams := (collectLevelParams {} eTyp).params
|
| 1467 |
+
-- Prune unused level parameters, preserving the original order
|
| 1468 |
+
let funUs := infos[0]!.levelParams.toArray
|
| 1469 |
+
let usMask := funUs.map (levelParams.contains Β·)
|
| 1470 |
+
let us := maskArray usMask funUs |>.toList
|
| 1471 |
+
|
| 1472 |
+
addDecl <| Declaration.thmDecl
|
| 1473 |
+
{ name := inductName, levelParams := us, type := eTyp, value := e' }
|
| 1474 |
+
|
| 1475 |
+
if names.size = 1 then
|
| 1476 |
+
let mut params := #[]
|
| 1477 |
+
for h : i in [:fixedParamPerms.perms[0]!.size] do
|
| 1478 |
+
if let some idx := fixedParamPerms.perms[0]![i] then
|
| 1479 |
+
if paramMask[idx]! then
|
| 1480 |
+
params := params.push .param
|
| 1481 |
+
else
|
| 1482 |
+
params := params.push .dropped
|
| 1483 |
+
else
|
| 1484 |
+
params := params.push .target
|
| 1485 |
+
|
| 1486 |
+
setFunIndInfo {
|
| 1487 |
+
funName := names[0]!
|
| 1488 |
+
funIndName := inductName, levelMask := usMask, params := params
|
| 1489 |
+
}
|
| 1490 |
+
|
| 1491 |
+
|
| 1492 |
+
/--
|
| 1493 |
+
Given an expression `fun x y z => body`, returns a bit mask of the functinon's arity length
|
| 1494 |
+
that has `true` whenever that parameter of the function appears as a scrutinee of a `match` in
|
| 1495 |
+
tail position. These are the parameters that are likely useful as targets of the motive
|
| 1496 |
+
of the functional cases theorem. All others become parameters or may be dropped.
|
| 1497 |
+
|
| 1498 |
+
-/
|
| 1499 |
+
partial def refinedArguments (e : Expr) : MetaM (Array Bool) := do
|
| 1500 |
+
let (_, mask) β lambdaTelescope e fun xs body =>
|
| 1501 |
+
let mask0 := Array.replicate xs.size false
|
| 1502 |
+
go xs body |>.run mask0
|
| 1503 |
+
let mut mask := mask
|
| 1504 |
+
let revDeps β getParamRevDeps e
|
| 1505 |
+
assert! revDeps.size = mask.size
|
| 1506 |
+
for i in [:mask.size] do
|
| 1507 |
+
if mask[i]! then
|
| 1508 |
+
for j in revDeps[i]! do
|
| 1509 |
+
mask := mask.set! j true
|
| 1510 |
+
pure mask
|
| 1511 |
+
where
|
| 1512 |
+
-- NB: we process open terms here.
|
| 1513 |
+
go (xs : Array Expr) (e : Expr) : StateT (Array Bool) MetaM Unit := do
|
| 1514 |
+
let e := e.consumeMData
|
| 1515 |
+
|
| 1516 |
+
if e.isLambda then
|
| 1517 |
+
-- Not strictly tail position, but simplifies the code below and should not make
|
| 1518 |
+
-- a difference in practice
|
| 1519 |
+
go xs e.bindingBody!
|
| 1520 |
+
else if e.isLet then
|
| 1521 |
+
go xs e.letBody!
|
| 1522 |
+
else
|
| 1523 |
+
e.withApp fun f args => do
|
| 1524 |
+
if f.isConst then
|
| 1525 |
+
if let some matchInfo β getMatcherInfo? f.constName! then
|
| 1526 |
+
for scrut in args[matchInfo.getFirstDiscrPos...matchInfo.getFirstAltPos] do
|
| 1527 |
+
if let some i := xs.idxOf? scrut then
|
| 1528 |
+
modify (Β·.set! i true)
|
| 1529 |
+
for alt in args[matchInfo.getFirstAltPos...matchInfo.arity] do
|
| 1530 |
+
go xs alt
|
| 1531 |
+
if f.isConstOf ``ite || f.isConstOf ``dite then
|
| 1532 |
+
for arg in args[3...5] do
|
| 1533 |
+
go xs arg
|
| 1534 |
+
if f.isConstOf ``cond then
|
| 1535 |
+
for arg in args[2...4] do
|
| 1536 |
+
go xs arg
|
| 1537 |
+
|
| 1538 |
+
/--
|
| 1539 |
+
For non-recursive (and recursive functions) functions we derive a βfunctional case splitting theoremβ. This is very similar
|
| 1540 |
+
than the functional induction theorem. It splits the goal, but does not give you inductive hyptheses.
|
| 1541 |
+
|
| 1542 |
+
For these, it is not really clear which parameters should be βtargetsβ of the motive, as there is
|
| 1543 |
+
no βfixed prefixβ to guide this decision. All? None? Some?
|
| 1544 |
+
|
| 1545 |
+
We tried none, but that did not work well. Right now it's all parameters, and it seems to work well.
|
| 1546 |
+
In the future, we might post-process the theorem (or run the code below iteratively) and remove
|
| 1547 |
+
targets that are unchanged in each case, so simplify applying the lemma when these βfixedβ parameters
|
| 1548 |
+
are not variables, to avoid having to generalize them.
|
| 1549 |
+
-/
|
| 1550 |
+
def deriveCases (unfolding : Bool) (name : Name) : MetaM Unit := do
|
| 1551 |
+
let casesName := getFunCasesName (unfolding := unfolding) name
|
| 1552 |
+
realizeConst name casesName do
|
| 1553 |
+
prependError m!"Cannot derive functional cases principle (please report this issue)" do
|
| 1554 |
+
let info β getConstInfo name
|
| 1555 |
+
let some unfoldEqnName β getUnfoldEqnFor? (nonRec := true) name
|
| 1556 |
+
| throwError "'{name}' does not have an unfold theorem nor a value"
|
| 1557 |
+
let value β do
|
| 1558 |
+
let eqInfo β getConstInfo unfoldEqnName
|
| 1559 |
+
forallTelescope eqInfo.type fun xs body => do
|
| 1560 |
+
let some (_, _, rhs) := body.eq?
|
| 1561 |
+
| throwError "Type of {unfoldEqnName} not an equality: {body}"
|
| 1562 |
+
mkLambdaFVars xs rhs
|
| 1563 |
+
let targetMask β refinedArguments value
|
| 1564 |
+
trace[Meta.FunInd] "targetMask: {targetMask}"
|
| 1565 |
+
|
| 1566 |
+
let (paramsMask, e') β lambdaTelescope value fun xs _ => do
|
| 1567 |
+
let params := maskArray (targetMask.map not) xs
|
| 1568 |
+
let targets := maskArray targetMask xs
|
| 1569 |
+
let motiveType β
|
| 1570 |
+
if unfolding then
|
| 1571 |
+
withLocalDeclD `r (β instantiateForall info.type xs) fun r =>
|
| 1572 |
+
mkForallFVars (targets.push r) (.sort 0)
|
| 1573 |
+
else
|
| 1574 |
+
mkForallFVars targets (.sort 0)
|
| 1575 |
+
-- Remove targets from local context, we want to bring them into scope after the motive
|
| 1576 |
+
-- so that the index passed to `abstractIndependentMVars` works.
|
| 1577 |
+
withErasedFVars (targets.map (Β·.fvarId!)) do
|
| 1578 |
+
withLocalDeclD `motive motiveType fun motive => do
|
| 1579 |
+
-- Bring targets freshly into scope again
|
| 1580 |
+
forallBoundedTelescope motiveType targets.size fun targets _ => do
|
| 1581 |
+
let (e', mvars) β M2.run do
|
| 1582 |
+
let args := zipMaskedArray targetMask params targets
|
| 1583 |
+
let body := value.beta args
|
| 1584 |
+
let goal := mkAppN motive targets
|
| 1585 |
+
let goal β if unfolding then
|
| 1586 |
+
pure <| mkApp goal (mkAppN (β mkConstWithLevelParams name) args)
|
| 1587 |
+
else
|
| 1588 |
+
pure goal
|
| 1589 |
+
withRewrittenMotiveArg goal (rwFun #[name]) fun goal => do
|
| 1590 |
+
-- We bring an unused FVars into scope to pass as `oldIH` and `newIH`. These do not appear anywhere
|
| 1591 |
+
-- so `buildInductionBody` should just do the right thing
|
| 1592 |
+
withLocalDeclD `fakeIH (mkConst ``Unit) fun fakeIH =>
|
| 1593 |
+
let isRecCall := fun _ => none
|
| 1594 |
+
buildInductionBody #[fakeIH.fvarId!] #[] goal fakeIH.fvarId! fakeIH.fvarId! isRecCall body
|
| 1595 |
+
let e' β mkLambdaFVars targets e'
|
| 1596 |
+
let e' β abstractIndependentMVars mvars (β motive.fvarId!.getDecl).index e'
|
| 1597 |
+
let e' β mkLambdaFVars #[motive] e'
|
| 1598 |
+
mkLambdaFVarsMasked params e'
|
| 1599 |
+
|
| 1600 |
+
mapError (f := (m!"constructed functional cases principle is not type correct:{indentExpr e'}\n{indentD Β·}")) do
|
| 1601 |
+
check e'
|
| 1602 |
+
|
| 1603 |
+
let eTyp β inferType e'
|
| 1604 |
+
let eTyp β elimTypeAnnotations eTyp
|
| 1605 |
+
let eTyp β letToHave eTyp
|
| 1606 |
+
-- logInfo m!"eTyp: {eTyp}"
|
| 1607 |
+
let levelParams := (collectLevelParams {} eTyp).params
|
| 1608 |
+
-- Prune unused level parameters, preserving the original order
|
| 1609 |
+
let funUs := info.levelParams.toArray
|
| 1610 |
+
let usMask := funUs.map (levelParams.contains Β·)
|
| 1611 |
+
let us := maskArray usMask funUs |>.toList
|
| 1612 |
+
|
| 1613 |
+
addDecl <| Declaration.thmDecl
|
| 1614 |
+
{ name := casesName, levelParams := us, type := eTyp, value := e' }
|
| 1615 |
+
|
| 1616 |
+
-- Calculate paramsKind from targetMask (length = arity) and paramsMask (length = params)
|
| 1617 |
+
let mut paramKinds := #[]
|
| 1618 |
+
let mut j := 0
|
| 1619 |
+
for isTarget in targetMask do
|
| 1620 |
+
if isTarget then
|
| 1621 |
+
paramKinds := paramKinds.push .target
|
| 1622 |
+
else
|
| 1623 |
+
assert! j < paramsMask.size
|
| 1624 |
+
if paramsMask[j]! then
|
| 1625 |
+
paramKinds := paramKinds.push .param
|
| 1626 |
+
else
|
| 1627 |
+
paramKinds := paramKinds.push .dropped
|
| 1628 |
+
j := j + 1
|
| 1629 |
+
|
| 1630 |
+
setFunIndInfo {
|
| 1631 |
+
funName := name
|
| 1632 |
+
funIndName := casesName
|
| 1633 |
+
levelMask := usMask
|
| 1634 |
+
params := paramKinds
|
| 1635 |
+
}
|
| 1636 |
+
|
| 1637 |
+
/--
|
| 1638 |
+
Given a recursively defined function `foo`, derives `foo.induct`. See the module doc for details.
|
| 1639 |
+
-/
|
| 1640 |
+
def deriveInduction (unfolding : Bool) (name : Name) : MetaM Unit := do
|
| 1641 |
+
prependError m!"Cannot derive functional induction principle (please report this issue)" do
|
| 1642 |
+
if let some eqnInfo := WF.eqnInfoExt.find? (β getEnv) name then
|
| 1643 |
+
let unaryInductName β deriveUnaryInduction unfolding eqnInfo.declNameNonRec
|
| 1644 |
+
if eqnInfo.declNames.size > 1 then
|
| 1645 |
+
projectMutualInduct unfolding eqnInfo.declNames (unpackMutualInduction unfolding eqnInfo) do
|
| 1646 |
+
-- We set the FunIndInfo on the first induction principle, which must happen inside its
|
| 1647 |
+
-- realization.
|
| 1648 |
+
if eqnInfo.argsPacker.numFuncs = 1 then
|
| 1649 |
+
setNaryFunIndInfo (unfolding := unfolding) eqnInfo.fixedParamPerms eqnInfo.declNames[0]! unaryInductName
|
| 1650 |
+
else
|
| 1651 |
+
-- (in this case, `unpackMutualInduction` already does `setNaryFunIndInfo`)
|
| 1652 |
+
let _ β unpackMutualInduction unfolding eqnInfo
|
| 1653 |
+
else if let some eqnInfo := Structural.eqnInfoExt.find? (β getEnv) name then
|
| 1654 |
+
if eqnInfo.declNames.size > 1 then
|
| 1655 |
+
projectMutualInduct unfolding eqnInfo.declNames (deriveInductionStructural unfolding eqnInfo.declNames eqnInfo.fixedParamPerms) (pure ())
|
| 1656 |
+
else
|
| 1657 |
+
let _ β deriveInductionStructural unfolding eqnInfo.declNames eqnInfo.fixedParamPerms
|
| 1658 |
+
else
|
| 1659 |
+
throwError "constant '{name}' is not structurally or well-founded recursive"
|
| 1660 |
+
|
| 1661 |
+
def isFunInductName (env : Environment) (name : Name) : Bool := Id.run do
|
| 1662 |
+
let .str p s := name | return false
|
| 1663 |
+
match s with
|
| 1664 |
+
| "induct"
|
| 1665 |
+
| "induct_unfolding" =>
|
| 1666 |
+
unless env.isSafeDefinition p do return false
|
| 1667 |
+
if let some eqnInfo := WF.eqnInfoExt.find? env p then
|
| 1668 |
+
return true
|
| 1669 |
+
if (Structural.eqnInfoExt.find? env p).isSome then return true
|
| 1670 |
+
return false
|
| 1671 |
+
| "mutual_induct"
|
| 1672 |
+
| "mutual_induct_unfolding" =>
|
| 1673 |
+
unless env.isSafeDefinition p do return false
|
| 1674 |
+
if let some eqnInfo := WF.eqnInfoExt.find? env p then
|
| 1675 |
+
if h : eqnInfo.declNames.size > 1 then
|
| 1676 |
+
return eqnInfo.declNames[0] = p
|
| 1677 |
+
if let some eqnInfo := Structural.eqnInfoExt.find? env p then
|
| 1678 |
+
if h : eqnInfo.declNames.size > 1 then
|
| 1679 |
+
return eqnInfo.declNames[0] = p
|
| 1680 |
+
return false
|
| 1681 |
+
| _ => return false
|
| 1682 |
+
|
| 1683 |
+
|
| 1684 |
+
def isFunCasesName (env : Environment) (name : Name) : Bool := Id.run do
|
| 1685 |
+
let .str p s := name | return false
|
| 1686 |
+
match s with
|
| 1687 |
+
| "fun_cases"
|
| 1688 |
+
| "fun_cases_unfolding" =>
|
| 1689 |
+
unless env.isSafeDefinition p do return false
|
| 1690 |
+
return true
|
| 1691 |
+
| _ => return false
|
| 1692 |
+
|
| 1693 |
+
builtin_initialize
|
| 1694 |
+
registerReservedNamePredicate isFunInductName
|
| 1695 |
+
registerReservedNamePredicate isFunCasesName
|
| 1696 |
+
|
| 1697 |
+
registerReservedNameAction fun name => do
|
| 1698 |
+
if isFunInductName (β getEnv) name then
|
| 1699 |
+
let .str p s := name | return false
|
| 1700 |
+
unless (β getEnv).isSafeDefinition p do return false
|
| 1701 |
+
let unfolding := s.endsWith "_unfolding"
|
| 1702 |
+
MetaM.run' <| deriveInduction unfolding p
|
| 1703 |
+
return true
|
| 1704 |
+
if isFunCasesName (β getEnv) name then
|
| 1705 |
+
let .str p s := name | return false
|
| 1706 |
+
unless (β getEnv).isSafeDefinition p do return false
|
| 1707 |
+
let unfolding := s == "fun_cases_unfolding"
|
| 1708 |
+
MetaM.run' <| deriveCases unfolding p
|
| 1709 |
+
return true
|
| 1710 |
+
return false
|
| 1711 |
+
|
| 1712 |
+
end Lean.Tactic.FunInd
|
| 1713 |
+
|
| 1714 |
+
builtin_initialize
|
| 1715 |
+
Lean.registerTraceClass `Meta.FunInd
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunIndCollect.lean
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Joachim Breitner
|
| 5 |
+
-/
|
| 6 |
+
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Meta.Tactic.Util
|
| 9 |
+
import Lean.Meta.Tactic.FunIndInfo
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
Support for collecting function calls that could be used for `fun_induction` or `fun_cases`.
|
| 13 |
+
Used by `fun_induction foo`, and the `Calls` structure is also used by `try?`.
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
namespace Lean.Meta.FunInd
|
| 17 |
+
|
| 18 |
+
structure Call where
|
| 19 |
+
/- The full function call -/
|
| 20 |
+
expr : Expr
|
| 21 |
+
/- Used to avoid adding calls that differ only in dropped arguments -/
|
| 22 |
+
relevantArgs : Expr
|
| 23 |
+
deriving Hashable, BEq
|
| 24 |
+
|
| 25 |
+
structure SeenCalls where
|
| 26 |
+
/-- the full calls -/
|
| 27 |
+
calls : Array Expr
|
| 28 |
+
/-- only function name and relevant arguments -/
|
| 29 |
+
seen : Std.HashSet (Name Γ Array Expr)
|
| 30 |
+
|
| 31 |
+
instance : EmptyCollection SeenCalls where
|
| 32 |
+
emptyCollection := β¨#[], {}β©
|
| 33 |
+
|
| 34 |
+
def SeenCalls.isEmpty (sc : SeenCalls) : Bool :=
|
| 35 |
+
sc.calls.isEmpty
|
| 36 |
+
|
| 37 |
+
def SeenCalls.push (e : Expr) (funIndInfo : FunIndInfo) (args : Array Expr) (calls : SeenCalls) :
|
| 38 |
+
MetaM SeenCalls := do
|
| 39 |
+
if funIndInfo.params.size != args.size then return calls
|
| 40 |
+
let mut keys := #[]
|
| 41 |
+
for arg in args, kind in funIndInfo.params do
|
| 42 |
+
if kind matches .target then
|
| 43 |
+
if !arg.isFVar then return calls
|
| 44 |
+
unless kind matches .dropped do
|
| 45 |
+
keys := keys.push arg
|
| 46 |
+
let key := (funIndInfo.funName, keys)
|
| 47 |
+
if calls.seen.contains key then return calls
|
| 48 |
+
return { calls := calls.calls.push e, seen := calls.seen.insert key }
|
| 49 |
+
|
| 50 |
+
/--
|
| 51 |
+
Which functions have exactly one candidate application. Used by `try?` to determine whether
|
| 52 |
+
we can use `fun_induction foo` or need `fun_induction foo x y z`.
|
| 53 |
+
-/
|
| 54 |
+
def SeenCalls.uniques (calls : SeenCalls) : NameSet := Id.run do
|
| 55 |
+
let mut seen : NameSet := {}
|
| 56 |
+
let mut seenTwice : NameSet := {}
|
| 57 |
+
for (n, _) in calls.seen do
|
| 58 |
+
unless seenTwice.contains n do
|
| 59 |
+
if seen.contains n then
|
| 60 |
+
seenTwice := seenTwice.insert n
|
| 61 |
+
else
|
| 62 |
+
seen := seen.insert n
|
| 63 |
+
return seen.filter (! seenTwice.contains Β·)
|
| 64 |
+
|
| 65 |
+
namespace Collector
|
| 66 |
+
|
| 67 |
+
abbrev M := ReaderT FunIndInfo <| StateRefT SeenCalls MetaM
|
| 68 |
+
|
| 69 |
+
def saveFunInd (e : Expr) (funIndInfo : FunIndInfo) (args : Array Expr) : M Unit := do
|
| 70 |
+
set (β (β get).push e funIndInfo args)
|
| 71 |
+
|
| 72 |
+
def visitApp (e : Expr) (funIndInfo : FunIndInfo) (args : Array Expr) : M Unit := do
|
| 73 |
+
saveFunInd e funIndInfo args
|
| 74 |
+
|
| 75 |
+
unsafe abbrev Cache := PtrSet Expr
|
| 76 |
+
|
| 77 |
+
unsafe def visit (e : Expr) : StateRefT Cache M Unit := do
|
| 78 |
+
unless (β get).contains e do
|
| 79 |
+
modify fun s => s.insert e
|
| 80 |
+
match e with
|
| 81 |
+
| .const _ _ => pure ()
|
| 82 |
+
| .forallE _ d b _ => visit d; visit b
|
| 83 |
+
| .lam _ d b _ => visit d; visit b
|
| 84 |
+
| .mdata _ b => visit b
|
| 85 |
+
| .letE _ t v b _ => visit t; visit v; visit b
|
| 86 |
+
| .app .. => e.withApp fun f args => do
|
| 87 |
+
if let .const declName _ := f then
|
| 88 |
+
if declName = (β read).funName then
|
| 89 |
+
unless e.hasLooseBVars do -- TODO: We can allow them in `.dropped` arguments
|
| 90 |
+
visitApp e (β read) args
|
| 91 |
+
else
|
| 92 |
+
visit f
|
| 93 |
+
args.forM visit
|
| 94 |
+
| .proj _ _ b => visit b
|
| 95 |
+
| _ => return ()
|
| 96 |
+
|
| 97 |
+
unsafe def main (needle : FunIndInfo) (mvarId : MVarId) : MetaM (Array Expr) := mvarId.withContext do
|
| 98 |
+
let (_, s) β go |>.run mkPtrSet |>.run needle |>.run {}
|
| 99 |
+
return s.calls
|
| 100 |
+
where
|
| 101 |
+
go : StateRefT Cache M Unit := do
|
| 102 |
+
for localDecl in (β getLCtx) do
|
| 103 |
+
unless localDecl.isAuxDecl do
|
| 104 |
+
if let some val := localDecl.value? then
|
| 105 |
+
visit val
|
| 106 |
+
else
|
| 107 |
+
visit localDecl.type
|
| 108 |
+
visit (β mvarId.getType)
|
| 109 |
+
|
| 110 |
+
end Collector
|
| 111 |
+
|
| 112 |
+
def collect (needle : FunIndInfo) (mvarId : MVarId) : MetaM (Array Expr) := do
|
| 113 |
+
unsafe Collector.main needle mvarId
|
| 114 |
+
|
| 115 |
+
end Lean.Meta.FunInd
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunIndInfo.lean
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Joachim Breitner
|
| 5 |
+
-/
|
| 6 |
+
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Meta.Basic
|
| 9 |
+
import Lean.ScopedEnvExtension
|
| 10 |
+
import Lean.ReservedNameAction
|
| 11 |
+
|
| 12 |
+
/-!
|
| 13 |
+
This module defines the data structure and environment extension to remember how to map the
|
| 14 |
+
function's arguments to the functional induction principle's arguments.
|
| 15 |
+
Also used for functional cases.
|
| 16 |
+
-/
|
| 17 |
+
|
| 18 |
+
namespace Lean.Meta
|
| 19 |
+
|
| 20 |
+
inductive FunIndParamKind where
|
| 21 |
+
| dropped
|
| 22 |
+
| param
|
| 23 |
+
| target
|
| 24 |
+
deriving BEq, Repr, Inhabited
|
| 25 |
+
|
| 26 |
+
/--
|
| 27 |
+
A `FunIndInfo` indicates how a function's arguments map to the arguments of the functional induction
|
| 28 |
+
(resp. cases) theorem.
|
| 29 |
+
|
| 30 |
+
The size of `params` also indicates the arity of the function.
|
| 31 |
+
-/
|
| 32 |
+
structure FunIndInfo where
|
| 33 |
+
funName : Name
|
| 34 |
+
funIndName : Name
|
| 35 |
+
/--
|
| 36 |
+
`true` means that the corresponding level parameter of the function is also a level param
|
| 37 |
+
of the induction principle.
|
| 38 |
+
-/
|
| 39 |
+
levelMask : Array Bool
|
| 40 |
+
params : Array FunIndParamKind
|
| 41 |
+
deriving Inhabited, Repr
|
| 42 |
+
|
| 43 |
+
builtin_initialize funIndInfoExt : MapDeclarationExtension FunIndInfo β mkMapDeclarationExtension
|
| 44 |
+
|
| 45 |
+
def getFunInductName (declName : Name) (unfolding : Bool := false) : Name :=
|
| 46 |
+
if unfolding then
|
| 47 |
+
declName ++ `induct_unfolding
|
| 48 |
+
else
|
| 49 |
+
declName ++ `induct
|
| 50 |
+
|
| 51 |
+
def getFunCasesName (declName : Name) (unfolding : Bool := false) : Name :=
|
| 52 |
+
if unfolding then
|
| 53 |
+
declName ++ `fun_cases_unfolding
|
| 54 |
+
else
|
| 55 |
+
declName ++ `fun_cases
|
| 56 |
+
|
| 57 |
+
def getMutualInductName (declName : Name) (unfolding : Bool := false) : Name :=
|
| 58 |
+
if unfolding then
|
| 59 |
+
declName ++ `mutual_induct_unfolding
|
| 60 |
+
else
|
| 61 |
+
declName ++ `mutual_induct
|
| 62 |
+
|
| 63 |
+
def getFunInduct? (unfolding : Bool) (cases : Bool) (declName : Name) : CoreM (Option Name) := do
|
| 64 |
+
let .defnInfo _ β getConstInfo declName | return none
|
| 65 |
+
try
|
| 66 |
+
let thmName := if cases then
|
| 67 |
+
getFunCasesName (unfolding := unfolding) declName
|
| 68 |
+
else
|
| 69 |
+
getFunInductName (unfolding := unfolding) declName
|
| 70 |
+
let result β realizeGlobalConstNoOverloadCore thmName
|
| 71 |
+
return some result
|
| 72 |
+
catch _ =>
|
| 73 |
+
return none
|
| 74 |
+
|
| 75 |
+
def setFunIndInfo (funIndInfo : FunIndInfo) : CoreM Unit := do
|
| 76 |
+
assert! !(funIndInfoExt.contains (β getEnv) funIndInfo.funIndName)
|
| 77 |
+
modifyEnv fun env => funIndInfoExt.insert env funIndInfo.funIndName funIndInfo
|
| 78 |
+
|
| 79 |
+
def getFunIndInfoForInduct? (inductName : Name) : CoreM (Option FunIndInfo) := do
|
| 80 |
+
return funIndInfoExt.find? (β getEnv) inductName
|
| 81 |
+
|
| 82 |
+
def getFunIndInfo? (cases : Bool) (unfolding : Bool) (funName : Name) : CoreM (Option FunIndInfo) := do
|
| 83 |
+
let some inductName β getFunInduct? (cases := cases) (unfolding := unfolding) funName | return none
|
| 84 |
+
getFunIndInfoForInduct? inductName
|
| 85 |
+
|
| 86 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Generalize.lean
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.KAbstract
|
| 8 |
+
import Lean.Meta.Tactic.Util
|
| 9 |
+
import Lean.Meta.Tactic.Intro
|
| 10 |
+
import Lean.Meta.Tactic.FVarSubst
|
| 11 |
+
import Lean.Meta.Tactic.Revert
|
| 12 |
+
|
| 13 |
+
namespace Lean.Meta
|
| 14 |
+
|
| 15 |
+
/-- The `generalize` tactic takes arguments of the form `h : e = x` -/
|
| 16 |
+
structure GeneralizeArg where
|
| 17 |
+
expr : Expr
|
| 18 |
+
xName? : Option Name := none
|
| 19 |
+
hName? : Option Name := none
|
| 20 |
+
deriving Inhabited
|
| 21 |
+
|
| 22 |
+
/--
|
| 23 |
+
Telescopic `generalize` tactic. It can simultaneously generalize many terms.
|
| 24 |
+
It uses `kabstract` to occurrences of the terms that need to be generalized.
|
| 25 |
+
-/
|
| 26 |
+
private partial def generalizeCore (mvarId : MVarId) (args : Array GeneralizeArg)
|
| 27 |
+
(transparency : TransparencyMode) : MetaM (Array FVarId Γ MVarId) :=
|
| 28 |
+
mvarId.withContext do
|
| 29 |
+
mvarId.checkNotAssigned `generalize
|
| 30 |
+
let tag β mvarId.getTag
|
| 31 |
+
let target β instantiateMVars (β mvarId.getType)
|
| 32 |
+
let rec go (i : Nat) : MetaM Expr := do
|
| 33 |
+
if _h : i < args.size then
|
| 34 |
+
let arg := args[i]
|
| 35 |
+
let e β instantiateMVars arg.expr
|
| 36 |
+
let eType β instantiateMVars (β inferType e)
|
| 37 |
+
let type β go (i+1)
|
| 38 |
+
let xName β if let some xName := arg.xName? then pure xName else mkFreshUserName `x
|
| 39 |
+
return Lean.mkForall xName BinderInfo.default eType
|
| 40 |
+
(β withTransparency transparency <| kabstract type e)
|
| 41 |
+
else
|
| 42 |
+
return target
|
| 43 |
+
let targetNew β go 0
|
| 44 |
+
unless (β isTypeCorrect targetNew) do
|
| 45 |
+
throwTacticEx `generalize mvarId m!"result is not type correct{indentExpr targetNew}"
|
| 46 |
+
let es := args.map (Β·.expr)
|
| 47 |
+
if !args.any fun arg => arg.hName?.isSome then
|
| 48 |
+
let mvarNew β mkFreshExprSyntheticOpaqueMVar targetNew tag
|
| 49 |
+
mvarId.assign (mkAppN mvarNew es)
|
| 50 |
+
mvarNew.mvarId!.introNP args.size
|
| 51 |
+
else
|
| 52 |
+
let (rfls, targetNew) β forallBoundedTelescope targetNew args.size fun xs type => do
|
| 53 |
+
let rec go' (i : Nat) : MetaM (List Expr Γ Expr) := do
|
| 54 |
+
if _h : i < xs.size then
|
| 55 |
+
let arg := args[i]!
|
| 56 |
+
if let some hName := arg.hName? then
|
| 57 |
+
let xType β inferType xs[i]
|
| 58 |
+
let e β instantiateMVars arg.expr
|
| 59 |
+
let eType β instantiateMVars (β inferType e)
|
| 60 |
+
let (hType, r) β if (β isDefEq xType eType) then
|
| 61 |
+
pure (β mkEq e xs[i], β mkEqRefl e)
|
| 62 |
+
else
|
| 63 |
+
pure (β mkHEq e xs[i], β mkHEqRefl e)
|
| 64 |
+
let (rs, type) β go' (i+1)
|
| 65 |
+
return (r :: rs, mkForall hName BinderInfo.default hType type)
|
| 66 |
+
else
|
| 67 |
+
go' (i+1)
|
| 68 |
+
else
|
| 69 |
+
return ([], type)
|
| 70 |
+
let (rfls, type) β go' 0
|
| 71 |
+
return (rfls, β mkForallFVars xs type)
|
| 72 |
+
let mvarNew β mkFreshExprSyntheticOpaqueMVar targetNew tag
|
| 73 |
+
mvarId.assign (mkAppN (mkAppN mvarNew es) rfls.toArray)
|
| 74 |
+
mvarNew.mvarId!.introNP (args.size + rfls.length)
|
| 75 |
+
|
| 76 |
+
/-
|
| 77 |
+
Remark: we use `TransparencyMode.instances` as the default setting at `generalize`
|
| 78 |
+
and `generalizeHyp` to avoid excessive resource usage.
|
| 79 |
+
|
| 80 |
+
**Motivation:**
|
| 81 |
+
The `kabstract e p` operation is widely used, for instance, in the `generalize` tactic.
|
| 82 |
+
It operates by taking an expression `e` and a pattern (i.e., an expression containing metavariables)
|
| 83 |
+
and employs keyed-matching to identify and abstract instances of `p` within `e`.
|
| 84 |
+
For example, if `e` is `a + (2 * (b + c))` and `p` is `2 * ?m`, the resultant expression
|
| 85 |
+
would be `a + #0`, where `#0` represents a loose bound variable.
|
| 86 |
+
|
| 87 |
+
This matching process is not merely syntactic; it also considers reduction. It's impractical
|
| 88 |
+
to attempt matching each sub-term with `p`; therefore, only sub-terms sharing the same "root"
|
| 89 |
+
symbol are evaluated. For instance, with the pattern `2 * ?m`, only sub-terms with the
|
| 90 |
+
root `*` are considered. Matching is executed using the definitionally equality test
|
| 91 |
+
(i.e., `isDefEq`).
|
| 92 |
+
|
| 93 |
+
The `generalize` tactic employs `kabstract` and defaults to standard reducibility.
|
| 94 |
+
Hence, the `isDefEq` operations invoked by `kabstract` can become highly resource-intensive
|
| 95 |
+
and potentially trigger "max recursion depth reached" errors, as observed in issue #3524.
|
| 96 |
+
This issue was isolated by @**Kim Morrison** with the following example:
|
| 97 |
+
```
|
| 98 |
+
example (a : Nat) : ((2 ^ 7) + a) - 2 ^ 7 = 0 := by
|
| 99 |
+
generalize 0 - 0 = x
|
| 100 |
+
```
|
| 101 |
+
In this scenario, `kabstract` triggers a "max recursion depth reached" error while
|
| 102 |
+
testing whether `((2 ^ 7) + a) - 2 ^ 7` is definitionally equal to `0 - 0`.
|
| 103 |
+
Note that the term `((2 ^ 7) + a) - 2 ^ 7` is not ground.
|
| 104 |
+
We believe most users find the error message to be uninformative and unexpected.
|
| 105 |
+
To fix this issue, we decided to use `TransparencyMode.instances` as the default setting.
|
| 106 |
+
|
| 107 |
+
Kyle Miller has performed the following analysis on the potential impact of the
|
| 108 |
+
changes on Mathlib (2024-03-02).
|
| 109 |
+
|
| 110 |
+
There seem to be just 130 cases of generalize in Mathlib, and after looking through a
|
| 111 |
+
good number of them, they seem to come in just two types:
|
| 112 |
+
|
| 113 |
+
- Ones where it looks like reducible+instance transparency should work, where in
|
| 114 |
+
particular there is nothing obvious being reduced, and
|
| 115 |
+
- Ones that don't make use of the `kabstract` feature at all (it's being used like a
|
| 116 |
+
`have` that introduces an equality for rewriting).
|
| 117 |
+
|
| 118 |
+
That wasn't a systematic review of generalize though. It's possible changing the
|
| 119 |
+
transparency settings would break things, but in my opinion it would be better
|
| 120 |
+
if generalize weren't used for unfolding things.
|
| 121 |
+
-/
|
| 122 |
+
|
| 123 |
+
@[inherit_doc generalizeCore]
|
| 124 |
+
def _root_.Lean.MVarId.generalize (mvarId : MVarId) (args : Array GeneralizeArg)
|
| 125 |
+
(transparency := TransparencyMode.instances) : MetaM (Array FVarId Γ MVarId) :=
|
| 126 |
+
generalizeCore mvarId args transparency
|
| 127 |
+
|
| 128 |
+
/--
|
| 129 |
+
Extension of `generalize` to support generalizing within specified hypotheses.
|
| 130 |
+
The `hyps` array contains the list of hypotheses within which to look for occurrences
|
| 131 |
+
of the generalizing expressions.
|
| 132 |
+
-/
|
| 133 |
+
def _root_.Lean.MVarId.generalizeHyp (mvarId : MVarId) (args : Array GeneralizeArg) (hyps : Array FVarId := #[])
|
| 134 |
+
(fvarSubst : FVarSubst := {}) (transparency := TransparencyMode.instances) : MetaM (FVarSubst Γ Array FVarId Γ MVarId) := do
|
| 135 |
+
if hyps.isEmpty then
|
| 136 |
+
-- trivial case
|
| 137 |
+
return (fvarSubst, β mvarId.generalize args transparency)
|
| 138 |
+
let args β args.mapM fun arg => return { arg with expr := β instantiateMVars arg.expr }
|
| 139 |
+
let hyps β hyps.filterM fun h => do
|
| 140 |
+
let type β instantiateMVars (β h.getType)
|
| 141 |
+
args.anyM fun arg => return (β withTransparency transparency <| kabstract type arg.expr).hasLooseBVars
|
| 142 |
+
let (reverted, mvarId) β mvarId.revert hyps true
|
| 143 |
+
let (newVars, mvarId) β mvarId.generalize args transparency
|
| 144 |
+
let (reintros, mvarId) β mvarId.introNP reverted.size
|
| 145 |
+
let fvarSubst := Id.run do
|
| 146 |
+
let mut subst : FVarSubst := fvarSubst
|
| 147 |
+
for h in reverted, reintro in reintros do
|
| 148 |
+
subst := subst.insert h (mkFVar reintro)
|
| 149 |
+
pure subst
|
| 150 |
+
return (fvarSubst, newVars, mvarId)
|
| 151 |
+
|
| 152 |
+
end Lean.Meta
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind.lean
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.Attr
|
| 8 |
+
import Lean.Meta.Tactic.Grind.RevertAll
|
| 9 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 10 |
+
import Lean.Meta.Tactic.Grind.Util
|
| 11 |
+
import Lean.Meta.Tactic.Grind.Cases
|
| 12 |
+
import Lean.Meta.Tactic.Grind.Injection
|
| 13 |
+
import Lean.Meta.Tactic.Grind.Core
|
| 14 |
+
import Lean.Meta.Tactic.Grind.Canon
|
| 15 |
+
import Lean.Meta.Tactic.Grind.MarkNestedProofs
|
| 16 |
+
import Lean.Meta.Tactic.Grind.Inv
|
| 17 |
+
import Lean.Meta.Tactic.Grind.Proof
|
| 18 |
+
import Lean.Meta.Tactic.Grind.Propagate
|
| 19 |
+
import Lean.Meta.Tactic.Grind.PP
|
| 20 |
+
import Lean.Meta.Tactic.Grind.Simp
|
| 21 |
+
import Lean.Meta.Tactic.Grind.Ctor
|
| 22 |
+
import Lean.Meta.Tactic.Grind.Parser
|
| 23 |
+
import Lean.Meta.Tactic.Grind.EMatchTheorem
|
| 24 |
+
import Lean.Meta.Tactic.Grind.EMatch
|
| 25 |
+
import Lean.Meta.Tactic.Grind.Main
|
| 26 |
+
import Lean.Meta.Tactic.Grind.CasesMatch
|
| 27 |
+
import Lean.Meta.Tactic.Grind.Arith
|
| 28 |
+
import Lean.Meta.Tactic.Grind.Ext
|
| 29 |
+
import Lean.Meta.Tactic.Grind.MatchCond
|
| 30 |
+
import Lean.Meta.Tactic.Grind.MatchDiscrOnly
|
| 31 |
+
import Lean.Meta.Tactic.Grind.Diseq
|
| 32 |
+
import Lean.Meta.Tactic.Grind.MBTC
|
| 33 |
+
import Lean.Meta.Tactic.Grind.Lookahead
|
| 34 |
+
import Lean.Meta.Tactic.Grind.LawfulEqCmp
|
| 35 |
+
import Lean.Meta.Tactic.Grind.ReflCmp
|
| 36 |
+
|
| 37 |
+
namespace Lean
|
| 38 |
+
|
| 39 |
+
/-! Trace options for `grind` users -/
|
| 40 |
+
builtin_initialize registerTraceClass `grind
|
| 41 |
+
builtin_initialize registerTraceClass `grind.trace
|
| 42 |
+
builtin_initialize registerTraceClass `grind.assert
|
| 43 |
+
builtin_initialize registerTraceClass `grind.eqc
|
| 44 |
+
builtin_initialize registerTraceClass `grind.internalize
|
| 45 |
+
builtin_initialize registerTraceClass `grind.ematch
|
| 46 |
+
builtin_initialize registerTraceClass `grind.ematch.pattern
|
| 47 |
+
builtin_initialize registerTraceClass `grind.ematch.instance
|
| 48 |
+
builtin_initialize registerTraceClass `grind.ematch.instance.assignment
|
| 49 |
+
builtin_initialize registerTraceClass `grind.eqResolution
|
| 50 |
+
builtin_initialize registerTraceClass `grind.issues
|
| 51 |
+
builtin_initialize registerTraceClass `grind.simp
|
| 52 |
+
builtin_initialize registerTraceClass `grind.split
|
| 53 |
+
builtin_initialize registerTraceClass `grind.split.candidate
|
| 54 |
+
builtin_initialize registerTraceClass `grind.split.resolved
|
| 55 |
+
builtin_initialize registerTraceClass `grind.beta
|
| 56 |
+
builtin_initialize registerTraceClass `grind.mbtc
|
| 57 |
+
builtin_initialize registerTraceClass `grind.ext
|
| 58 |
+
builtin_initialize registerTraceClass `grind.ext.candidate
|
| 59 |
+
builtin_initialize registerTraceClass `grind.lookahead
|
| 60 |
+
builtin_initialize registerTraceClass `grind.lookahead.add (inherited := true)
|
| 61 |
+
builtin_initialize registerTraceClass `grind.lookahead.try (inherited := true)
|
| 62 |
+
builtin_initialize registerTraceClass `grind.lookahead.assert (inherited := true)
|
| 63 |
+
|
| 64 |
+
/-! Trace options for `grind` developers -/
|
| 65 |
+
builtin_initialize registerTraceClass `grind.debug
|
| 66 |
+
builtin_initialize registerTraceClass `grind.debug.proofs
|
| 67 |
+
builtin_initialize registerTraceClass `grind.debug.congr
|
| 68 |
+
builtin_initialize registerTraceClass `grind.debug.proof
|
| 69 |
+
builtin_initialize registerTraceClass `grind.debug.proj
|
| 70 |
+
builtin_initialize registerTraceClass `grind.debug.parent
|
| 71 |
+
builtin_initialize registerTraceClass `grind.debug.final
|
| 72 |
+
builtin_initialize registerTraceClass `grind.debug.forallPropagator
|
| 73 |
+
builtin_initialize registerTraceClass `grind.debug.split
|
| 74 |
+
builtin_initialize registerTraceClass `grind.debug.canon
|
| 75 |
+
builtin_initialize registerTraceClass `grind.debug.ematch.activate
|
| 76 |
+
builtin_initialize registerTraceClass `grind.debug.ematch.pattern
|
| 77 |
+
builtin_initialize registerTraceClass `grind.debug.beta
|
| 78 |
+
builtin_initialize registerTraceClass `grind.debug.internalize
|
| 79 |
+
builtin_initialize registerTraceClass `grind.debug.matchCond
|
| 80 |
+
builtin_initialize registerTraceClass `grind.debug.matchCond.lambda
|
| 81 |
+
builtin_initialize registerTraceClass `grind.debug.matchCond.proveFalse
|
| 82 |
+
builtin_initialize registerTraceClass `grind.debug.mbtc
|
| 83 |
+
builtin_initialize registerTraceClass `grind.debug.ematch
|
| 84 |
+
builtin_initialize registerTraceClass `grind.debug.proveEq
|
| 85 |
+
builtin_initialize registerTraceClass `grind.debug.pushNewFact
|
| 86 |
+
builtin_initialize registerTraceClass `grind.debug.appMap
|
| 87 |
+
builtin_initialize registerTraceClass `grind.debug.ext
|
| 88 |
+
|
| 89 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/AlphaShareCommon.lean
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.ExprPtr
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.Grind
|
| 10 |
+
|
| 11 |
+
private def hashChild (e : Expr) : UInt64 :=
|
| 12 |
+
match e with
|
| 13 |
+
| .bvar .. | .mvar .. | .const .. | .fvar .. | .sort .. | .lit .. =>
|
| 14 |
+
hash e
|
| 15 |
+
| .app .. | .letE .. | .forallE .. | .lam .. | .mdata .. | .proj .. =>
|
| 16 |
+
hashPtrExpr e
|
| 17 |
+
|
| 18 |
+
private def alphaHash (e : Expr) : UInt64 :=
|
| 19 |
+
match e with
|
| 20 |
+
| .bvar .. | .mvar .. | .const .. | .fvar .. | .sort .. | .lit .. =>
|
| 21 |
+
hash e
|
| 22 |
+
| .app f a => mixHash (hashChild f) (hashChild a)
|
| 23 |
+
| .letE _ _ v b _ => mixHash (hashChild v) (hashChild b)
|
| 24 |
+
| .forallE _ d b _ | .lam _ d b _ => mixHash (hashChild d) (hashChild b)
|
| 25 |
+
| .mdata _ b => mixHash 13 (hashChild b)
|
| 26 |
+
| .proj n i b => mixHash (mixHash (hash n) (hash i)) (hashChild b)
|
| 27 |
+
|
| 28 |
+
private def alphaEq (eβ eβ : Expr) : Bool := Id.run do
|
| 29 |
+
match eβ with
|
| 30 |
+
| .bvar .. | .mvar .. | .const .. | .fvar .. | .sort .. | .lit .. =>
|
| 31 |
+
eβ == eβ
|
| 32 |
+
| .app fβ aβ =>
|
| 33 |
+
let .app fβ aβ := eβ | false
|
| 34 |
+
isSameExpr fβ fβ && isSameExpr aβ aβ
|
| 35 |
+
| .letE _ _ vβ bβ _ =>
|
| 36 |
+
let .letE _ _ vβ bβ _ := eβ | false
|
| 37 |
+
isSameExpr vβ vβ && isSameExpr bβ bβ
|
| 38 |
+
| .forallE _ dβ bβ _ =>
|
| 39 |
+
let .forallE _ dβ bβ _ := eβ | false
|
| 40 |
+
isSameExpr dβ dβ && isSameExpr bβ bβ
|
| 41 |
+
| .lam _ dβ bβ _ =>
|
| 42 |
+
let .lam _ dβ bβ _ := eβ | false
|
| 43 |
+
isSameExpr dβ dβ && isSameExpr bβ bβ
|
| 44 |
+
| .mdata dβ bβ =>
|
| 45 |
+
let .mdata dβ bβ := eβ | false
|
| 46 |
+
return isSameExpr bβ bβ && dβ == dβ
|
| 47 |
+
| .proj nβ iβ bβ =>
|
| 48 |
+
let .proj nβ iβ bβ := eβ | false
|
| 49 |
+
nβ == nβ && iβ == iβ && isSameExpr bβ bβ
|
| 50 |
+
|
| 51 |
+
structure AlphaKey where
|
| 52 |
+
expr : Expr
|
| 53 |
+
|
| 54 |
+
instance : Hashable AlphaKey where
|
| 55 |
+
hash k := alphaHash k.expr
|
| 56 |
+
|
| 57 |
+
instance : BEq AlphaKey where
|
| 58 |
+
beq kβ kβ := alphaEq kβ.expr kβ.expr
|
| 59 |
+
|
| 60 |
+
structure AlphaShareCommon.State where
|
| 61 |
+
map : PHashMap ExprPtr Expr := {}
|
| 62 |
+
set : PHashSet AlphaKey := {}
|
| 63 |
+
|
| 64 |
+
abbrev AlphaShareCommonM := StateM AlphaShareCommon.State
|
| 65 |
+
|
| 66 |
+
private def save (e : Expr) (r : Expr) : AlphaShareCommonM Expr := do
|
| 67 |
+
if let some r := (β get).set.find? { expr := r } then
|
| 68 |
+
let r := r.expr
|
| 69 |
+
modify fun { set, map } => {
|
| 70 |
+
set
|
| 71 |
+
map := map.insert { expr := e } r
|
| 72 |
+
}
|
| 73 |
+
return r
|
| 74 |
+
else
|
| 75 |
+
modify fun { set, map } => {
|
| 76 |
+
set := set.insert { expr := r }
|
| 77 |
+
map := map.insert { expr := e } r |>.insert { expr := r } r
|
| 78 |
+
}
|
| 79 |
+
return r
|
| 80 |
+
|
| 81 |
+
private abbrev visit (e : Expr) (k : AlphaShareCommonM Expr) : AlphaShareCommonM Expr := do
|
| 82 |
+
if let some r := (β get).map.find? { expr := e } then
|
| 83 |
+
return r
|
| 84 |
+
else
|
| 85 |
+
save e (β k)
|
| 86 |
+
|
| 87 |
+
/-- Similar to `shareCommon`, but handles alpha-equivalence. -/
|
| 88 |
+
def shareCommonAlpha (e : Expr) : AlphaShareCommonM Expr :=
|
| 89 |
+
go e
|
| 90 |
+
where
|
| 91 |
+
go (e : Expr) : AlphaShareCommonM Expr := do
|
| 92 |
+
match e with
|
| 93 |
+
| .bvar .. | .mvar .. | .const .. | .fvar .. | .sort .. | .lit .. =>
|
| 94 |
+
if let some r := (β get).set.find? { expr := e } then
|
| 95 |
+
return r.expr
|
| 96 |
+
else
|
| 97 |
+
modify fun { set, map } => { set := set.insert { expr := e }, map }
|
| 98 |
+
return e
|
| 99 |
+
| .app f a =>
|
| 100 |
+
visit e (return mkApp (β go f) (β go a))
|
| 101 |
+
| .letE n t v b nd =>
|
| 102 |
+
visit e (return mkLet n t (β go v) (β go b) nd)
|
| 103 |
+
| .forallE n d b bi =>
|
| 104 |
+
visit e (return mkForall n bi (β go d) (β go b))
|
| 105 |
+
| .lam n d b bi =>
|
| 106 |
+
visit e (return mkLambda n bi (β go d) (β go b))
|
| 107 |
+
| .mdata d b =>
|
| 108 |
+
visit e (return mkMData d (β go b))
|
| 109 |
+
| .proj n i b =>
|
| 110 |
+
visit e (return mkProj n i (β go b))
|
| 111 |
+
|
| 112 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith.lean
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.Arith.Util
|
| 8 |
+
import Lean.Meta.Tactic.Grind.Arith.ProofUtil
|
| 9 |
+
import Lean.Meta.Tactic.Grind.Arith.Types
|
| 10 |
+
import Lean.Meta.Tactic.Grind.Arith.Main
|
| 11 |
+
import Lean.Meta.Tactic.Grind.Arith.Offset
|
| 12 |
+
import Lean.Meta.Tactic.Grind.Arith.Cutsat
|
| 13 |
+
import Lean.Meta.Tactic.Grind.Arith.CommRing
|
| 14 |
+
import Lean.Meta.Tactic.Grind.Arith.Linear
|
| 15 |
+
import Lean.Meta.Tactic.Grind.Arith.Simproc
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Attr.lean
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.EMatchTheorem
|
| 8 |
+
import Lean.Meta.Tactic.Grind.Cases
|
| 9 |
+
import Lean.Meta.Tactic.Grind.ExtAttr
|
| 10 |
+
|
| 11 |
+
namespace Lean.Meta.Grind
|
| 12 |
+
|
| 13 |
+
inductive AttrKind where
|
| 14 |
+
| ematch (k : EMatchTheoremKind)
|
| 15 |
+
| cases (eager : Bool)
|
| 16 |
+
| intro
|
| 17 |
+
| infer
|
| 18 |
+
| ext
|
| 19 |
+
| symbol (prio : Nat)
|
| 20 |
+
|
| 21 |
+
/-- Return theorem kind for `stx` of the form `Attr.grindThmMod` -/
|
| 22 |
+
def getAttrKindCore (stx : Syntax) : CoreM AttrKind := do
|
| 23 |
+
match stx with
|
| 24 |
+
| `(Parser.Attr.grindMod|=) => return .ematch (.eqLhs false)
|
| 25 |
+
| `(Parser.Attr.grindMod|= gen) => return .ematch (.eqLhs true)
|
| 26 |
+
| `(Parser.Attr.grindMod|β) => return .ematch .fwd
|
| 27 |
+
| `(Parser.Attr.grindMod|β) => return .ematch (.bwd false)
|
| 28 |
+
| `(Parser.Attr.grindMod|β gen) => return .ematch (.bwd true)
|
| 29 |
+
| `(Parser.Attr.grindMod|=_) => return .ematch (.eqRhs false)
|
| 30 |
+
| `(Parser.Attr.grindMod|=_ gen) => return .ematch (.eqRhs true)
|
| 31 |
+
| `(Parser.Attr.grindMod|_=_) => return .ematch (.eqBoth false)
|
| 32 |
+
| `(Parser.Attr.grindMod|_=_ gen) => return .ematch (.eqBoth true)
|
| 33 |
+
| `(Parser.Attr.grindMod|β=) => return .ematch .eqBwd
|
| 34 |
+
| `(Parser.Attr.grindMod|=>) => return .ematch .leftRight
|
| 35 |
+
| `(Parser.Attr.grindMod|<=) => return .ematch .rightLeft
|
| 36 |
+
| `(Parser.Attr.grindMod|usr) => return .ematch .user
|
| 37 |
+
| `(Parser.Attr.grindMod|gen) => return .ematch (.default true)
|
| 38 |
+
| `(Parser.Attr.grindMod|cases) => return .cases false
|
| 39 |
+
| `(Parser.Attr.grindMod|cases eager) => return .cases true
|
| 40 |
+
| `(Parser.Attr.grindMod|intro) => return .intro
|
| 41 |
+
| `(Parser.Attr.grindMod|ext) => return .ext
|
| 42 |
+
| `(Parser.Attr.grindMod|symbol $prio:prio) =>
|
| 43 |
+
let some prio := prio.raw.isNatLit? | throwErrorAt prio "priority expected"
|
| 44 |
+
return .symbol prio
|
| 45 |
+
| _ => throwError "unexpected `grind` theorem kind: `{stx}`"
|
| 46 |
+
|
| 47 |
+
/-- Return theorem kind for `stx` of the form `(Attr.grindMod)?` -/
|
| 48 |
+
def getAttrKindFromOpt (stx : Syntax) : CoreM AttrKind := do
|
| 49 |
+
if stx[1].isNone then
|
| 50 |
+
return .infer
|
| 51 |
+
else
|
| 52 |
+
getAttrKindCore stx[1][0]
|
| 53 |
+
|
| 54 |
+
def throwInvalidUsrModifier : CoreM Ξ± :=
|
| 55 |
+
throwError "the modifier `usr` is only relevant in parameters for `grind only`"
|
| 56 |
+
|
| 57 |
+
/--
|
| 58 |
+
Auxiliary function for registering `grind` and `grind?` attributes.
|
| 59 |
+
The `grind?` is an alias for `grind` which displays patterns using `logInfo`.
|
| 60 |
+
It is just a convenience for users.
|
| 61 |
+
-/
|
| 62 |
+
private def registerGrindAttr (showInfo : Bool) : IO Unit :=
|
| 63 |
+
registerBuiltinAttribute {
|
| 64 |
+
name := if showInfo then `grind? else `grind
|
| 65 |
+
descr :=
|
| 66 |
+
let header := if showInfo then
|
| 67 |
+
"The `[grind?]` attribute is identical to the `[grind]` attribute, but displays inferred pattern information."
|
| 68 |
+
else
|
| 69 |
+
"The `[grind]` attribute is used to annotate declarations."
|
| 70 |
+
header ++ "\
|
| 71 |
+
\
|
| 72 |
+
When applied to an equational theorem, `[grind =]`, `[grind =_]`, or `[grind _=_]`\
|
| 73 |
+
will mark the theorem for use in heuristic instantiations by the `grind` tactic,
|
| 74 |
+
using respectively the left-hand side, the right-hand side, or both sides of the theorem.\
|
| 75 |
+
When applied to a function, `[grind =]` automatically annotates the equational theorems associated with that function.\
|
| 76 |
+
When applied to a theorem `[grind β]` will instantiate the theorem whenever it encounters the conclusion of the theorem
|
| 77 |
+
(that is, it will use the theorem for backwards reasoning).\
|
| 78 |
+
When applied to a theorem `[grind β]` will instantiate the theorem whenever it encounters sufficiently many of the propositional hypotheses
|
| 79 |
+
(that is, it will use the theorem for forwards reasoning).\
|
| 80 |
+
\
|
| 81 |
+
The attribute `[grind]` by itself will effectively try `[grind β]` (if the conclusion is sufficient for instantiation) and then `[grind β]`.\
|
| 82 |
+
\
|
| 83 |
+
The `grind` tactic utilizes annotated theorems to add instances of matching patterns into the local context during proof search.\
|
| 84 |
+
For example, if a theorem `@[grind =] theorem foo_idempotent : foo (foo x) = foo x` is annotated,\
|
| 85 |
+
`grind` will add an instance of this theorem to the local context whenever it encounters the pattern `foo (foo x)`."
|
| 86 |
+
applicationTime := .afterCompilation
|
| 87 |
+
add := fun declName stx attrKind => MetaM.run' do
|
| 88 |
+
match (β getAttrKindFromOpt stx) with
|
| 89 |
+
| .ematch .user => throwInvalidUsrModifier
|
| 90 |
+
| .ematch k => addEMatchAttr declName attrKind k (β getGlobalSymbolPriorities) (showInfo := showInfo)
|
| 91 |
+
| .cases eager => addCasesAttr declName eager attrKind
|
| 92 |
+
| .intro =>
|
| 93 |
+
if let some info β isCasesAttrPredicateCandidate? declName false then
|
| 94 |
+
for ctor in info.ctors do
|
| 95 |
+
addEMatchAttr ctor attrKind (.default false) (β getGlobalSymbolPriorities) (showInfo := showInfo)
|
| 96 |
+
else
|
| 97 |
+
throwError "invalid `[grind intro]`, `{declName}` is not an inductive predicate"
|
| 98 |
+
| .ext => addExtAttr declName attrKind
|
| 99 |
+
| .infer =>
|
| 100 |
+
if let some declName β isCasesAttrCandidate? declName false then
|
| 101 |
+
addCasesAttr declName false attrKind
|
| 102 |
+
if let some info β isInductivePredicate? declName then
|
| 103 |
+
-- If it is an inductive predicate,
|
| 104 |
+
-- we also add the constructors (intro rules) as E-matching rules
|
| 105 |
+
for ctor in info.ctors do
|
| 106 |
+
addEMatchAttr ctor attrKind (.default false) (β getGlobalSymbolPriorities) (showInfo := showInfo)
|
| 107 |
+
else
|
| 108 |
+
addEMatchAttr declName attrKind (.default false) (β getGlobalSymbolPriorities) (showInfo := showInfo)
|
| 109 |
+
| .symbol prio => addSymbolPriorityAttr declName attrKind prio
|
| 110 |
+
erase := fun declName => MetaM.run' do
|
| 111 |
+
if showInfo then
|
| 112 |
+
throwError "`[grind?]` is a helper attribute for displaying inferred patterns, if you want to remove the attribute, consider using `[grind]` instead"
|
| 113 |
+
if (β isCasesAttrCandidate declName false) then
|
| 114 |
+
eraseCasesAttr declName
|
| 115 |
+
else if (β isExtTheorem declName) then
|
| 116 |
+
eraseExtAttr declName
|
| 117 |
+
else
|
| 118 |
+
eraseEMatchAttr declName
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
builtin_initialize
|
| 122 |
+
registerGrindAttr true
|
| 123 |
+
registerGrindAttr false
|
| 124 |
+
|
| 125 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Beta.lean
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.Grind
|
| 10 |
+
|
| 11 |
+
/-- Returns all lambda expressions in the equivalence class with root `root`. -/
|
| 12 |
+
def getEqcLambdas (root : ENode) : GoalM (Array Expr) := do
|
| 13 |
+
unless root.hasLambdas do return #[]
|
| 14 |
+
foldEqc root.self (init := #[]) fun n lams =>
|
| 15 |
+
if n.self.isLambda then return lams.push n.self else return lams
|
| 16 |
+
|
| 17 |
+
/--
|
| 18 |
+
Returns the root of the functions in the equivalence class containing `e`.
|
| 19 |
+
That is, if `f a` is in `root`s equivalence class, results contains the root of `f`.
|
| 20 |
+
-/
|
| 21 |
+
def getFnRoots (e : Expr) : GoalM (Array Expr) := do
|
| 22 |
+
foldEqc e (init := #[]) fun n fns => do
|
| 23 |
+
let fn := n.self.getAppFn
|
| 24 |
+
let fnRoot := (β getRoot? fn).getD fn
|
| 25 |
+
if Option.isNone <| fns.find? (isSameExpr Β· fnRoot) then
|
| 26 |
+
return fns.push fnRoot
|
| 27 |
+
else
|
| 28 |
+
return fns
|
| 29 |
+
|
| 30 |
+
/--
|
| 31 |
+
For each `lam` in `lams` s.t. `lam` and `f` are in the same equivalence class,
|
| 32 |
+
propagate `f args = lam args`.
|
| 33 |
+
-/
|
| 34 |
+
def propagateBetaEqs (lams : Array Expr) (f : Expr) (args : Array Expr) : GoalM Unit := do
|
| 35 |
+
if args.isEmpty then return ()
|
| 36 |
+
for lam in lams do
|
| 37 |
+
let rhs := lam.beta args
|
| 38 |
+
unless rhs.isLambda do
|
| 39 |
+
let mut gen := Nat.max (β getGeneration lam) (β getGeneration f)
|
| 40 |
+
let lhs := mkAppN f args
|
| 41 |
+
if (β hasSameType f lam) then
|
| 42 |
+
let mut h β mkEqProof f lam
|
| 43 |
+
for arg in args do
|
| 44 |
+
gen := Nat.max gen (β getGeneration arg)
|
| 45 |
+
h β mkCongrFun h arg
|
| 46 |
+
let eq β mkEq lhs rhs
|
| 47 |
+
trace_goal[grind.beta] "{eq}, using {lam}"
|
| 48 |
+
addNewRawFact h eq (gen+1) (.beta lam)
|
| 49 |
+
|
| 50 |
+
private def isPropagateBetaTarget (e : Expr) : GoalM Bool := do
|
| 51 |
+
let .app f _ := e | return false
|
| 52 |
+
go f
|
| 53 |
+
where
|
| 54 |
+
go (f : Expr) : GoalM Bool := do
|
| 55 |
+
if let some root β getRootENode? f then
|
| 56 |
+
return root.hasLambdas
|
| 57 |
+
let .app f _ := f | return false
|
| 58 |
+
go f
|
| 59 |
+
|
| 60 |
+
/--
|
| 61 |
+
Applies beta-reduction for lambdas in `f`s equivalence class.
|
| 62 |
+
We use this function while internalizing new applications.
|
| 63 |
+
-/
|
| 64 |
+
def propagateBetaForNewApp (e : Expr) : GoalM Unit := do
|
| 65 |
+
unless (β isPropagateBetaTarget e) do return ()
|
| 66 |
+
let mut e := e
|
| 67 |
+
let mut args := #[]
|
| 68 |
+
repeat
|
| 69 |
+
unless args.isEmpty do
|
| 70 |
+
if let some root β getRootENode? e then
|
| 71 |
+
if root.hasLambdas then
|
| 72 |
+
propagateBetaEqs (β getEqcLambdas root) e args.reverse
|
| 73 |
+
let .app f arg := e | return ()
|
| 74 |
+
e := f
|
| 75 |
+
args := args.push arg
|
| 76 |
+
|
| 77 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Canon.lean
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Grind.Util
|
| 8 |
+
import Lean.Meta.Basic
|
| 9 |
+
import Lean.Meta.FunInfo
|
| 10 |
+
import Lean.Util.FVarSubset
|
| 11 |
+
import Lean.Util.PtrSet
|
| 12 |
+
import Lean.Util.FVarSubset
|
| 13 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 14 |
+
|
| 15 |
+
namespace Lean.Meta.Grind
|
| 16 |
+
namespace Canon
|
| 17 |
+
|
| 18 |
+
/-!
|
| 19 |
+
A canonicalizer module for the `grind` tactic. The canonicalizer defined in `Meta/Canonicalizer.lean` is
|
| 20 |
+
not suitable for the `grind` tactic. It was designed for tactics such as `omega`, where the goal is
|
| 21 |
+
to detect when two structurally different atoms are definitionally equal.
|
| 22 |
+
|
| 23 |
+
The `grind` tactic, on the other hand, uses congruence closure. Moreover, types, type formers, proofs, and instances
|
| 24 |
+
are considered supporting elements and are not factored into congruence detection.
|
| 25 |
+
|
| 26 |
+
This module minimizes the number of `isDefEq` checks by comparing two terms `a` and `b` only if they are instances,
|
| 27 |
+
types, or type formers and are the `i`-th arguments of two different `f`-applications. This approach is
|
| 28 |
+
sufficient for the congruence closure procedure used by the `grind` tactic.
|
| 29 |
+
|
| 30 |
+
To further optimize `isDefEq` checks, instances are compared using `TransparencyMode.instances`, which reduces
|
| 31 |
+
the number of constants that need to be unfolded. If diagnostics are enabled, instances are compared using
|
| 32 |
+
the default transparency mode too for sanity checking, and discrepancies are reported.
|
| 33 |
+
Types and type formers are always checked using default transparency.
|
| 34 |
+
|
| 35 |
+
Remark:
|
| 36 |
+
The canonicalizer minimizes issues with non-canonical instances and structurally different but definitionally equal types,
|
| 37 |
+
but it does not solve all problems. For example, consider a situation where we have `(a : BitVec n)`
|
| 38 |
+
and `(b : BitVec m)`, along with instances `inst1 n : Add (BitVec n)` and `inst2 m : Add (BitVec m)` where `inst1`
|
| 39 |
+
and `inst2` are structurally different. Now consider the terms `a + a` and `b + b`. After canonicalization, the two
|
| 40 |
+
additions will still use structurally different (and definitionally different) instances: `inst1 n` and `inst2 m`.
|
| 41 |
+
Furthermore, `grind` will not be able to infer that `a + a β b + b` even if we add the assumptions `n = m` and `a β b`.
|
| 42 |
+
-/
|
| 43 |
+
|
| 44 |
+
@[inline] private def get' : GoalM State :=
|
| 45 |
+
return (β get).canon
|
| 46 |
+
|
| 47 |
+
@[inline] private def modify' (f : State β State) : GoalM Unit :=
|
| 48 |
+
modify fun s => { s with canon := f s.canon }
|
| 49 |
+
|
| 50 |
+
/--
|
| 51 |
+
Helper function for `canonElemCore`. It tries `isDefEq a b` with default transparency, but using
|
| 52 |
+
at most `canonHeartbeats` heartbeats. It reports an issue if the threshold is reached.
|
| 53 |
+
Remark: `parent` is use only to report an issue.
|
| 54 |
+
-/
|
| 55 |
+
private def isDefEqBounded (a b : Expr) (parent : Expr) : GoalM Bool := do
|
| 56 |
+
withCurrHeartbeats do
|
| 57 |
+
let curr := (β getConfig).canonHeartbeats
|
| 58 |
+
tryCatchRuntimeEx
|
| 59 |
+
(withTheReader Core.Context (fun ctx => { ctx with maxHeartbeats := curr*1000 }) do
|
| 60 |
+
withDefault <| isDefEq a b)
|
| 61 |
+
fun ex => do
|
| 62 |
+
if ex.isRuntime then
|
| 63 |
+
reportIssue! "failed to show that{indentExpr a}\nis definitionally equal to{indentExpr b}\nwhile canonicalizing{indentExpr parent}\nusing `{curr}*1000` heartbeats, `(canonHeartbeats := {curr})`"
|
| 64 |
+
return false
|
| 65 |
+
else
|
| 66 |
+
throw ex
|
| 67 |
+
|
| 68 |
+
/--
|
| 69 |
+
Helper function for canonicalizing `e` occurring as the `i`th argument of an `f`-application.
|
| 70 |
+
If `useIsDefEqBounded` is `true`, we try `isDefEqBounded` before returning false
|
| 71 |
+
-/
|
| 72 |
+
def canonElemCore (parent : Expr) (f : Expr) (i : Nat) (e : Expr) (useIsDefEqBounded : Bool) : GoalM Expr := do
|
| 73 |
+
let s β get'
|
| 74 |
+
if let some c := s.canon.find? e then
|
| 75 |
+
return c
|
| 76 |
+
let key := (f, i)
|
| 77 |
+
let eType β inferType e
|
| 78 |
+
let cs := s.argMap.find? key |>.getD []
|
| 79 |
+
for (c, cType) in cs do
|
| 80 |
+
/-
|
| 81 |
+
We first check the types
|
| 82 |
+
The following checks are a performance bottleneck.
|
| 83 |
+
For example, in the test `grind_ite.lean`, there are many checks of the form:
|
| 84 |
+
```
|
| 85 |
+
w_4 β assign.insert v true β Prop =?= w_1 β assign.insert v false β Prop
|
| 86 |
+
```
|
| 87 |
+
where `grind` unfolds the definition of `DHashMap.insert` and `TreeMap.insert`.
|
| 88 |
+
-/
|
| 89 |
+
if (β withDefault <| isDefEq eType cType) then
|
| 90 |
+
if (β isDefEq e c) then
|
| 91 |
+
-- We used to check `c.fvarsSubset e` because it is not
|
| 92 |
+
-- in general safe to replace `e` with `c` if `c` has more free variables than `e`.
|
| 93 |
+
-- However, we don't revert previously canonicalized elements in the `grind` tactic.
|
| 94 |
+
-- Moreover, we store the canonicalizer state in the `Goal` because we case-split
|
| 95 |
+
-- and different locals are added in different branches.
|
| 96 |
+
modify' fun s => { s with canon := s.canon.insert e c }
|
| 97 |
+
trace_goal[grind.debugn.canon] "found {e} ===> {c}"
|
| 98 |
+
return c
|
| 99 |
+
if useIsDefEqBounded then
|
| 100 |
+
-- If `e` and `c` are not types, we use `isDefEqBounded`
|
| 101 |
+
if (β isDefEqBounded e c parent) then
|
| 102 |
+
modify' fun s => { s with canon := s.canon.insert e c }
|
| 103 |
+
trace_goal[grind.debugn.canon] "found using `isDefEqBounded`: {e} ===> {c}"
|
| 104 |
+
return c
|
| 105 |
+
trace_goal[grind.debug.canon] "({f}, {i}) β¦ {e}"
|
| 106 |
+
modify' fun s => { s with canon := s.canon.insert e e, argMap := s.argMap.insert key ((e, eType)::cs) }
|
| 107 |
+
return e
|
| 108 |
+
|
| 109 |
+
abbrev canonType (parent f : Expr) (i : Nat) (e : Expr) := withDefault <| canonElemCore parent f i e (useIsDefEqBounded := false)
|
| 110 |
+
abbrev canonInst (parent f : Expr) (i : Nat) (e : Expr) := withReducibleAndInstances <| canonElemCore parent f i e (useIsDefEqBounded := true)
|
| 111 |
+
abbrev canonImplicit (parent f : Expr) (i : Nat) (e : Expr) := withReducible <| canonElemCore parent f i e (useIsDefEqBounded := true)
|
| 112 |
+
|
| 113 |
+
/--
|
| 114 |
+
Return type for the `shouldCanon` function.
|
| 115 |
+
-/
|
| 116 |
+
private inductive ShouldCanonResult where
|
| 117 |
+
| /- Nested types (and type formers) are canonicalized. -/
|
| 118 |
+
canonType
|
| 119 |
+
| /- Nested instances are canonicalized. -/
|
| 120 |
+
canonInst
|
| 121 |
+
| /- Implicit argument that is not an instance nor a type. -/
|
| 122 |
+
canonImplicit
|
| 123 |
+
| /-
|
| 124 |
+
Term is not a proof, type (former), nor an instance.
|
| 125 |
+
Thus, it must be recursively visited by the canonizer.
|
| 126 |
+
-/
|
| 127 |
+
visit
|
| 128 |
+
deriving Inhabited
|
| 129 |
+
|
| 130 |
+
instance : Repr ShouldCanonResult where
|
| 131 |
+
reprPrec r _ := match r with
|
| 132 |
+
| .canonType => "canonType"
|
| 133 |
+
| .canonInst => "canonInst"
|
| 134 |
+
| .canonImplicit => "canonImplicit"
|
| 135 |
+
| .visit => "visit"
|
| 136 |
+
|
| 137 |
+
/--
|
| 138 |
+
See comments at `ShouldCanonResult`.
|
| 139 |
+
-/
|
| 140 |
+
def shouldCanon (pinfos : Array ParamInfo) (i : Nat) (arg : Expr) : MetaM ShouldCanonResult := do
|
| 141 |
+
if h : i < pinfos.size then
|
| 142 |
+
let pinfo := pinfos[i]
|
| 143 |
+
if pinfo.isInstImplicit then
|
| 144 |
+
return .canonInst
|
| 145 |
+
else if pinfo.isProp then
|
| 146 |
+
return .visit
|
| 147 |
+
else if pinfo.isImplicit then
|
| 148 |
+
if (β isTypeFormer arg) then
|
| 149 |
+
return .canonType
|
| 150 |
+
else
|
| 151 |
+
return .canonImplicit
|
| 152 |
+
if (β isProp arg) then
|
| 153 |
+
return .visit
|
| 154 |
+
else if (β isTypeFormer arg) then
|
| 155 |
+
return .canonType
|
| 156 |
+
else
|
| 157 |
+
return .visit
|
| 158 |
+
|
| 159 |
+
unsafe def canonImpl (e : Expr) : GoalM Expr := do
|
| 160 |
+
visit e |>.run' mkPtrMap
|
| 161 |
+
where
|
| 162 |
+
visit (e : Expr) : StateRefT (PtrMap Expr Expr) GoalM Expr := do
|
| 163 |
+
unless e.isApp || e.isForall do return e
|
| 164 |
+
-- Check whether it is cached
|
| 165 |
+
if let some r := (β get).find? e then
|
| 166 |
+
return r
|
| 167 |
+
let e' β match e with
|
| 168 |
+
| .app .. => e.withApp fun f args => do
|
| 169 |
+
if f.isConstOf ``Lean.Grind.nestedProof && args.size == 2 then
|
| 170 |
+
let prop := args[0]!
|
| 171 |
+
let prop' β visit prop
|
| 172 |
+
if let some r := (β get').proofCanon.find? prop' then
|
| 173 |
+
pure r
|
| 174 |
+
else
|
| 175 |
+
let e' := if ptrEq prop prop' then e else mkAppN f (args.set! 0 prop')
|
| 176 |
+
modify' fun s => { s with proofCanon := s.proofCanon.insert prop' e' }
|
| 177 |
+
pure e'
|
| 178 |
+
else
|
| 179 |
+
let pinfos := (β getFunInfo f).paramInfo
|
| 180 |
+
let mut modified := false
|
| 181 |
+
let mut args := args.toVector
|
| 182 |
+
for h : i in [:args.size] do
|
| 183 |
+
let arg := args[i]
|
| 184 |
+
trace_goal[grind.debug.canon] "[{repr (β shouldCanon pinfos i arg)}]: {arg} : {β inferType arg}"
|
| 185 |
+
let arg' β match (β shouldCanon pinfos i arg) with
|
| 186 |
+
| .canonType => canonType e f i arg
|
| 187 |
+
| .canonInst => canonInst e f i arg
|
| 188 |
+
| .canonImplicit => canonImplicit e f i (β visit arg)
|
| 189 |
+
| .visit => visit arg
|
| 190 |
+
unless ptrEq arg arg' do
|
| 191 |
+
args := args.set i arg'
|
| 192 |
+
modified := true
|
| 193 |
+
pure <| if modified then mkAppN f args.toArray else e
|
| 194 |
+
| .forallE _ d b _ =>
|
| 195 |
+
-- Recall that we have `ForallProp.lean`.
|
| 196 |
+
let d' β visit d
|
| 197 |
+
-- Remark: users may not want to convert `p β q` into `Β¬p β¨ q`
|
| 198 |
+
let b' β if b.hasLooseBVars then pure b else visit b
|
| 199 |
+
pure <| e.updateForallE! d' b'
|
| 200 |
+
| _ => unreachable!
|
| 201 |
+
modify fun s => s.insert e e'
|
| 202 |
+
return e'
|
| 203 |
+
|
| 204 |
+
end Canon
|
| 205 |
+
|
| 206 |
+
/-- Canonicalizes nested types, type formers, and instances in `e`. -/
|
| 207 |
+
def canon (e : Expr) : GoalM Expr := do profileitM Exception "grind canon" (β getOptions) do
|
| 208 |
+
trace_goal[grind.debug.canon] "{e}"
|
| 209 |
+
unsafe Canon.canonImpl e
|
| 210 |
+
|
| 211 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Cases.lean
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Cases
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.Grind
|
| 10 |
+
|
| 11 |
+
/-- Types that `grind` will case-split on. -/
|
| 12 |
+
structure CasesTypes where
|
| 13 |
+
casesMap : PHashMap Name Bool := {}
|
| 14 |
+
deriving Inhabited
|
| 15 |
+
|
| 16 |
+
structure CasesEntry where
|
| 17 |
+
declName : Name
|
| 18 |
+
eager : Bool
|
| 19 |
+
deriving Inhabited
|
| 20 |
+
|
| 21 |
+
/--
|
| 22 |
+
`grind` always case-splits on the following types. Even when using `grind only`.
|
| 23 |
+
The goal is to reduce noise in the tactic generated by `grind?`
|
| 24 |
+
-/
|
| 25 |
+
private def builtinEagerCases : NameSet :=
|
| 26 |
+
RBTree.ofList [``And, ``Exists, ``True, ``False, ``Unit, ``Empty]
|
| 27 |
+
|
| 28 |
+
/--
|
| 29 |
+
Returns `true` if `declName` is the name of inductive type/predicate that
|
| 30 |
+
even `grind only` case splits on.
|
| 31 |
+
Remark: we added support for them to reduce the noise in the tactic generated by
|
| 32 |
+
`grind?`
|
| 33 |
+
-/
|
| 34 |
+
def isBuiltinEagerCases (declName : Name) : Bool :=
|
| 35 |
+
builtinEagerCases.contains declName
|
| 36 |
+
|
| 37 |
+
/-- Returns `true` if `s` contains a `declName`. -/
|
| 38 |
+
def CasesTypes.contains (s : CasesTypes) (declName : Name) : Bool :=
|
| 39 |
+
s.casesMap.contains declName
|
| 40 |
+
|
| 41 |
+
/-- Removes the given declaration from `s`. -/
|
| 42 |
+
def CasesTypes.erase (s : CasesTypes) (declName : Name) : CasesTypes :=
|
| 43 |
+
{ s with casesMap := s.casesMap.erase declName }
|
| 44 |
+
|
| 45 |
+
def CasesTypes.insert (s : CasesTypes) (declName : Name) (eager : Bool) : CasesTypes :=
|
| 46 |
+
{ s with casesMap := s.casesMap.insert declName eager }
|
| 47 |
+
|
| 48 |
+
def CasesTypes.find? (s : CasesTypes) (declName : Name) : Option Bool :=
|
| 49 |
+
s.casesMap.find? declName
|
| 50 |
+
|
| 51 |
+
def CasesTypes.isEagerSplit (s : CasesTypes) (declName : Name) : Bool :=
|
| 52 |
+
(s.casesMap.find? declName |>.getD false) || isBuiltinEagerCases declName
|
| 53 |
+
|
| 54 |
+
def CasesTypes.isSplit (s : CasesTypes) (declName : Name) : Bool :=
|
| 55 |
+
(s.casesMap.find? declName |>.isSome) || isBuiltinEagerCases declName
|
| 56 |
+
|
| 57 |
+
builtin_initialize casesExt : SimpleScopedEnvExtension CasesEntry CasesTypes β
|
| 58 |
+
registerSimpleScopedEnvExtension {
|
| 59 |
+
initial := {}
|
| 60 |
+
addEntry := fun s {declName, eager} => s.insert declName eager
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
def resetCasesExt : CoreM Unit := do
|
| 64 |
+
modifyEnv fun env => casesExt.modifyState env fun _ => {}
|
| 65 |
+
|
| 66 |
+
def getCasesTypes : CoreM CasesTypes :=
|
| 67 |
+
return casesExt.getState (β getEnv)
|
| 68 |
+
|
| 69 |
+
/-- Returns `true` is `declName` is a builtin split or has been tagged with `[grind]` attribute. -/
|
| 70 |
+
def isSplit (declName : Name) : CoreM Bool := do
|
| 71 |
+
return (β getCasesTypes).isSplit declName
|
| 72 |
+
|
| 73 |
+
partial def isCasesAttrCandidate? (declName : Name) (eager : Bool) : CoreM (Option Name) := do
|
| 74 |
+
match (β getConstInfo declName) with
|
| 75 |
+
| .inductInfo info => if !info.isRec || !eager then return some declName else return none
|
| 76 |
+
| _ => return none
|
| 77 |
+
|
| 78 |
+
def isCasesAttrCandidate (declName : Name) (eager : Bool) : CoreM Bool := do
|
| 79 |
+
return (β isCasesAttrCandidate? declName eager).isSome
|
| 80 |
+
|
| 81 |
+
def isCasesAttrPredicateCandidate? (declName : Name) (eager : Bool) : MetaM (Option InductiveVal) := do
|
| 82 |
+
let some declName β isCasesAttrCandidate? declName eager | return none
|
| 83 |
+
isInductivePredicate? declName
|
| 84 |
+
|
| 85 |
+
def validateCasesAttr (declName : Name) (eager : Bool) : CoreM Unit := do
|
| 86 |
+
unless (β isCasesAttrCandidate declName eager) do
|
| 87 |
+
if eager then
|
| 88 |
+
throwError "invalid `[grind cases eager]`, `{declName}` is not a non-recursive inductive datatype or an alias for one"
|
| 89 |
+
else
|
| 90 |
+
throwError "invalid `[grind cases]`, `{declName}` is not an inductive datatype or an alias for one"
|
| 91 |
+
|
| 92 |
+
def addCasesAttr (declName : Name) (eager : Bool) (attrKind : AttributeKind) : CoreM Unit := do
|
| 93 |
+
validateCasesAttr declName eager
|
| 94 |
+
casesExt.add { declName, eager } attrKind
|
| 95 |
+
|
| 96 |
+
def CasesTypes.eraseDecl (s : CasesTypes) (declName : Name) : CoreM CasesTypes := do
|
| 97 |
+
if s.contains declName then
|
| 98 |
+
return s.erase declName
|
| 99 |
+
else
|
| 100 |
+
throwError "`{declName}` is not marked with the `[grind]` attribute"
|
| 101 |
+
|
| 102 |
+
def ensureNotBuiltinCases (declName : Name) : CoreM Unit := do
|
| 103 |
+
if isBuiltinEagerCases declName then
|
| 104 |
+
throwError "`{declName}` is marked as a built-in case-split for `grind` and cannot be erased"
|
| 105 |
+
|
| 106 |
+
def eraseCasesAttr (declName : Name) : CoreM Unit := do
|
| 107 |
+
ensureNotBuiltinCases declName
|
| 108 |
+
let s := casesExt.getState (β getEnv)
|
| 109 |
+
let s β s.eraseDecl declName
|
| 110 |
+
modifyEnv fun env => casesExt.modifyState env fun _ => s
|
| 111 |
+
|
| 112 |
+
/--
|
| 113 |
+
We say a free variable is "simple" to be processed by the cases tactic IF:
|
| 114 |
+
- It is the latest and consequently there are no forward dependencies, OR
|
| 115 |
+
- It is not a proposion.
|
| 116 |
+
-/
|
| 117 |
+
private def isSimpleFVar (e : Expr) : MetaM Bool := do
|
| 118 |
+
let .fvar fvarId := e | return false
|
| 119 |
+
let decl β fvarId.getDecl
|
| 120 |
+
if decl.index == (β getLCtx).numIndices - 1 then
|
| 121 |
+
-- It is the latest free variable, so there are no forward dependencies
|
| 122 |
+
return true
|
| 123 |
+
else
|
| 124 |
+
-- It is pointless to add an auxiliary equality if `e`s type is a proposition
|
| 125 |
+
isProp decl.type
|
| 126 |
+
|
| 127 |
+
/--
|
| 128 |
+
The `grind` tactic includes an auxiliary `cases` tactic that is not intended for direct use by users.
|
| 129 |
+
This method implements it.
|
| 130 |
+
This tactic is automatically applied when introducing local declarations with a type tagged with `[grind_cases]`.
|
| 131 |
+
It is also used for "case-splitting" on terms during the search.
|
| 132 |
+
|
| 133 |
+
It differs from the user-facing Lean `cases` tactic in the following ways:
|
| 134 |
+
|
| 135 |
+
- It avoids unnecessary `revert` and `intro` operations.
|
| 136 |
+
|
| 137 |
+
- It does not introduce new local declarations for each minor premise. Instead, the `grind` tactic preprocessor is responsible for introducing them.
|
| 138 |
+
|
| 139 |
+
- If the major premise type is an indexed family, auxiliary declarations and (heterogeneous) equalities are introduced.
|
| 140 |
+
However, these equalities are not resolved using `unifyEqs`. Instead, the `grind` tactic employs union-find and
|
| 141 |
+
congruence closure to process these auxiliary equalities. This approach avoids applying substitution to propositions
|
| 142 |
+
that have already been internalized by `grind`.
|
| 143 |
+
-/
|
| 144 |
+
def cases (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := mvarId.withContext do
|
| 145 |
+
let tag β mvarId.getTag
|
| 146 |
+
let type β whnf (β inferType e)
|
| 147 |
+
let .const declName _ := type.getAppFn | throwInductiveExpected type
|
| 148 |
+
let .inductInfo _ β getConstInfo declName | throwInductiveExpected type
|
| 149 |
+
let recursorInfo β mkRecursorInfo (mkCasesOnName declName)
|
| 150 |
+
let k (mvarId : MVarId) (fvarId : FVarId) (indices : Array FVarId) : MetaM (List MVarId) := do
|
| 151 |
+
let indicesExpr := indices.map mkFVar
|
| 152 |
+
let recursor β mkRecursorAppPrefix mvarId `grind.cases fvarId recursorInfo indicesExpr
|
| 153 |
+
let lctx β getLCtx
|
| 154 |
+
let lctx := lctx.setKind fvarId .implDetail
|
| 155 |
+
let lctx := indices.foldl (init := lctx) fun lctx fvarId => lctx.setKind fvarId .implDetail
|
| 156 |
+
let localInsts β getLocalInstances
|
| 157 |
+
let mut recursor := mkApp (mkAppN recursor indicesExpr) (mkFVar fvarId)
|
| 158 |
+
let mut recursorType β inferType recursor
|
| 159 |
+
let mut mvarIdsNew := #[]
|
| 160 |
+
let mut idx := 1
|
| 161 |
+
for _ in [:recursorInfo.numMinors] do
|
| 162 |
+
let .forallE _ targetNew recursorTypeNew _ β whnf recursorType
|
| 163 |
+
| throwTacticEx `grind.cases mvarId "unexpected recursor type"
|
| 164 |
+
recursorType := recursorTypeNew
|
| 165 |
+
let tagNew := if recursorInfo.numMinors > 1 then Name.num tag idx else tag
|
| 166 |
+
let mvar β mkFreshExprMVarAt lctx localInsts targetNew .syntheticOpaque tagNew
|
| 167 |
+
recursor := mkApp recursor mvar
|
| 168 |
+
mvarIdsNew := mvarIdsNew.push mvar.mvarId!
|
| 169 |
+
idx := idx + 1
|
| 170 |
+
mvarId.assign recursor
|
| 171 |
+
return mvarIdsNew.toList
|
| 172 |
+
if recursorInfo.numIndices > 0 then
|
| 173 |
+
let s β generalizeIndices' mvarId e
|
| 174 |
+
s.mvarId.withContext do
|
| 175 |
+
k s.mvarId s.fvarId s.indicesFVarIds
|
| 176 |
+
else if (β isSimpleFVar e) then
|
| 177 |
+
-- We don't need to revert anything.
|
| 178 |
+
k mvarId e.fvarId! #[]
|
| 179 |
+
else
|
| 180 |
+
let mvarId β if (β isProof e) then
|
| 181 |
+
mvarId.assert (β mkFreshUserName `x) type e
|
| 182 |
+
else
|
| 183 |
+
mvarId.assertExt (β mkFreshUserName `x) type e
|
| 184 |
+
let (fvarId, mvarId) β mvarId.intro1
|
| 185 |
+
mvarId.withContext do k mvarId fvarId #[]
|
| 186 |
+
|
| 187 |
+
where
|
| 188 |
+
throwInductiveExpected {Ξ±} (type : Expr) : MetaM Ξ± := do
|
| 189 |
+
throwTacticEx `grind.cases mvarId m!"(non-recursive) inductive type expected at {e}{indentExpr type}"
|
| 190 |
+
|
| 191 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/CasesMatch.lean
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Util
|
| 8 |
+
import Lean.Meta.Tactic.Cases
|
| 9 |
+
import Lean.Meta.Match.MatcherApp
|
| 10 |
+
import Lean.Meta.Tactic.Grind.MatchCond
|
| 11 |
+
|
| 12 |
+
namespace Lean.Meta.Grind
|
| 13 |
+
|
| 14 |
+
/-- Returns `true` if `e` is of the form `β ..., _ = _ ... -> False` -/
|
| 15 |
+
def isMatchCondCandidate (e : Expr) : Bool :=
|
| 16 |
+
-- TODO: see comment at the beginning of `MatchCond.lean`.
|
| 17 |
+
Simp.isEqnThmHypothesis e
|
| 18 |
+
|
| 19 |
+
/--
|
| 20 |
+
Given a splitter alternative, annotate the terms that are `match`-expression
|
| 21 |
+
conditions corresponding to overlapping patterns.
|
| 22 |
+
-/
|
| 23 |
+
private def addMatchCondsToAlt (alt : Expr) : Expr := Id.run do
|
| 24 |
+
let .forallE _ d b _ := alt
|
| 25 |
+
| return alt
|
| 26 |
+
let d := if isMatchCondCandidate d then markAsPreMatchCond d else d
|
| 27 |
+
return alt.updateForallE! d (addMatchCondsToAlt b)
|
| 28 |
+
|
| 29 |
+
/--
|
| 30 |
+
Annotates the `match`-expression conditions in the alternatives in the given
|
| 31 |
+
`match` splitter type.
|
| 32 |
+
-/
|
| 33 |
+
private def addMatchCondsToSplitter (splitterType : Expr) (numAlts : Nat) : Expr := Id.run do
|
| 34 |
+
if numAlts == 0 then return splitterType
|
| 35 |
+
let .forallE _ alt b _ := splitterType
|
| 36 |
+
| return splitterType
|
| 37 |
+
return splitterType.updateForallE! (addMatchCondsToAlt alt) (addMatchCondsToSplitter b (numAlts-1))
|
| 38 |
+
|
| 39 |
+
def casesMatch (mvarId : MVarId) (e : Expr) : MetaM (List MVarId) := mvarId.withContext do
|
| 40 |
+
let some app β matchMatcherApp? e
|
| 41 |
+
| throwTacticEx `grind.casesMatch mvarId m!"`match`-expression expected{indentExpr e}"
|
| 42 |
+
let (motive, eqRefls) β mkMotiveAndRefls app
|
| 43 |
+
let target β mvarId.getType
|
| 44 |
+
let mut us := app.matcherLevels
|
| 45 |
+
if let some i := app.uElimPos? then
|
| 46 |
+
us := us.set! i (β getLevel target)
|
| 47 |
+
let splitterName := (β Match.getEquationsFor app.matcherName).splitterName
|
| 48 |
+
let splitterApp := mkConst splitterName us.toList
|
| 49 |
+
let splitterApp := mkAppN splitterApp app.params
|
| 50 |
+
let splitterApp := mkApp splitterApp motive
|
| 51 |
+
let splitterApp := mkAppN splitterApp app.discrs
|
| 52 |
+
let numAlts := app.alts.size
|
| 53 |
+
let splitterType β inferType splitterApp
|
| 54 |
+
let splitterType := addMatchCondsToSplitter splitterType app.alts.size
|
| 55 |
+
let (mvars, _, _) β forallMetaBoundedTelescope splitterType numAlts (kind := .syntheticOpaque)
|
| 56 |
+
let splitterApp := mkAppN splitterApp mvars
|
| 57 |
+
let val := mkAppN splitterApp eqRefls
|
| 58 |
+
mvarId.assign val
|
| 59 |
+
updateTags mvars
|
| 60 |
+
return mvars.toList.map (Β·.mvarId!)
|
| 61 |
+
where
|
| 62 |
+
mkMotiveAndRefls (app : MatcherApp) : MetaM (Expr Γ Array Expr) := do
|
| 63 |
+
let dummy := mkSort 0
|
| 64 |
+
let aux := mkApp (mkAppN e.getAppFn app.params) dummy
|
| 65 |
+
forallBoundedTelescope (β inferType aux) app.discrs.size fun xs _ => do
|
| 66 |
+
withNewEqs app.discrs xs fun eqs eqRefls => do
|
| 67 |
+
let type β mvarId.getType
|
| 68 |
+
let type β mkForallFVars eqs type
|
| 69 |
+
let motive β mkLambdaFVars xs type
|
| 70 |
+
return (motive, eqRefls)
|
| 71 |
+
|
| 72 |
+
updateTags (mvars : Array Expr) : MetaM Unit := do
|
| 73 |
+
let tag β mvarId.getTag
|
| 74 |
+
if mvars.size == 1 then
|
| 75 |
+
mvars[0]!.mvarId!.setTag tag
|
| 76 |
+
else
|
| 77 |
+
let mut idx := 1
|
| 78 |
+
for mvar in mvars do
|
| 79 |
+
mvar.mvarId!.setTag (Name.num tag idx)
|
| 80 |
+
idx := idx + 1
|
| 81 |
+
|
| 82 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Core.lean
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Grind.Util
|
| 8 |
+
import Lean.Meta.LitValues
|
| 9 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 10 |
+
import Lean.Meta.Tactic.Grind.Inv
|
| 11 |
+
import Lean.Meta.Tactic.Grind.PP
|
| 12 |
+
import Lean.Meta.Tactic.Grind.Ctor
|
| 13 |
+
import Lean.Meta.Tactic.Grind.Util
|
| 14 |
+
import Lean.Meta.Tactic.Grind.Beta
|
| 15 |
+
import Lean.Meta.Tactic.Grind.Internalize
|
| 16 |
+
import Lean.Meta.Tactic.Grind.Simp
|
| 17 |
+
|
| 18 |
+
namespace Lean.Meta.Grind
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
The fields `target?` and `proof?` in `e`'s `ENode` are encoding a transitivity proof
|
| 22 |
+
from `e` to the root of the equivalence class
|
| 23 |
+
This method "inverts" the proof, and makes it to go from the root of the equivalence class to `e`.
|
| 24 |
+
|
| 25 |
+
We use this method when merging two equivalence classes.
|
| 26 |
+
-/
|
| 27 |
+
private partial def invertTrans (e : Expr) : GoalM Unit := do
|
| 28 |
+
go e false none none
|
| 29 |
+
where
|
| 30 |
+
go (e : Expr) (flippedNew : Bool) (targetNew? : Option Expr) (proofNew? : Option Expr) : GoalM Unit := do
|
| 31 |
+
let node β getENode e
|
| 32 |
+
if let some target := node.target? then
|
| 33 |
+
go target (!node.flipped) (some e) node.proof?
|
| 34 |
+
setENode e { node with
|
| 35 |
+
target? := targetNew?
|
| 36 |
+
flipped := flippedNew
|
| 37 |
+
proof? := proofNew?
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
/--
|
| 41 |
+
Returns `true` if the parent is relevant for congruence closure.
|
| 42 |
+
-/
|
| 43 |
+
private def isCongrRelevant (parent : Expr) : Bool :=
|
| 44 |
+
parent.isApp || parent.isArrow
|
| 45 |
+
|
| 46 |
+
/--
|
| 47 |
+
Removes `root` parents from the congruence table.
|
| 48 |
+
This is an auxiliary function performed while merging equivalence classes.
|
| 49 |
+
-/
|
| 50 |
+
private def removeParents (root : Expr) : GoalM ParentSet := do
|
| 51 |
+
let parents β getParents root
|
| 52 |
+
for parent in parents do
|
| 53 |
+
-- Recall that we may have `Expr.forallE` in `parents` because of `ForallProp.lean`
|
| 54 |
+
if (β pure (isCongrRelevant parent) <&&> isCongrRoot parent) then
|
| 55 |
+
trace_goal[grind.debug.parent] "remove: {parent}"
|
| 56 |
+
modify fun s => { s with congrTable := s.congrTable.erase { e := parent } }
|
| 57 |
+
return parents
|
| 58 |
+
|
| 59 |
+
/--
|
| 60 |
+
Reinserts parents into the congruence table and detect new equalities.
|
| 61 |
+
This is an auxiliary function performed while merging equivalence classes.
|
| 62 |
+
-/
|
| 63 |
+
private def reinsertParents (parents : ParentSet) : GoalM Unit := do
|
| 64 |
+
for parent in parents do
|
| 65 |
+
if (β pure (isCongrRelevant parent) <&&> isCongrRoot parent) then
|
| 66 |
+
trace_goal[grind.debug.parent] "reinsert: {parent}"
|
| 67 |
+
addCongrTable parent
|
| 68 |
+
|
| 69 |
+
/-- Closes the goal when `True` and `False` are in the same equivalence class. -/
|
| 70 |
+
private def closeGoalWithTrueEqFalse : GoalM Unit := do
|
| 71 |
+
let mvarId := (β get).mvarId
|
| 72 |
+
unless (β mvarId.isAssigned) do
|
| 73 |
+
let trueEqFalse β mkEqFalseProof (β getTrueExpr)
|
| 74 |
+
let falseProof := mkApp4 (mkConst ``Eq.mp [levelZero]) (β getTrueExpr) (β getFalseExpr) trueEqFalse (mkConst ``True.intro)
|
| 75 |
+
closeGoal falseProof
|
| 76 |
+
|
| 77 |
+
/-- Closes the goal when `lhs` and `rhs` are both literal values and belong to the same equivalence class. -/
|
| 78 |
+
private def closeGoalWithValuesEq (lhs rhs : Expr) : GoalM Unit := do
|
| 79 |
+
let p β mkEq lhs rhs
|
| 80 |
+
let hp β mkEqProof lhs rhs
|
| 81 |
+
let d β mkDecide p
|
| 82 |
+
let pEqFalse := mkApp3 (mkConst ``eq_false_of_decide) p d.appArg! (mkApp2 (mkConst ``Eq.refl [1]) (mkConst ``Bool) (mkConst ``false))
|
| 83 |
+
let falseProof := mkApp4 (mkConst ``Eq.mp [levelZero]) p (β getFalseExpr) pEqFalse hp
|
| 84 |
+
closeGoal falseProof
|
| 85 |
+
|
| 86 |
+
/--
|
| 87 |
+
Updates the modification time to `gmt` for the parents of `root`.
|
| 88 |
+
The modification time is used to decide which terms are considered during e-matching.
|
| 89 |
+
-/
|
| 90 |
+
private partial def updateMT (root : Expr) : GoalM Unit := do
|
| 91 |
+
let gmt := (β get).ematch.gmt
|
| 92 |
+
for parent in (β getParents root) do
|
| 93 |
+
let node β getENode parent
|
| 94 |
+
if node.mt < gmt then
|
| 95 |
+
setENode parent { node with mt := gmt }
|
| 96 |
+
updateMT parent
|
| 97 |
+
|
| 98 |
+
/--
|
| 99 |
+
Equalities or disequalities to be propagated to a theory solver **after**
|
| 100 |
+
two equivalence classes have been merged.
|
| 101 |
+
|
| 102 |
+
Some solvers (e.g. `cutsat`) require the core data structures to satisfy
|
| 103 |
+
their invariants. During the merge operations some of these invariants do not hold.
|
| 104 |
+
Thus, we first *record* the facts that must be propagated in a `PendingTheoryPropagation` value,
|
| 105 |
+
complete the merge, and only then perform the propagation.
|
| 106 |
+
|
| 107 |
+
We now use this workflow for *all* theory solvers, even when a particular
|
| 108 |
+
solver does not rely on these invariants. This keeps the core
|
| 109 |
+
solver-agnostic and lets us modify solvers without further adjustments.
|
| 110 |
+
-/
|
| 111 |
+
inductive PendingTheoryPropagation where
|
| 112 |
+
| /-- Nothing to propagate. -/
|
| 113 |
+
none
|
| 114 |
+
| /-- Propagate the equality `lhs = rhs`. -/
|
| 115 |
+
eq (lhs rhs : Expr)
|
| 116 |
+
| /-- Propagate the disequalities in `ps`. -/
|
| 117 |
+
diseqs (ps : ParentSet)
|
| 118 |
+
|
| 119 |
+
/--
|
| 120 |
+
Helper function for combining `ENode.offset?` fields and detecting what needs
|
| 121 |
+
to be propagated to the offset constraint module.
|
| 122 |
+
-/
|
| 123 |
+
private def checkOffsetEq (rhsRoot lhsRoot : ENode) : GoalM PendingTheoryPropagation := do
|
| 124 |
+
match lhsRoot.offset? with
|
| 125 |
+
| some lhsOffset =>
|
| 126 |
+
if let some rhsOffset := rhsRoot.offset? then
|
| 127 |
+
return .eq lhsOffset rhsOffset
|
| 128 |
+
else
|
| 129 |
+
-- We have to retrieve the node because other fields have been updated
|
| 130 |
+
let rhsRoot β getENode rhsRoot.self
|
| 131 |
+
setENode rhsRoot.self { rhsRoot with offset? := lhsOffset }
|
| 132 |
+
return .none
|
| 133 |
+
| none => return .none
|
| 134 |
+
|
| 135 |
+
def propagateOffset : PendingTheoryPropagation β GoalM Unit
|
| 136 |
+
| .eq lhs rhs => Arith.Offset.processNewEq lhs rhs
|
| 137 |
+
| _ => return ()
|
| 138 |
+
|
| 139 |
+
/--
|
| 140 |
+
Helper function for combining `ENode.cutsat?` fields and detecting what needs
|
| 141 |
+
to be propagated to the cutsat module.
|
| 142 |
+
-/
|
| 143 |
+
private def checkCutsatEq (rhsRoot lhsRoot : ENode) : GoalM PendingTheoryPropagation := do
|
| 144 |
+
match lhsRoot.cutsat? with
|
| 145 |
+
| some lhsCutsat =>
|
| 146 |
+
if let some rhsCutsat := rhsRoot.cutsat? then
|
| 147 |
+
return .eq lhsCutsat rhsCutsat
|
| 148 |
+
else
|
| 149 |
+
-- We have to retrieve the node because other fields have been updated
|
| 150 |
+
let rhsRoot β getENode rhsRoot.self
|
| 151 |
+
setENode rhsRoot.self { rhsRoot with cutsat? := lhsCutsat }
|
| 152 |
+
return .diseqs (β getParents rhsRoot.self)
|
| 153 |
+
| none =>
|
| 154 |
+
if rhsRoot.cutsat?.isSome then
|
| 155 |
+
return .diseqs (β getParents lhsRoot.self)
|
| 156 |
+
else
|
| 157 |
+
return .none
|
| 158 |
+
|
| 159 |
+
def propagateCutsat : PendingTheoryPropagation β GoalM Unit
|
| 160 |
+
| .eq lhs rhs => Arith.Cutsat.processNewEq lhs rhs
|
| 161 |
+
| .diseqs ps => propagateCutsatDiseqs ps
|
| 162 |
+
| .none => return ()
|
| 163 |
+
|
| 164 |
+
/--
|
| 165 |
+
Helper function for combining `ENode.ring?` fields and detecting what needs to be
|
| 166 |
+
propagated to the commutative ring module.
|
| 167 |
+
-/
|
| 168 |
+
private def checkCommRingEq (rhsRoot lhsRoot : ENode) : GoalM PendingTheoryPropagation := do
|
| 169 |
+
match lhsRoot.ring? with
|
| 170 |
+
| some lhsRing =>
|
| 171 |
+
if let some rhsRing := rhsRoot.ring? then
|
| 172 |
+
return .eq lhsRing rhsRing
|
| 173 |
+
else
|
| 174 |
+
-- We have to retrieve the node because other fields have been updated
|
| 175 |
+
let rhsRoot β getENode rhsRoot.self
|
| 176 |
+
setENode rhsRoot.self { rhsRoot with ring? := lhsRing }
|
| 177 |
+
return .diseqs (β getParents rhsRoot.self)
|
| 178 |
+
| none =>
|
| 179 |
+
if rhsRoot.ring?.isSome then
|
| 180 |
+
return .diseqs (β getParents lhsRoot.self)
|
| 181 |
+
else
|
| 182 |
+
return .none
|
| 183 |
+
|
| 184 |
+
def propagateCommRing : PendingTheoryPropagation β GoalM Unit
|
| 185 |
+
| .eq lhs rhs => Arith.CommRing.processNewEq lhs rhs
|
| 186 |
+
| .diseqs ps => propagateCommRingDiseqs ps
|
| 187 |
+
| _ => return ()
|
| 188 |
+
|
| 189 |
+
/--
|
| 190 |
+
Helper function for combining `ENode.linarith?` fields and detecting what needs to be
|
| 191 |
+
propagated to the linarith module.
|
| 192 |
+
-/
|
| 193 |
+
private def checkLinarithEq (rhsRoot lhsRoot : ENode) : GoalM PendingTheoryPropagation := do
|
| 194 |
+
match lhsRoot.linarith? with
|
| 195 |
+
| some lhs =>
|
| 196 |
+
if let some rhs := rhsRoot.linarith? then
|
| 197 |
+
return .eq lhs rhs
|
| 198 |
+
else
|
| 199 |
+
-- We have to retrieve the node because other fields have been updated
|
| 200 |
+
let rhsRoot β getENode rhsRoot.self
|
| 201 |
+
setENode rhsRoot.self { rhsRoot with linarith? := lhs }
|
| 202 |
+
return .diseqs (β getParents rhsRoot.self)
|
| 203 |
+
| none =>
|
| 204 |
+
if rhsRoot.linarith?.isSome then
|
| 205 |
+
return .diseqs (β getParents lhsRoot.self)
|
| 206 |
+
else
|
| 207 |
+
return .none
|
| 208 |
+
|
| 209 |
+
def propagateLinarith : PendingTheoryPropagation β GoalM Unit
|
| 210 |
+
| .eq lhs rhs => Arith.Linear.processNewEq lhs rhs
|
| 211 |
+
| .diseqs ps => propagateLinarithDiseqs ps
|
| 212 |
+
| _ => return ()
|
| 213 |
+
|
| 214 |
+
/--
|
| 215 |
+
Tries to apply beta-reductiong using the parent applications of the functions in `fns` with
|
| 216 |
+
the lambda expressions in `lams`.
|
| 217 |
+
-/
|
| 218 |
+
def propagateBeta (lams : Array Expr) (fns : Array Expr) : GoalM Unit := do
|
| 219 |
+
if lams.isEmpty then return ()
|
| 220 |
+
let lamRoot β getRoot lams.back!
|
| 221 |
+
trace_goal[grind.debug.beta] "fns: {fns}, lams: {lams}"
|
| 222 |
+
for fn in fns do
|
| 223 |
+
trace_goal[grind.debug.beta] "fn: {fn}, parents: {(β getParents fn).toArray}"
|
| 224 |
+
for parent in (β getParents fn) do
|
| 225 |
+
let mut args := #[]
|
| 226 |
+
let mut curr := parent
|
| 227 |
+
trace_goal[grind.debug.beta] "parent: {parent}"
|
| 228 |
+
repeat
|
| 229 |
+
trace_goal[grind.debug.beta] "curr: {curr}"
|
| 230 |
+
if (β isEqv curr lamRoot) then
|
| 231 |
+
propagateBetaEqs lams curr args.reverse
|
| 232 |
+
let .app f arg := curr
|
| 233 |
+
| break
|
| 234 |
+
-- Remark: recall that we do not eagerly internalize partial applications.
|
| 235 |
+
internalize curr (β getGeneration parent)
|
| 236 |
+
args := args.push arg
|
| 237 |
+
curr := f
|
| 238 |
+
|
| 239 |
+
private partial def addEqStep (lhs rhs proof : Expr) (isHEq : Bool) : GoalM Unit := do
|
| 240 |
+
let lhsNode β getENode lhs
|
| 241 |
+
let rhsNode β getENode rhs
|
| 242 |
+
if isSameExpr lhsNode.root rhsNode.root then
|
| 243 |
+
-- `lhs` and `rhs` are already in the same equivalence class.
|
| 244 |
+
trace_goal[grind.debug] "{β ppENodeRef lhs} and {β ppENodeRef rhs} are already in the same equivalence class"
|
| 245 |
+
return ()
|
| 246 |
+
trace_goal[grind.eqc] "{β if isHEq then mkHEq lhs rhs else mkEq lhs rhs}"
|
| 247 |
+
let lhsRoot β getENode lhsNode.root
|
| 248 |
+
let rhsRoot β getENode rhsNode.root
|
| 249 |
+
let mut valueInconsistency := false
|
| 250 |
+
let mut trueEqFalse := false
|
| 251 |
+
if lhsRoot.interpreted && rhsRoot.interpreted then
|
| 252 |
+
if lhsNode.root.isTrue || rhsNode.root.isTrue then
|
| 253 |
+
markAsInconsistent
|
| 254 |
+
trueEqFalse := true
|
| 255 |
+
else
|
| 256 |
+
valueInconsistency := true
|
| 257 |
+
if (lhsRoot.interpreted && !rhsRoot.interpreted)
|
| 258 |
+
|| (lhsRoot.ctor && !rhsRoot.ctor)
|
| 259 |
+
|| (lhsRoot.size > rhsRoot.size && !rhsRoot.interpreted && !rhsRoot.ctor) then
|
| 260 |
+
go rhs lhs rhsNode lhsNode rhsRoot lhsRoot true
|
| 261 |
+
else
|
| 262 |
+
go lhs rhs lhsNode rhsNode lhsRoot rhsRoot false
|
| 263 |
+
if trueEqFalse then
|
| 264 |
+
closeGoalWithTrueEqFalse
|
| 265 |
+
unless (β isInconsistent) do
|
| 266 |
+
if lhsRoot.ctor && rhsRoot.ctor then
|
| 267 |
+
propagateCtor lhsRoot.self rhsRoot.self
|
| 268 |
+
unless (β isInconsistent) do
|
| 269 |
+
if valueInconsistency then
|
| 270 |
+
closeGoalWithValuesEq lhsRoot.self rhsRoot.self
|
| 271 |
+
trace_goal[grind.debug] "after addEqStep, {β (β get).ppState}"
|
| 272 |
+
checkInvariants
|
| 273 |
+
where
|
| 274 |
+
go (lhs rhs : Expr) (lhsNode rhsNode lhsRoot rhsRoot : ENode) (flipped : Bool) : GoalM Unit := do
|
| 275 |
+
trace_goal[grind.debug] "adding {β ppENodeRef lhs} β¦ {β ppENodeRef rhs}"
|
| 276 |
+
/-
|
| 277 |
+
We have the following `target?/proof?`
|
| 278 |
+
`lhs -> ... -> lhsNode.root`
|
| 279 |
+
`rhs -> ... -> rhsNode.root`
|
| 280 |
+
We want to convert it to
|
| 281 |
+
`lhsNode.root -> ... -> lhs -*-> rhs -> ... -> rhsNode.root`
|
| 282 |
+
where step `-*->` is justified by `proof` (or `proof.symm` if `flipped := true`)
|
| 283 |
+
-/
|
| 284 |
+
invertTrans lhs
|
| 285 |
+
setENode lhs { lhsNode with
|
| 286 |
+
target? := rhs
|
| 287 |
+
proof? := proof
|
| 288 |
+
flipped
|
| 289 |
+
}
|
| 290 |
+
let lamsβ β getEqcLambdas lhsRoot
|
| 291 |
+
let lamsβ β getEqcLambdas rhsRoot
|
| 292 |
+
let fnsβ β if lamsβ.isEmpty then pure #[] else getFnRoots rhsRoot.self
|
| 293 |
+
let fnsβ β if lamsβ.isEmpty then pure #[] else getFnRoots lhsRoot.self
|
| 294 |
+
let parents β removeParents lhsRoot.self
|
| 295 |
+
updateRoots lhs rhsNode.root
|
| 296 |
+
trace_goal[grind.debug] "{β ppENodeRef lhs} new root {β ppENodeRef rhsNode.root}, {β ppENodeRef (β getRoot lhs)}"
|
| 297 |
+
reinsertParents parents
|
| 298 |
+
/-
|
| 299 |
+
Remark: we used to `propagateDown` here, but this was problematic
|
| 300 |
+
because it limits what the propagator can do because several invariants do not
|
| 301 |
+
hold until we complete all updates.
|
| 302 |
+
-/
|
| 303 |
+
-- TODO: improve performance: we only need to collect terms that may propagate.
|
| 304 |
+
let toPropagateDown β getEqc lhs
|
| 305 |
+
setENode lhsNode.root { (β getENode lhsRoot.self) with -- We must retrieve `lhsRoot` since it was updated.
|
| 306 |
+
next := rhsRoot.next
|
| 307 |
+
}
|
| 308 |
+
setENode rhsNode.root { rhsRoot with
|
| 309 |
+
next := lhsRoot.next
|
| 310 |
+
size := rhsRoot.size + lhsRoot.size
|
| 311 |
+
hasLambdas := rhsRoot.hasLambdas || lhsRoot.hasLambdas
|
| 312 |
+
heqProofs := isHEq || rhsRoot.heqProofs || lhsRoot.heqProofs
|
| 313 |
+
}
|
| 314 |
+
propagateBeta lamsβ fnsβ
|
| 315 |
+
propagateBeta lamsβ fnsβ
|
| 316 |
+
let offsetTodo β checkOffsetEq rhsRoot lhsRoot
|
| 317 |
+
let cutsatTodo β checkCutsatEq rhsRoot lhsRoot
|
| 318 |
+
let ringTodo β checkCommRingEq rhsRoot lhsRoot
|
| 319 |
+
let linarithTodo β checkLinarithEq rhsRoot lhsRoot
|
| 320 |
+
resetParentsOf lhsRoot.self
|
| 321 |
+
copyParentsTo parents rhsNode.root
|
| 322 |
+
unless (β isInconsistent) do
|
| 323 |
+
updateMT rhsRoot.self
|
| 324 |
+
unless (β isInconsistent) do
|
| 325 |
+
for parent in parents do
|
| 326 |
+
propagateUp parent
|
| 327 |
+
for e in toPropagateDown do
|
| 328 |
+
propagateDown e
|
| 329 |
+
propagateOffset offsetTodo
|
| 330 |
+
propagateCutsat cutsatTodo
|
| 331 |
+
propagateCommRing ringTodo
|
| 332 |
+
propagateLinarith linarithTodo
|
| 333 |
+
updateRoots (lhs : Expr) (rootNew : Expr) : GoalM Unit := do
|
| 334 |
+
traverseEqc lhs fun n =>
|
| 335 |
+
setENode n.self { n with root := rootNew }
|
| 336 |
+
|
| 337 |
+
/-- Ensures collection of equations to be processed is empty. -/
|
| 338 |
+
private def resetNewFacts : GoalM Unit :=
|
| 339 |
+
modify fun s => { s with newFacts := #[] }
|
| 340 |
+
|
| 341 |
+
/-- Pops and returns the next equality to be processed. -/
|
| 342 |
+
private def popNextFact? : GoalM (Option NewFact) := do
|
| 343 |
+
let r := (β get).newFacts.back?
|
| 344 |
+
if r.isSome then
|
| 345 |
+
modify fun s => { s with newFacts := s.newFacts.pop }
|
| 346 |
+
return r
|
| 347 |
+
|
| 348 |
+
private def addEqCore (lhs rhs proof : Expr) (isHEq : Bool) : GoalM Unit := do
|
| 349 |
+
addEqStep lhs rhs proof isHEq
|
| 350 |
+
processNewFacts
|
| 351 |
+
|
| 352 |
+
/-- Adds a new equality `lhs = rhs`. It assumes `lhs` and `rhs` have already been internalized. -/
|
| 353 |
+
private def addEq (lhs rhs proof : Expr) : GoalM Unit := do
|
| 354 |
+
addEqCore lhs rhs proof false
|
| 355 |
+
|
| 356 |
+
/-- Adds a new heterogeneous equality `lhs β rhs`. It assumes `lhs` and `rhs` have already been internalized. -/
|
| 357 |
+
private def addHEq (lhs rhs proof : Expr) : GoalM Unit := do
|
| 358 |
+
addEqCore lhs rhs proof true
|
| 359 |
+
|
| 360 |
+
/-- Save asserted facts for pretty printing goal. -/
|
| 361 |
+
private def storeFact (fact : Expr) : GoalM Unit := do
|
| 362 |
+
modify fun s => { s with facts := s.facts.push fact }
|
| 363 |
+
|
| 364 |
+
/-- Internalizes `lhs` and `rhs`, and then adds equality `lhs = rhs`. -/
|
| 365 |
+
def addNewEq (lhs rhs proof : Expr) (generation : Nat) : GoalM Unit := do
|
| 366 |
+
let eq β mkEq lhs rhs
|
| 367 |
+
storeFact eq
|
| 368 |
+
internalize lhs generation eq
|
| 369 |
+
internalize rhs generation eq
|
| 370 |
+
addEq lhs rhs proof
|
| 371 |
+
|
| 372 |
+
private def addFactStep (fact : Expr) (proof : Expr) (generation : Nat) : GoalM Unit := do
|
| 373 |
+
storeFact fact
|
| 374 |
+
trace_goal[grind.assert] "{fact}"
|
| 375 |
+
let_expr Not p := fact
|
| 376 |
+
| go fact false
|
| 377 |
+
go p true
|
| 378 |
+
where
|
| 379 |
+
go (p : Expr) (isNeg : Bool) : GoalM Unit := do
|
| 380 |
+
match_expr p with
|
| 381 |
+
| Eq Ξ± lhs rhs =>
|
| 382 |
+
if Ξ±.isProp then
|
| 383 |
+
-- It is morally an iff.
|
| 384 |
+
-- We do not use the `goEq` optimization because we want to register `p` as a case-split
|
| 385 |
+
goFact p isNeg
|
| 386 |
+
else
|
| 387 |
+
goEq p lhs rhs isNeg false
|
| 388 |
+
| HEq _ lhs _ rhs => goEq p lhs rhs isNeg true
|
| 389 |
+
| _ => goFact p isNeg
|
| 390 |
+
|
| 391 |
+
goFact (p : Expr) (isNeg : Bool) : GoalM Unit := do
|
| 392 |
+
internalize p generation
|
| 393 |
+
if isNeg then
|
| 394 |
+
addEq p (β getFalseExpr) (β mkEqFalse proof)
|
| 395 |
+
else
|
| 396 |
+
addEq p (β getTrueExpr) (β mkEqTrue proof)
|
| 397 |
+
|
| 398 |
+
goEq (p : Expr) (lhs rhs : Expr) (isNeg : Bool) (isHEq : Bool) : GoalM Unit := do
|
| 399 |
+
if isNeg then
|
| 400 |
+
internalize p generation
|
| 401 |
+
addEq p (β getFalseExpr) (β mkEqFalse proof)
|
| 402 |
+
else
|
| 403 |
+
internalize lhs generation p
|
| 404 |
+
internalize rhs generation p
|
| 405 |
+
addEqCore lhs rhs proof isHEq
|
| 406 |
+
|
| 407 |
+
@[export lean_grind_process_new_facts]
|
| 408 |
+
private def processNewFactsImpl : GoalM Unit := do
|
| 409 |
+
repeat
|
| 410 |
+
if (β isInconsistent) then
|
| 411 |
+
resetNewFacts
|
| 412 |
+
return ()
|
| 413 |
+
checkSystem "grind"
|
| 414 |
+
let some next := (β popNextFact?)
|
| 415 |
+
| return ()
|
| 416 |
+
match next with
|
| 417 |
+
| .eq lhs rhs proof isHEq => addEqStep lhs rhs proof isHEq
|
| 418 |
+
| .fact prop proof gen => addFactStep prop proof gen
|
| 419 |
+
|
| 420 |
+
/-- Adds a new `fact` justified by the given proof and using the given generation. -/
|
| 421 |
+
def add (fact : Expr) (proof : Expr) (generation := 0) : GoalM Unit := do
|
| 422 |
+
if fact.isTrue then return ()
|
| 423 |
+
if (β isInconsistent) then return ()
|
| 424 |
+
resetNewFacts
|
| 425 |
+
addFactStep fact proof generation
|
| 426 |
+
|
| 427 |
+
/-- Adds a new hypothesis. -/
|
| 428 |
+
def addHypothesis (fvarId : FVarId) (generation := 0) : GoalM Unit := do
|
| 429 |
+
add (β fvarId.getType) (mkFVar fvarId) generation
|
| 430 |
+
|
| 431 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Ctor.lean
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 8 |
+
import Lean.Meta.Tactic.Grind.ProveEq
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta.Grind
|
| 11 |
+
|
| 12 |
+
private partial def propagateInjEqs (eqs : Expr) (proof : Expr) : GoalM Unit := do
|
| 13 |
+
-- Remark: we must use `shareCommon` before using `pushEq` and `pushHEq`.
|
| 14 |
+
-- This is needed because the result type of the injection theorem may allocate
|
| 15 |
+
match_expr eqs with
|
| 16 |
+
| And left right =>
|
| 17 |
+
propagateInjEqs left (.proj ``And 0 proof)
|
| 18 |
+
propagateInjEqs right (.proj ``And 1 proof)
|
| 19 |
+
| Eq _ lhs rhs =>
|
| 20 |
+
pushEq (β preprocessLight lhs) (β preprocessLight rhs) proof
|
| 21 |
+
| HEq _ lhs _ rhs =>
|
| 22 |
+
pushHEq (β preprocessLight lhs) (β preprocessLight rhs) proof
|
| 23 |
+
| _ =>
|
| 24 |
+
reportIssue! "unexpected injectivity theorem result type{indentExpr eqs}"
|
| 25 |
+
return ()
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
Given constructors `a` and `b`, propagate equalities if they are the same,
|
| 29 |
+
and close goal if they are different.
|
| 30 |
+
-/
|
| 31 |
+
def propagateCtor (a b : Expr) : GoalM Unit := do
|
| 32 |
+
let aType β whnfD (β inferType a)
|
| 33 |
+
let bType β whnfD (β inferType b)
|
| 34 |
+
unless (β withDefault <| isDefEq aType bType) do
|
| 35 |
+
return ()
|
| 36 |
+
let ctorβ := a.getAppFn
|
| 37 |
+
let ctorβ := b.getAppFn
|
| 38 |
+
if ctorβ == ctorβ then
|
| 39 |
+
let .const ctorName _ := a.getAppFn | return ()
|
| 40 |
+
let injDeclName := Name.mkStr ctorName "inj"
|
| 41 |
+
unless (β getEnv).contains injDeclName do return ()
|
| 42 |
+
let info β getConstInfo injDeclName
|
| 43 |
+
let n := info.type.getForallArity
|
| 44 |
+
let mask : Array (Option Expr) := .replicate n none
|
| 45 |
+
/-
|
| 46 |
+
We use `mkExpectedTypeHint` here to ensure that `mkAppOptM` will "fill" the implicit
|
| 47 |
+
arguments of `injDeclName` using exactly the fields of `a` and `b`.
|
| 48 |
+
There is no guarantee that `inferType (β mkEqProof a b)` is structurally equal to `a = b`.
|
| 49 |
+
-/
|
| 50 |
+
let mask := mask.set! (n-1) (some (β mkExpectedTypeHint (β mkEqProof a b) (β mkEq a b)))
|
| 51 |
+
let injLemma β mkAppOptM injDeclName mask
|
| 52 |
+
let injLemmaType β inferType injLemma
|
| 53 |
+
propagateInjEqs injLemmaType injLemma
|
| 54 |
+
else
|
| 55 |
+
let .const declName _ := aType.getAppFn | return ()
|
| 56 |
+
let noConfusionDeclName := Name.mkStr declName "noConfusion"
|
| 57 |
+
unless (β getEnv).contains noConfusionDeclName do return ()
|
| 58 |
+
closeGoal (β mkNoConfusion (β getFalseExpr) (β mkEqProof a b))
|
| 59 |
+
|
| 60 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Diseq.lean
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Grind.Lemmas
|
| 8 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta.Grind
|
| 11 |
+
|
| 12 |
+
/--
|
| 13 |
+
Returns `some (c = d)` if
|
| 14 |
+
- `c = d` and `False` are in the same equivalence class, and
|
| 15 |
+
- `a` (`b`) and `c` are in the same equivalence class, and
|
| 16 |
+
- `b` (`a`) and `d` are in the same equivalence class.
|
| 17 |
+
Otherwise return `none`.
|
| 18 |
+
|
| 19 |
+
Remark `a` and `b` are assumed to have the same type.
|
| 20 |
+
-/
|
| 21 |
+
private def getDiseqFor? (a b : Expr) : GoalM (Option Expr) := do
|
| 22 |
+
/-
|
| 23 |
+
In Z3, we use the congruence table to find equalities more efficiently,
|
| 24 |
+
but this optimization would be more complicated here because equalities have
|
| 25 |
+
the type as an implicit argument, and `grind`s congruence table assumes it is
|
| 26 |
+
hash-consed and canonicalized. So, we use the "slower" approach of visiting
|
| 27 |
+
parents.
|
| 28 |
+
-/
|
| 29 |
+
let aRoot β getRoot a
|
| 30 |
+
let bRoot β getRoot b
|
| 31 |
+
let aParents β getParents aRoot
|
| 32 |
+
let bParents β getParents bRoot
|
| 33 |
+
if aParents.size β€ bParents.size then
|
| 34 |
+
go aParents
|
| 35 |
+
else
|
| 36 |
+
go bParents
|
| 37 |
+
where
|
| 38 |
+
go (parents : ParentSet) : GoalM (Option Expr) := do
|
| 39 |
+
for parent in parents do
|
| 40 |
+
let_expr Eq Ξ± c d := parent | continue
|
| 41 |
+
if (β isEqFalse parent) then
|
| 42 |
+
-- Remark: we expect `hasType` test to seldom fail, but it can happen because of
|
| 43 |
+
-- heterogeneous equalities
|
| 44 |
+
if (β isEqv a c <&&> isEqv b d <&&> hasType a Ξ±) then
|
| 45 |
+
return some parent
|
| 46 |
+
if (β isEqv a d <&&> isEqv b c <&&> hasType a Ξ±) then
|
| 47 |
+
return some parent
|
| 48 |
+
return none
|
| 49 |
+
|
| 50 |
+
/--
|
| 51 |
+
Returns `true` if `a` and `b` are known to be disequal.
|
| 52 |
+
See `getDiseqFor?`
|
| 53 |
+
-/
|
| 54 |
+
def isDiseq (a b : Expr) : GoalM Bool := do
|
| 55 |
+
return (β getDiseqFor? a b).isSome
|
| 56 |
+
|
| 57 |
+
/--
|
| 58 |
+
Returns a proof for `true` if `a` and `b` are known to be disequal.
|
| 59 |
+
See `getDiseqFor?`
|
| 60 |
+
-/
|
| 61 |
+
def mkDiseqProof? (a b : Expr) : GoalM (Option Expr) := do
|
| 62 |
+
let some eq β getDiseqFor? a b | return none
|
| 63 |
+
let_expr f@Eq Ξ± c d := eq | unreachable!
|
| 64 |
+
let u := f.constLevels!
|
| 65 |
+
let h β mkOfEqFalse (β mkEqFalseProof eq)
|
| 66 |
+
let (c, d, h) β if (β isEqv a c <&&> isEqv b d) then
|
| 67 |
+
pure (c, d, h)
|
| 68 |
+
else
|
| 69 |
+
pure (d, c, mkApp4 (mkConst ``Ne.symm u) Ξ± c d h)
|
| 70 |
+
-- We have `a = c` and `b = d`
|
| 71 |
+
let h β if isSameExpr a c then
|
| 72 |
+
pure h
|
| 73 |
+
else
|
| 74 |
+
pure <| mkApp6 (mkConst ``Grind.ne_of_ne_of_eq_left u) Ξ± a c d (β mkEqProof a c) h
|
| 75 |
+
-- `h : a β d
|
| 76 |
+
if isSameExpr b d then
|
| 77 |
+
return h
|
| 78 |
+
else
|
| 79 |
+
return mkApp6 (mkConst ``Grind.ne_of_ne_of_eq_right u) Ξ± b a d (β mkEqProof b d) h
|
| 80 |
+
|
| 81 |
+
def mkDiseqProof (a b : Expr) : GoalM Expr := do
|
| 82 |
+
let some h β mkDiseqProof? a b
|
| 83 |
+
| throwError "internal `grind` error, failed to build disequality proof for{indentExpr a}\nand{indentExpr b}"
|
| 84 |
+
return h
|
| 85 |
+
|
| 86 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EMatch.lean
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 8 |
+
import Lean.Meta.Tactic.Grind.Intro
|
| 9 |
+
import Lean.Meta.Tactic.Grind.MatchDiscrOnly
|
| 10 |
+
import Lean.Meta.Tactic.Grind.MatchCond
|
| 11 |
+
import Lean.Meta.Tactic.Grind.Core
|
| 12 |
+
|
| 13 |
+
namespace Lean.Meta.Grind
|
| 14 |
+
namespace EMatch
|
| 15 |
+
/-! This module implements a simple E-matching procedure as a backtracking search. -/
|
| 16 |
+
|
| 17 |
+
/-- We represent an `E-matching` problem as a list of constraints. -/
|
| 18 |
+
inductive Cnstr where
|
| 19 |
+
| /-- Matches pattern `pat` with term `e` -/
|
| 20 |
+
Β«matchΒ» (gen? : Option GenPatternInfo) (pat : Expr) (e : Expr)
|
| 21 |
+
| /-- Matches offset pattern `pat+k` with term `e` -/
|
| 22 |
+
offset (gen? : Option GenPatternInfo) (pat : Expr) (k : Nat) (e : Expr)
|
| 23 |
+
| /-- This constraint is used to encode multi-patterns. -/
|
| 24 |
+
Β«continueΒ» (pat : Expr)
|
| 25 |
+
deriving Inhabited
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
Internal "marker" for representing unassigned elements in the `assignment` field.
|
| 29 |
+
This is a small hack to avoid one extra level of indirection by using `Option Expr` at `assignment`.
|
| 30 |
+
-/
|
| 31 |
+
private def unassigned : Expr := mkConst (Name.mkSimple "[grind_unassigned]")
|
| 32 |
+
|
| 33 |
+
/--
|
| 34 |
+
Internal "marker" for representing equality proofs for generalized patterns.
|
| 35 |
+
They must be synthesized after we have the partial assignment.
|
| 36 |
+
-/
|
| 37 |
+
private def delayedEqProof : Expr := mkConst (Name.mkSimple "[grind_delayed_eq_proof]")
|
| 38 |
+
|
| 39 |
+
private def assignmentToMessageData (assignment : Array Expr) : Array MessageData :=
|
| 40 |
+
assignment.reverse.map fun e =>
|
| 41 |
+
if isSameExpr e unassigned then m!"_" else m!"{e}"
|
| 42 |
+
|
| 43 |
+
/--
|
| 44 |
+
Choice point for the backtracking search.
|
| 45 |
+
The state of the procedure contains a stack of choices.
|
| 46 |
+
-/
|
| 47 |
+
structure Choice where
|
| 48 |
+
/-- Constraints to be processed. -/
|
| 49 |
+
cnstrs : List Cnstr
|
| 50 |
+
/-- Maximum term generation found so far. -/
|
| 51 |
+
gen : Nat
|
| 52 |
+
/-- Partial assignment so far. Recall that pattern variables are encoded as de-Bruijn variables. -/
|
| 53 |
+
assignment : Array Expr
|
| 54 |
+
deriving Inhabited
|
| 55 |
+
|
| 56 |
+
/-- Context for the E-matching monad. -/
|
| 57 |
+
structure Context where
|
| 58 |
+
/-- `useMT` is `true` if we are using the mod-time optimization. It is always set to false for new `EMatchTheorem`s. -/
|
| 59 |
+
useMT : Bool := true
|
| 60 |
+
/-- `EMatchTheorem` being processed. -/
|
| 61 |
+
thm : EMatchTheorem := default
|
| 62 |
+
/-- Initial application used to start E-matching -/
|
| 63 |
+
initApp : Expr := default
|
| 64 |
+
deriving Inhabited
|
| 65 |
+
|
| 66 |
+
/-- State for the E-matching monad -/
|
| 67 |
+
structure SearchState where
|
| 68 |
+
/-- Choices that still have to be processed. -/
|
| 69 |
+
choiceStack : List Choice := []
|
| 70 |
+
deriving Inhabited
|
| 71 |
+
|
| 72 |
+
abbrev M := ReaderT Context $ StateRefT SearchState GoalM
|
| 73 |
+
|
| 74 |
+
def M.run' (x : M Ξ±) : GoalM Ξ± :=
|
| 75 |
+
x {} |>.run' {}
|
| 76 |
+
|
| 77 |
+
@[inline] private abbrev withInitApp (e : Expr) (x : M Ξ±) : M Ξ± :=
|
| 78 |
+
withReader (fun ctx => { ctx with initApp := e }) x
|
| 79 |
+
|
| 80 |
+
/--
|
| 81 |
+
Assigns `bidx := e` in `c`. If `bidx` is already assigned in `c`, we check whether
|
| 82 |
+
`e` and `c.assignment[bidx]` are in the same equivalence class.
|
| 83 |
+
This function assumes `bidx < c.assignment.size`.
|
| 84 |
+
Recall that we initialize the assignment array with the number of theorem parameters.
|
| 85 |
+
-/
|
| 86 |
+
private def assign? (c : Choice) (bidx : Nat) (e : Expr) : OptionT GoalM Choice := do
|
| 87 |
+
if h : bidx < c.assignment.size then
|
| 88 |
+
let v := c.assignment[bidx]
|
| 89 |
+
if isSameExpr v unassigned then
|
| 90 |
+
return { c with assignment := c.assignment.set bidx e }
|
| 91 |
+
else
|
| 92 |
+
guard (β isEqv v e)
|
| 93 |
+
return c
|
| 94 |
+
else
|
| 95 |
+
-- `Choice` was not properly initialized
|
| 96 |
+
unreachable!
|
| 97 |
+
|
| 98 |
+
/--
|
| 99 |
+
Assigns `bidx` with the marker for a delayed equality proof for generalized patterns.
|
| 100 |
+
The proof is assigned after we have the complete assignment.
|
| 101 |
+
-/
|
| 102 |
+
private def assignDelayedEqProof? (c : Choice) (bidx : Nat) : OptionT GoalM Choice := do
|
| 103 |
+
if h : bidx < c.assignment.size then
|
| 104 |
+
let v := c.assignment[bidx]
|
| 105 |
+
if isSameExpr v unassigned then
|
| 106 |
+
return { c with assignment := c.assignment.set bidx delayedEqProof }
|
| 107 |
+
else
|
| 108 |
+
return c
|
| 109 |
+
else
|
| 110 |
+
-- `Choice` was not properly initialized
|
| 111 |
+
unreachable!
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
private def unassign (c : Choice) (bidx : Nat) : Choice :=
|
| 115 |
+
{ c with assignment := c.assignment.set! bidx unassigned }
|
| 116 |
+
|
| 117 |
+
/--
|
| 118 |
+
Returns `true` if the function `pFn` of a pattern is equivalent to the function `eFn`.
|
| 119 |
+
Recall that we ignore universe levels in patterns.
|
| 120 |
+
-/
|
| 121 |
+
private def eqvFunctions (pFn eFn : Expr) : Bool :=
|
| 122 |
+
(pFn.isFVar && pFn == eFn)
|
| 123 |
+
|| (pFn.isConst && eFn.isConstOf pFn.constName!)
|
| 124 |
+
|
| 125 |
+
protected def _root_.Lean.Meta.Grind.GenPatternInfo.assign? (genInfo : GenPatternInfo) (c : Choice) (x : Expr) : OptionT GoalM Choice := do
|
| 126 |
+
let c β assign? c genInfo.xIdx x
|
| 127 |
+
let c β assignDelayedEqProof? c genInfo.hIdx
|
| 128 |
+
return c
|
| 129 |
+
|
| 130 |
+
/-- Matches a pattern argument. See `matchArgs?`. -/
|
| 131 |
+
private def matchArg? (c : Choice) (pArg : Expr) (eArg : Expr) : OptionT GoalM Choice := do
|
| 132 |
+
if isPatternDontCare pArg then
|
| 133 |
+
return c
|
| 134 |
+
else if pArg.isBVar then
|
| 135 |
+
assign? c pArg.bvarIdx! eArg
|
| 136 |
+
else if let some pArg := groundPattern? pArg then
|
| 137 |
+
/-
|
| 138 |
+
We need to use `withReducibleAndIntances` because ground patterns are often instances.
|
| 139 |
+
Here is an example
|
| 140 |
+
```
|
| 141 |
+
instance : Max Nat where
|
| 142 |
+
max := Nat.max -- Redefined the instance
|
| 143 |
+
|
| 144 |
+
example (a : Nat) : max a a = a := by
|
| 145 |
+
grind
|
| 146 |
+
```
|
| 147 |
+
Possible future improvements:
|
| 148 |
+
- When `diagnostics` is true, try with `withDefault` and report issue if it succeeds.
|
| 149 |
+
- (minor) Only use `withReducibleAndInstances` if the argument is an implicit instance.
|
| 150 |
+
Potential issue: some user write `{_ : Class Ξ±}` when the instance can be inferred from
|
| 151 |
+
explicit arguments.
|
| 152 |
+
-/
|
| 153 |
+
guard (β isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg))
|
| 154 |
+
return c
|
| 155 |
+
else if let some (pArg, k) := isOffsetPattern? pArg then
|
| 156 |
+
assert! Option.isNone <| isOffsetPattern? pArg
|
| 157 |
+
assert! !isPatternDontCare pArg
|
| 158 |
+
return { c with cnstrs := .offset none pArg k eArg :: c.cnstrs }
|
| 159 |
+
else if let some (genInfo, pArg) := isGenPattern? pArg then
|
| 160 |
+
if pArg.isBVar then
|
| 161 |
+
let c β assign? c pArg.bvarIdx! eArg
|
| 162 |
+
genInfo.assign? c eArg
|
| 163 |
+
else if let some pArg := groundPattern? pArg then
|
| 164 |
+
guard (β isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg))
|
| 165 |
+
genInfo.assign? c eArg
|
| 166 |
+
else if let some (pArg, k) := isOffsetPattern? pArg then
|
| 167 |
+
return { c with cnstrs := .offset (some genInfo) pArg k eArg :: c.cnstrs }
|
| 168 |
+
else
|
| 169 |
+
return { c with cnstrs := .match (some genInfo) pArg eArg :: c.cnstrs }
|
| 170 |
+
else
|
| 171 |
+
return { c with cnstrs := .match none pArg eArg :: c.cnstrs }
|
| 172 |
+
|
| 173 |
+
private def Choice.updateGen (c : Choice) (gen : Nat) : Choice :=
|
| 174 |
+
{ c with gen := Nat.max gen c.gen }
|
| 175 |
+
|
| 176 |
+
private def pushChoice (c : Choice) : M Unit :=
|
| 177 |
+
modify fun s => { s with choiceStack := c :: s.choiceStack }
|
| 178 |
+
|
| 179 |
+
/--
|
| 180 |
+
Matches arguments of pattern `p` with term `e`. Returns `some` if successful,
|
| 181 |
+
and `none` otherwise. It may update `c`s assignment and list of constraints to be
|
| 182 |
+
processed.
|
| 183 |
+
-/
|
| 184 |
+
private partial def matchArgs? (c : Choice) (p : Expr) (e : Expr) : OptionT GoalM Choice := do
|
| 185 |
+
if !p.isApp then return c -- Done
|
| 186 |
+
let pArg := p.appArg!
|
| 187 |
+
let eArg := e.appArg!
|
| 188 |
+
let c β matchArg? c pArg eArg
|
| 189 |
+
matchArgs? c p.appFn! e.appFn!
|
| 190 |
+
|
| 191 |
+
/-- Similar to `matchArgs?` but if `p` has fewer arguments than `e`, we match `p` with a prefix of `e`. -/
|
| 192 |
+
private partial def matchArgsPrefix? (c : Choice) (p : Expr) (e : Expr) : OptionT GoalM Choice := do
|
| 193 |
+
let pn := p.getAppNumArgs
|
| 194 |
+
let en := e.getAppNumArgs
|
| 195 |
+
guard (pn <= en)
|
| 196 |
+
if pn == en then
|
| 197 |
+
matchArgs? c p e
|
| 198 |
+
else
|
| 199 |
+
matchArgs? c p (e.getAppPrefix pn)
|
| 200 |
+
|
| 201 |
+
-- Helper function for `processMatch` and `processGenMatch`
|
| 202 |
+
@[inline] private def isCandidate (n : ENode) (pFn : Expr) (pNumArgs : Nat) (maxGeneration : Nat) : Bool :=
|
| 203 |
+
-- Remark: we use `<` because the instance generation is the maximum term generation + 1
|
| 204 |
+
n.generation < maxGeneration
|
| 205 |
+
-- uses heterogeneous equality or is the root of its congruence class
|
| 206 |
+
&& (n.heqProofs || n.isCongrRoot)
|
| 207 |
+
&& eqvFunctions pFn n.self.getAppFn
|
| 208 |
+
&& n.self.getAppNumArgs == pNumArgs
|
| 209 |
+
|
| 210 |
+
private def assignGenInfo? (genInfo? : Option GenPatternInfo) (c : Choice) (x : Expr) : OptionT GoalM Choice := do
|
| 211 |
+
let some genInfo := genInfo? | return c
|
| 212 |
+
genInfo.assign? c x
|
| 213 |
+
|
| 214 |
+
/--
|
| 215 |
+
Matches pattern `p` with term `e` with respect to choice `c`.
|
| 216 |
+
We traverse the equivalence class of `e` looking for applications compatible with `p`.
|
| 217 |
+
For each candidate application, we match the arguments and may update `c`s assignments and constraints.
|
| 218 |
+
We add the updated choices to the choice stack.
|
| 219 |
+
-/
|
| 220 |
+
private partial def processMatch (c : Choice) (genInfo? : Option GenPatternInfo) (p : Expr) (e : Expr) : M Unit := do
|
| 221 |
+
let maxGeneration β getMaxGeneration
|
| 222 |
+
let pFn := p.getAppFn
|
| 223 |
+
let numArgs := p.getAppNumArgs
|
| 224 |
+
let mut curr := e
|
| 225 |
+
repeat
|
| 226 |
+
let n β getENode curr
|
| 227 |
+
-- Remark: we use `<` because the instance generation is the maximum term generation + 1
|
| 228 |
+
if isCandidate n pFn numArgs maxGeneration then
|
| 229 |
+
if let some c β assignGenInfo? genInfo? c e |>.run then
|
| 230 |
+
if let some c β matchArgs? c p curr |>.run then
|
| 231 |
+
pushChoice (c.updateGen n.generation)
|
| 232 |
+
curr β getNext curr
|
| 233 |
+
if isSameExpr curr e then break
|
| 234 |
+
|
| 235 |
+
/--
|
| 236 |
+
Matches offset pattern `pArg+k` with term `e` with respect to choice `c`.
|
| 237 |
+
-/
|
| 238 |
+
private partial def processOffset (c : Choice) (genInfo? : Option GenPatternInfo) (pArg : Expr) (k : Nat) (e : Expr) : M Unit := do
|
| 239 |
+
let maxGeneration β getMaxGeneration
|
| 240 |
+
let mut curr := e
|
| 241 |
+
repeat
|
| 242 |
+
let n β getENode curr
|
| 243 |
+
if n.generation < maxGeneration then
|
| 244 |
+
if let some (eArg, k') β isOffset? curr |>.run then
|
| 245 |
+
if let some c β assignGenInfo? genInfo? c e |>.run then
|
| 246 |
+
if k' < k then
|
| 247 |
+
let c := c.updateGen n.generation
|
| 248 |
+
pushChoice { c with cnstrs := .offset none pArg (k - k') eArg :: c.cnstrs }
|
| 249 |
+
else if k' == k then
|
| 250 |
+
if let some c β matchArg? c pArg eArg |>.run then
|
| 251 |
+
pushChoice (c.updateGen n.generation)
|
| 252 |
+
else if k' > k then
|
| 253 |
+
let eArg' := mkNatAdd eArg (mkNatLit (k' - k))
|
| 254 |
+
let eArg' β shareCommon (β canon eArg')
|
| 255 |
+
internalize eArg' (n.generation+1)
|
| 256 |
+
if let some c β matchArg? c pArg eArg' |>.run then
|
| 257 |
+
pushChoice (c.updateGen n.generation)
|
| 258 |
+
else if let some k' β evalNat curr |>.run then
|
| 259 |
+
if let some c β assignGenInfo? genInfo? c e |>.run then
|
| 260 |
+
if k' >= k then
|
| 261 |
+
let eArg' := mkNatLit (k' - k)
|
| 262 |
+
let eArg' β shareCommon (β canon eArg')
|
| 263 |
+
internalize eArg' (n.generation+1)
|
| 264 |
+
if let some c β matchArg? c pArg eArg' |>.run then
|
| 265 |
+
pushChoice (c.updateGen n.generation)
|
| 266 |
+
curr β getNext curr
|
| 267 |
+
if isSameExpr curr e then break
|
| 268 |
+
|
| 269 |
+
/--
|
| 270 |
+
Returns "applications" in the given goal that may match `p`.
|
| 271 |
+
We say "applications," because we assume a constant is a zero-ary application.
|
| 272 |
+
-/
|
| 273 |
+
private def getAppsOf (p : Expr) : GoalM (Option (List Expr)) := do
|
| 274 |
+
if p.isConst then
|
| 275 |
+
if (β alreadyInternalized p) then
|
| 276 |
+
return some [p]
|
| 277 |
+
else
|
| 278 |
+
return none
|
| 279 |
+
else
|
| 280 |
+
return (β get).appMap.find? p.toHeadIndex
|
| 281 |
+
|
| 282 |
+
/-- Processes `continue` constraint used to implement multi-patterns. -/
|
| 283 |
+
private def processContinue (c : Choice) (p : Expr) : M Unit := do
|
| 284 |
+
let some apps β getAppsOf p
|
| 285 |
+
| return ()
|
| 286 |
+
let maxGeneration β getMaxGeneration
|
| 287 |
+
for app in apps do
|
| 288 |
+
let n β getENode app
|
| 289 |
+
if n.generation < maxGeneration
|
| 290 |
+
&& (n.heqProofs || n.isCongrRoot) then
|
| 291 |
+
if let some c β matchArgsPrefix? c p app |>.run then
|
| 292 |
+
let gen := n.generation
|
| 293 |
+
let c := { c with gen := Nat.max gen c.gen }
|
| 294 |
+
modify fun s => { s with choiceStack := c :: s.choiceStack }
|
| 295 |
+
|
| 296 |
+
/--
|
| 297 |
+
Given a proposition `prop` corresponding to an equational theorem.
|
| 298 |
+
Annotate the conditions using `Grind.MatchCond`. See `MatchCond.lean`.
|
| 299 |
+
-/
|
| 300 |
+
private partial def annotateEqnTypeConds (prop : Expr) (k : Expr β M Expr := pure) : M Expr := do
|
| 301 |
+
if let .forallE n d b bi := prop then
|
| 302 |
+
let d := if (β isProp d) then
|
| 303 |
+
markAsPreMatchCond d
|
| 304 |
+
else
|
| 305 |
+
d
|
| 306 |
+
withLocalDecl n bi d fun x => do
|
| 307 |
+
mkForallFVars #[x] (β annotateEqnTypeConds (b.instantiate1 x) k)
|
| 308 |
+
else
|
| 309 |
+
k prop
|
| 310 |
+
|
| 311 |
+
/--
|
| 312 |
+
Helper function for marking parts of `match`-equation theorem as "do-not-simplify"
|
| 313 |
+
`initApp` is the match-expression used to instantiate the `match`-equation.
|
| 314 |
+
It also introduce `Grind.MatchCond` at equation conditions. See `MatchCond.lean`.
|
| 315 |
+
-/
|
| 316 |
+
private def annotateMatchEqnType (prop : Expr) (initApp : Expr) : M Expr := do
|
| 317 |
+
annotateEqnTypeConds prop fun prop => do
|
| 318 |
+
let_expr f@Eq Ξ± lhs rhs := prop | return prop
|
| 319 |
+
-- See comment at `Grind.EqMatch`
|
| 320 |
+
let lhs β markAsSimpMatchDiscrsOnly lhs
|
| 321 |
+
return mkApp4 (mkConst ``Grind.EqMatch f.constLevels!) Ξ± lhs rhs initApp
|
| 322 |
+
|
| 323 |
+
/--
|
| 324 |
+
Stores new theorem instance in the state.
|
| 325 |
+
Recall that new instances are internalized later, after a full round of ematching.
|
| 326 |
+
-/
|
| 327 |
+
private def addNewInstance (thm : EMatchTheorem) (proof : Expr) (generation : Nat) : M Unit := do
|
| 328 |
+
let proof β instantiateMVars proof
|
| 329 |
+
if grind.debug.proofs.get (β getOptions) then
|
| 330 |
+
check proof
|
| 331 |
+
let mut prop β inferType proof
|
| 332 |
+
let mut proof := proof
|
| 333 |
+
if (β isMatchEqLikeDeclName thm.origin.key) then
|
| 334 |
+
prop β annotateMatchEqnType prop (β read).initApp
|
| 335 |
+
-- We must add a hint here because `annotateMatchEqnType` introduces `simpMatchDiscrsOnly` and
|
| 336 |
+
-- `Grind.PreMatchCond` which are not reducible.
|
| 337 |
+
proof := mkExpectedPropHint proof prop
|
| 338 |
+
else if (β isEqnThm thm.origin.key) then
|
| 339 |
+
prop β annotateEqnTypeConds prop
|
| 340 |
+
-- We must add a hint because `annotateEqnTypeConds` introduces `Grind.PreMatchCond`
|
| 341 |
+
-- which is not reducible.
|
| 342 |
+
proof := mkExpectedPropHint proof prop
|
| 343 |
+
trace_goal[grind.ematch.instance] "{β thm.origin.pp}: {prop}"
|
| 344 |
+
addTheoremInstance thm proof prop (generation+1)
|
| 345 |
+
|
| 346 |
+
private def synthesizeInsts (mvars : Array Expr) (bis : Array BinderInfo) : OptionT M Unit := do
|
| 347 |
+
let thm := (β read).thm
|
| 348 |
+
for mvar in mvars, bi in bis do
|
| 349 |
+
if bi.isInstImplicit && !(β mvar.mvarId!.isAssigned) then
|
| 350 |
+
let type β inferType mvar
|
| 351 |
+
unless (β synthesizeInstanceAndAssign mvar type) do
|
| 352 |
+
reportIssue! "failed to synthesize instance when instantiating {β thm.origin.pp}{indentExpr type}"
|
| 353 |
+
failure
|
| 354 |
+
|
| 355 |
+
private def preprocessGeneralizedPatternRHS (lhs : Expr) (rhs : Expr) (origin : Origin) (expectedType : Expr) : OptionT (StateT Choice M) Expr := do
|
| 356 |
+
assert! (β alreadyInternalized lhs)
|
| 357 |
+
-- We use `dsimp` here to ensure terms such as `Nat.succ x` are normalized as `x+1`.
|
| 358 |
+
let rhs β preprocessLight (β dsimpCore rhs)
|
| 359 |
+
internalize rhs (β getGeneration lhs)
|
| 360 |
+
processNewFacts
|
| 361 |
+
if (β isEqv lhs rhs) then
|
| 362 |
+
return rhs
|
| 363 |
+
else
|
| 364 |
+
reportIssue! "invalid generalized pattern at `{β origin.pp}`\nwhen processing argument with type{indentExpr expectedType}\nfailed to prove{indentExpr lhs}\nis equal to{indentExpr rhs}"
|
| 365 |
+
failure
|
| 366 |
+
|
| 367 |
+
private def assignGeneralizedPatternProof (mvarId : MVarId) (eqProof : Expr) (origin : Origin) : OptionT (StateT Choice M) Unit := do
|
| 368 |
+
unless (β mvarId.checkedAssign eqProof) do
|
| 369 |
+
reportIssue! "invalid generalized pattern at `{β origin.pp}`\nfailed to assign {mkMVar mvarId}\nwith{indentExpr eqProof}"
|
| 370 |
+
failure
|
| 371 |
+
|
| 372 |
+
private def applyAssignment (mvars : Array Expr) : OptionT (StateT Choice M) Unit := do
|
| 373 |
+
let thm := (β read).thm
|
| 374 |
+
let numParams := thm.numParams
|
| 375 |
+
for h : i in [:mvars.size] do
|
| 376 |
+
let bidx := numParams - i - 1
|
| 377 |
+
let mut v := (β get).assignment[bidx]!
|
| 378 |
+
if isSameExpr v delayedEqProof then
|
| 379 |
+
let mvarId := mvars[i].mvarId!
|
| 380 |
+
let mvarIdType β instantiateMVars (β mvarId.getType)
|
| 381 |
+
match_expr mvarIdType with
|
| 382 |
+
| Eq Ξ± lhs rhs =>
|
| 383 |
+
let rhs β preprocessGeneralizedPatternRHS lhs rhs thm.origin mvarIdType
|
| 384 |
+
assignGeneralizedPatternProof mvarId (β mkEqProof lhs rhs) thm.origin
|
| 385 |
+
| HEq Ξ± lhs Ξ² rhs =>
|
| 386 |
+
let rhs β preprocessGeneralizedPatternRHS lhs rhs thm.origin mvarIdType
|
| 387 |
+
assignGeneralizedPatternProof mvarId (β mkHEqProof lhs rhs) thm.origin
|
| 388 |
+
| _ =>
|
| 389 |
+
reportIssue! "invalid generalized pattern at `{β thm.origin.pp}`\nequality type expected{indentExpr mvarIdType}"
|
| 390 |
+
failure
|
| 391 |
+
else if !isSameExpr v unassigned then
|
| 392 |
+
let mvarId := mvars[i].mvarId!
|
| 393 |
+
let mvarIdType β mvarId.getType
|
| 394 |
+
let vType β inferType v
|
| 395 |
+
let unassignOrFail : OptionT (StateT Choice M) Unit := do
|
| 396 |
+
/-
|
| 397 |
+
If there is type error and `vType` is a proposition, we can still instantiate the
|
| 398 |
+
theorem by unassigning `v` and using it as an extra hypothesis.
|
| 399 |
+
Here is an example to motivate the unassignment.
|
| 400 |
+
```
|
| 401 |
+
example (xs : Array Nat) (w : xs.reverse = xs) (j : Nat) (hj : 0 β€ j) (hj' : j < xs.size / 2)
|
| 402 |
+
: xs[j] = xs[xs.size - 1 - j] := by
|
| 403 |
+
grind
|
| 404 |
+
```
|
| 405 |
+
Without the unassignment we get a type error while trying to instantiate the theorem
|
| 406 |
+
```
|
| 407 |
+
theorem getElem_reverse {xs : Array Ξ±} {i : Nat} (hi : i < xs.reverse.size) :
|
| 408 |
+
(xs.reverse)[i] = xs[xs.size - 1 - i]'(by simp at hi; omega)
|
| 409 |
+
```
|
| 410 |
+
The pattern for this theorem is `xs.reverse[i]`. Note that `hi` occurs there as an implicit argument.
|
| 411 |
+
The term `xs[j]` in our goal e-matches the pattern because we have the equality `xs.reverse = xs`.
|
| 412 |
+
However, the implicit proof at `xs[j]` has type `j < xs.size` instead of `j < xs.reverse.size`.
|
| 413 |
+
-/
|
| 414 |
+
if (β isProp vType) then
|
| 415 |
+
modify (unassign Β· bidx)
|
| 416 |
+
else
|
| 417 |
+
reportIssue! "type error constructing proof for {β thm.origin.pp}\nwhen assigning metavariable {mvars[i]} with {indentExpr v}\n{β mkHasTypeButIsExpectedMsg vType mvarIdType}"
|
| 418 |
+
failure
|
| 419 |
+
unless (β withDefault <| isDefEq mvarIdType vType) do
|
| 420 |
+
if let some heq β withoutReportingMVarIssues <| proveEq? vType mvarIdType (abstract := true) then
|
| 421 |
+
/-
|
| 422 |
+
Some of the `cast`s will appear inside the `Grind.MatchCond` binders, and
|
| 423 |
+
we want their proofs to be properly wrapped.
|
| 424 |
+
-/
|
| 425 |
+
let heq := mkApp2 (mkConst ``Grind.nestedProof) (β mkEq vType mvarIdType) heq
|
| 426 |
+
v β mkAppM ``cast #[heq, v]
|
| 427 |
+
else
|
| 428 |
+
unassignOrFail
|
| 429 |
+
continue
|
| 430 |
+
unless (β mvarId.checkedAssign v) do
|
| 431 |
+
unassignOrFail
|
| 432 |
+
|
| 433 |
+
/--
|
| 434 |
+
After processing a (multi-)pattern, use the choice assignment to instantiate the proof.
|
| 435 |
+
Missing parameters are synthesized using type inference and type class synthesis."
|
| 436 |
+
-/
|
| 437 |
+
private partial def instantiateTheorem (c : Choice) : M Unit := withDefault do withNewMCtxDepth do
|
| 438 |
+
let thm := (β read).thm
|
| 439 |
+
unless (β markTheoremInstance thm.proof c.assignment) do
|
| 440 |
+
return ()
|
| 441 |
+
trace_goal[grind.ematch.instance.assignment] "{β thm.origin.pp}: {assignmentToMessageData c.assignment}"
|
| 442 |
+
let proof β thm.getProofWithFreshMVarLevels
|
| 443 |
+
let numParams := thm.numParams
|
| 444 |
+
assert! c.assignment.size == numParams
|
| 445 |
+
let (mvars, bis, _) β forallMetaBoundedTelescope (β inferType proof) numParams
|
| 446 |
+
if mvars.size != thm.numParams then
|
| 447 |
+
reportIssue! "unexpected number of parameters at {β thm.origin.pp}"
|
| 448 |
+
return ()
|
| 449 |
+
let (some _, c) β applyAssignment mvars |>.run c | return ()
|
| 450 |
+
let some _ β synthesizeInsts mvars bis | return ()
|
| 451 |
+
let proof := mkAppN proof mvars
|
| 452 |
+
if (β mvars.allM (Β·.mvarId!.isAssigned)) then
|
| 453 |
+
addNewInstance thm proof c.gen
|
| 454 |
+
else
|
| 455 |
+
let mvars β mvars.filterM fun mvar => return !(β mvar.mvarId!.isAssigned)
|
| 456 |
+
if let some mvarBad β mvars.findM? fun mvar => return !(β isProof mvar) then
|
| 457 |
+
reportIssue! "failed to instantiate {β thm.origin.pp}, failed to instantiate non propositional argument with type{indentExpr (β inferType mvarBad)}"
|
| 458 |
+
let proof β mkLambdaFVars (binderInfoForMVars := .default) mvars (β instantiateMVars proof)
|
| 459 |
+
addNewInstance thm proof c.gen
|
| 460 |
+
|
| 461 |
+
/-- Process choice stack until we don't have more choices to be processed. -/
|
| 462 |
+
private def processChoices : M Unit := do
|
| 463 |
+
let maxGeneration β getMaxGeneration
|
| 464 |
+
while !(β get).choiceStack.isEmpty do
|
| 465 |
+
checkSystem "ematch"
|
| 466 |
+
if (β checkMaxInstancesExceeded) then return ()
|
| 467 |
+
let c β modifyGet fun s : SearchState => (s.choiceStack.head!, { s with choiceStack := s.choiceStack.tail! })
|
| 468 |
+
if c.gen < maxGeneration then
|
| 469 |
+
match c.cnstrs with
|
| 470 |
+
| [] => instantiateTheorem c
|
| 471 |
+
| .match genInfo? p e :: cnstrs => processMatch { c with cnstrs } genInfo? p e
|
| 472 |
+
| .offset genInfo? p k e :: cnstrs => processOffset { c with cnstrs } genInfo? p k e
|
| 473 |
+
| .continue p :: cnstrs => processContinue { c with cnstrs } p
|
| 474 |
+
|
| 475 |
+
private def main (p : Expr) (cnstrs : List Cnstr) : M Unit := do
|
| 476 |
+
let some apps β getAppsOf p
|
| 477 |
+
| return ()
|
| 478 |
+
let numParams := (β read).thm.numParams
|
| 479 |
+
let assignment := .replicate numParams unassigned
|
| 480 |
+
let useMT := (β read).useMT
|
| 481 |
+
let gmt := (β getThe Goal).ematch.gmt
|
| 482 |
+
for app in apps do
|
| 483 |
+
if (β checkMaxInstancesExceeded) then return ()
|
| 484 |
+
let n β getENode app
|
| 485 |
+
if (n.heqProofs || n.isCongrRoot) &&
|
| 486 |
+
(!useMT || n.mt == gmt) then
|
| 487 |
+
withInitApp app do
|
| 488 |
+
if let some c β matchArgsPrefix? { cnstrs, assignment, gen := n.generation } p app |>.run then
|
| 489 |
+
modify fun s => { s with choiceStack := [c] }
|
| 490 |
+
processChoices
|
| 491 |
+
|
| 492 |
+
/--
|
| 493 |
+
Entry point for matching `lhs β= rhs` patterns.
|
| 494 |
+
It traverses disequalities `a = b`, and tries to solve two matching problems:
|
| 495 |
+
1- match `lhs` with `a` and `rhs` with `b`
|
| 496 |
+
2- match `lhs` with `b` and `rhs` with `a`
|
| 497 |
+
-/
|
| 498 |
+
private def matchEqBwdPat (p : Expr) : M Unit := do
|
| 499 |
+
let_expr Grind.eqBwdPattern pΞ± plhs prhs := p | return ()
|
| 500 |
+
let numParams := (β read).thm.numParams
|
| 501 |
+
let assignment := .replicate numParams unassigned
|
| 502 |
+
let useMT := (β read).useMT
|
| 503 |
+
let gmt := (β getThe Goal).ematch.gmt
|
| 504 |
+
let false β getFalseExpr
|
| 505 |
+
let mut curr := false
|
| 506 |
+
repeat
|
| 507 |
+
if (β checkMaxInstancesExceeded) then return ()
|
| 508 |
+
let n β getENode curr
|
| 509 |
+
if (n.heqProofs || n.isCongrRoot) &&
|
| 510 |
+
(!useMT || n.mt == gmt) then
|
| 511 |
+
let_expr Eq Ξ± lhs rhs := n.self | pure ()
|
| 512 |
+
if let some cβ β matchArg? { cnstrs := [], assignment, gen := n.generation } pΞ± Ξ± |>.run then
|
| 513 |
+
let go (lhs rhs : Expr) : M Unit := do
|
| 514 |
+
let some cβ β matchArg? cβ plhs lhs |>.run | return ()
|
| 515 |
+
let some cβ β matchArg? cβ prhs rhs |>.run | return ()
|
| 516 |
+
modify fun s => { s with choiceStack := [cβ] }
|
| 517 |
+
processChoices
|
| 518 |
+
go lhs rhs
|
| 519 |
+
go rhs lhs
|
| 520 |
+
if isSameExpr n.next false then return ()
|
| 521 |
+
curr := n.next
|
| 522 |
+
|
| 523 |
+
def ematchTheorem (thm : EMatchTheorem) : M Unit := do
|
| 524 |
+
if (β checkMaxInstancesExceeded) then return ()
|
| 525 |
+
withReader (fun ctx => { ctx with thm }) do
|
| 526 |
+
let ps := thm.patterns
|
| 527 |
+
match ps, (β read).useMT with
|
| 528 |
+
| [p], _ => if isEqBwdPattern p then matchEqBwdPat p else main p []
|
| 529 |
+
| p::ps, false => main p (ps.map (.continue Β·))
|
| 530 |
+
| _::_, true => tryAll ps []
|
| 531 |
+
| _, _ => unreachable!
|
| 532 |
+
where
|
| 533 |
+
/--
|
| 534 |
+
When using the mod-time optimization with multi-patterns,
|
| 535 |
+
we must start ematching at each different pattern. That is,
|
| 536 |
+
if we have `[pβ, pβ, pβ]`, we must execute
|
| 537 |
+
- `main pβ [.continue pβ, .continue pβ]`
|
| 538 |
+
- `main pβ [.continue pβ, .continue pβ]`
|
| 539 |
+
- `main pβ [.continue pβ, .continue pβ]`
|
| 540 |
+
-/
|
| 541 |
+
tryAll (ps : List Expr) (cs : List Cnstr) : M Unit := do
|
| 542 |
+
match ps with
|
| 543 |
+
| [] => return ()
|
| 544 |
+
| p::ps =>
|
| 545 |
+
main p (cs.reverse ++ (ps.map (.continue Β·)))
|
| 546 |
+
tryAll ps (.continue p :: cs)
|
| 547 |
+
|
| 548 |
+
def ematchTheorems (thms : PArray EMatchTheorem) : M Unit := do
|
| 549 |
+
thms.forM ematchTheorem
|
| 550 |
+
|
| 551 |
+
end EMatch
|
| 552 |
+
|
| 553 |
+
open EMatch
|
| 554 |
+
|
| 555 |
+
/-- Performs one round of E-matching, and returns new instances. -/
|
| 556 |
+
private def ematchCore : GoalM Unit := do
|
| 557 |
+
let go (thms newThms : PArray EMatchTheorem) : EMatch.M Unit := do
|
| 558 |
+
withReader (fun ctx => { ctx with useMT := true }) <| ematchTheorems thms
|
| 559 |
+
withReader (fun ctx => { ctx with useMT := false }) <| ematchTheorems newThms
|
| 560 |
+
if (β checkMaxInstancesExceeded <||> checkMaxEmatchExceeded) then
|
| 561 |
+
return ()
|
| 562 |
+
else
|
| 563 |
+
go (β get).ematch.thms (β get).ematch.newThms |>.run'
|
| 564 |
+
modify fun s => { s with
|
| 565 |
+
ematch.thms := s.ematch.thms ++ s.ematch.newThms
|
| 566 |
+
ematch.newThms := {}
|
| 567 |
+
ematch.gmt := s.ematch.gmt + 1
|
| 568 |
+
ematch.num := s.ematch.num + 1
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
/-- Performs one round of E-matching, and returns `true` if new instances were generated. -/
|
| 572 |
+
def ematch : GoalM Bool := do
|
| 573 |
+
let numInstances := (β get).ematch.numInstances
|
| 574 |
+
ematchCore
|
| 575 |
+
return (β get).ematch.numInstances != numInstances
|
| 576 |
+
|
| 577 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EMatchTheorem.lean
ADDED
|
@@ -0,0 +1,1277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Grind.Util
|
| 8 |
+
import Init.Grind.Tactics
|
| 9 |
+
import Lean.HeadIndex
|
| 10 |
+
import Lean.PrettyPrinter
|
| 11 |
+
import Lean.Util.FoldConsts
|
| 12 |
+
import Lean.Util.CollectFVars
|
| 13 |
+
import Lean.Meta.Basic
|
| 14 |
+
import Lean.Meta.InferType
|
| 15 |
+
import Lean.Meta.Eqns
|
| 16 |
+
import Lean.Meta.Match.MatchEqs
|
| 17 |
+
import Lean.Meta.Tactic.Grind.Util
|
| 18 |
+
|
| 19 |
+
namespace Lean.Meta.Grind
|
| 20 |
+
|
| 21 |
+
/--
|
| 22 |
+
`grind` uses symbol priorities when inferring patterns for E-matching.
|
| 23 |
+
Symbols not in `map` are assumed to have default priority (i.e., `eval_prio default`).
|
| 24 |
+
-/
|
| 25 |
+
structure SymbolPriorities where
|
| 26 |
+
map : PHashMap Name Nat := {}
|
| 27 |
+
deriving Inhabited
|
| 28 |
+
|
| 29 |
+
structure SymbolPriorityEntry where
|
| 30 |
+
declName : Name
|
| 31 |
+
prio : Nat
|
| 32 |
+
deriving Inhabited
|
| 33 |
+
|
| 34 |
+
/-- Removes the given declaration from `s`. -/
|
| 35 |
+
def SymbolPriorities.erase (s : SymbolPriorities) (declName : Name) : SymbolPriorities :=
|
| 36 |
+
{ s with map := s.map.erase declName }
|
| 37 |
+
|
| 38 |
+
/-- Inserts `declName β¦ prio` into `s`. -/
|
| 39 |
+
def SymbolPriorities.insert (s : SymbolPriorities) (declName : Name) (prio : Nat) : SymbolPriorities :=
|
| 40 |
+
{ s with map := s.map.insert declName prio }
|
| 41 |
+
|
| 42 |
+
/-- Returns `declName` priority for E-matching pattern inference in `s`. -/
|
| 43 |
+
def SymbolPriorities.getPrio (s : SymbolPriorities) (declName : Name) : Nat :=
|
| 44 |
+
if let some prio := s.map.find? declName then
|
| 45 |
+
prio
|
| 46 |
+
else
|
| 47 |
+
eval_prio default
|
| 48 |
+
|
| 49 |
+
/--
|
| 50 |
+
Returns `true`, if there is an entry `declName β¦ prio` in `s`.
|
| 51 |
+
Recall that symbols not in `s` are assumed to have default priority.
|
| 52 |
+
-/
|
| 53 |
+
def SymbolPriorities.contains (s : SymbolPriorities) (declName : Name) : Bool :=
|
| 54 |
+
s.map.contains declName
|
| 55 |
+
|
| 56 |
+
private builtin_initialize symbolPrioExt : SimpleScopedEnvExtension SymbolPriorityEntry SymbolPriorities β
|
| 57 |
+
registerSimpleScopedEnvExtension {
|
| 58 |
+
initial := {}
|
| 59 |
+
addEntry := fun s {declName, prio} => s.insert declName prio
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
def resetSymbolPrioExt : CoreM Unit := do
|
| 63 |
+
modifyEnv fun env => symbolPrioExt.modifyState env fun _ => {}
|
| 64 |
+
|
| 65 |
+
def getGlobalSymbolPriorities : CoreM SymbolPriorities :=
|
| 66 |
+
return symbolPrioExt.getState (β getEnv)
|
| 67 |
+
|
| 68 |
+
/-- Sets `declName` priority to be used during E-matching pattern inference -/
|
| 69 |
+
def addSymbolPriorityAttr (declName : Name) (attrKind : AttributeKind) (prio : Nat) : MetaM Unit := do
|
| 70 |
+
symbolPrioExt.add { declName, prio } attrKind
|
| 71 |
+
|
| 72 |
+
def mkOffsetPattern (pat : Expr) (k : Nat) : Expr :=
|
| 73 |
+
mkApp2 (mkConst ``Grind.offset) pat (mkRawNatLit k)
|
| 74 |
+
|
| 75 |
+
private def detectOffsets (pat : Expr) : MetaM Expr := do
|
| 76 |
+
let pre (e : Expr) := do
|
| 77 |
+
if e == pat then
|
| 78 |
+
-- We only consider nested offset patterns
|
| 79 |
+
return .continue e
|
| 80 |
+
else match e with
|
| 81 |
+
| .letE .. | .lam .. | .forallE .. => return .done e
|
| 82 |
+
| _ =>
|
| 83 |
+
let some (e, k) β isOffset? e
|
| 84 |
+
| return .continue e
|
| 85 |
+
if k == 0 then return .continue e
|
| 86 |
+
return .continue <| mkOffsetPattern e k
|
| 87 |
+
Core.transform pat (pre := pre)
|
| 88 |
+
|
| 89 |
+
def isOffsetPattern? (pat : Expr) : Option (Expr Γ Nat) := Id.run do
|
| 90 |
+
let_expr Grind.offset pat k := pat | none
|
| 91 |
+
let .lit (.natVal k) := k | none
|
| 92 |
+
return some (pat, k)
|
| 93 |
+
|
| 94 |
+
/--
|
| 95 |
+
`detectOffsets` inverse.
|
| 96 |
+
This function is used to expand `mkOffsetPattern` occurring in a constant pattern.
|
| 97 |
+
-/
|
| 98 |
+
private def expandOffsetPatterns (pat : Expr) : CoreM Expr := do
|
| 99 |
+
let pre (e : Expr) := do
|
| 100 |
+
match e with
|
| 101 |
+
| .letE .. | .lam .. | .forallE .. => return .done e
|
| 102 |
+
| _ =>
|
| 103 |
+
let some (e, k) := isOffsetPattern? e
|
| 104 |
+
| return .continue e
|
| 105 |
+
if k == 0 then return .continue e
|
| 106 |
+
return .continue <| mkNatAdd e (mkNatLit k)
|
| 107 |
+
Core.transform pat (pre := pre)
|
| 108 |
+
|
| 109 |
+
def mkEqBwdPattern (u : List Level) (Ξ± : Expr) (lhs rhs : Expr) : Expr :=
|
| 110 |
+
mkApp3 (mkConst ``Grind.eqBwdPattern u) Ξ± lhs rhs
|
| 111 |
+
|
| 112 |
+
def isEqBwdPattern (e : Expr) : Bool :=
|
| 113 |
+
e.isAppOfArity ``Grind.eqBwdPattern 3
|
| 114 |
+
|
| 115 |
+
def isEqBwdPattern? (e : Expr) : Option (Expr Γ Expr) :=
|
| 116 |
+
let_expr Grind.eqBwdPattern _ lhs rhs := e
|
| 117 |
+
| none
|
| 118 |
+
some (lhs, rhs)
|
| 119 |
+
|
| 120 |
+
def mkGenPattern (u : List Level) (Ξ± : Expr) (h : Expr) (x : Expr) (val : Expr) : Expr :=
|
| 121 |
+
mkApp4 (mkConst ``Grind.genPattern u) Ξ± h x val
|
| 122 |
+
|
| 123 |
+
def mkGenHEqPattern (u : List Level) (Ξ± Ξ² : Expr) (h : Expr) (x : Expr) (val : Expr) : Expr :=
|
| 124 |
+
mkApp5 (mkConst ``Grind.genHEqPattern u) Ξ± Ξ² h x val
|
| 125 |
+
|
| 126 |
+
/-- Generalized pattern information. See `Grind.genPattern` gadget. -/
|
| 127 |
+
structure GenPatternInfo where
|
| 128 |
+
heq : Bool
|
| 129 |
+
hIdx : Nat
|
| 130 |
+
xIdx : Nat
|
| 131 |
+
deriving Repr
|
| 132 |
+
|
| 133 |
+
def isGenPattern? (pat : Expr) : Option (GenPatternInfo Γ Expr) :=
|
| 134 |
+
match_expr pat with
|
| 135 |
+
| Grind.genPattern _ h x pat => Id.run do
|
| 136 |
+
let .bvar hIdx := h | unreachable!
|
| 137 |
+
let .bvar xIdx := x | unreachable!
|
| 138 |
+
return some ({ heq := false, hIdx, xIdx }, pat )
|
| 139 |
+
| Grind.genHEqPattern _ _ h x pat => Id.run do
|
| 140 |
+
let .bvar hIdx := h | unreachable!
|
| 141 |
+
let .bvar xIdx := x | unreachable!
|
| 142 |
+
return some ({ heq := true, hIdx, xIdx }, pat )
|
| 143 |
+
| _ => none
|
| 144 |
+
|
| 145 |
+
/-- Returns `true` if `declName` is the name of a `match`-expression congruence equation. -/
|
| 146 |
+
def isMatchCongrEqDeclName (declName : Name) : CoreM Bool := do
|
| 147 |
+
let declName := privateToUserName declName
|
| 148 |
+
match declName with
|
| 149 |
+
| .str p s => return (β isMatcher p) && Match.isCongrEqnReservedNameSuffix s
|
| 150 |
+
| _ => return false
|
| 151 |
+
|
| 152 |
+
/-- Returns `true` if `e` is a constant for a `match`-expression congruence equation. -/
|
| 153 |
+
private def isMatchCongrEqConst (e : Expr) : CoreM Bool := do
|
| 154 |
+
let .const declName _ := e | return false
|
| 155 |
+
isMatchCongrEqDeclName declName
|
| 156 |
+
|
| 157 |
+
/--
|
| 158 |
+
Given the type of a `match` congruence equation, annotate the discriminants using
|
| 159 |
+
the gadgets `Grind.genPattern` and `Grind.genHEqPattern`.
|
| 160 |
+
For example, consider the following `match` congruence theorem type
|
| 161 |
+
```
|
| 162 |
+
forall
|
| 163 |
+
(motive : Option Nat β Sort u_1) (aβ : Option Nat)
|
| 164 |
+
(h_1 : aβ = none β motive none)
|
| 165 |
+
(h_2 : (val : Nat) β aβ = some val β motive (some val))
|
| 166 |
+
(valβ : Nat)
|
| 167 |
+
(heq_1 : aβ = some valβ),
|
| 168 |
+
g.match_1 motive aβ h_1 h_2 β h_2 valβ heq_1
|
| 169 |
+
```
|
| 170 |
+
This function returns the type
|
| 171 |
+
```
|
| 172 |
+
forall
|
| 173 |
+
(motive : Option Nat β Sort u_1) (aβ : Option Nat)
|
| 174 |
+
(h_1 : aβ = none β motive none)
|
| 175 |
+
(h_2 : (val : Nat) β aβ = some val β motive (some val))
|
| 176 |
+
(valβ : Nat)
|
| 177 |
+
(heq_1 : aβ = some valβ),
|
| 178 |
+
g.match_1 motive (Grind.genPattern (Option Nat) heq_1 aβ (some valβ)) h_1 h_2
|
| 179 |
+
β h_2 valβ heq_1
|
| 180 |
+
```
|
| 181 |
+
The gadget is used to infer a `generalize` pattern. The following term is used
|
| 182 |
+
during E-matching `g.match_1 motive (Grind.genPattern (Option Nat) heq_1 aβ (some valβ)) h_1 h_2`
|
| 183 |
+
when matching `Grind.genPattern (Option Nat) heq_1 aβ (some valβ)` the matcher uses
|
| 184 |
+
`(some valβ)` as the actual pattern, but also assigns `heq_1` `aβ` using the information stored
|
| 185 |
+
in the equivalence class.
|
| 186 |
+
-/
|
| 187 |
+
private def preprocessMatchCongrEqType (type : Expr) : MetaM Expr := do
|
| 188 |
+
forallTelescopeReducing type fun hs resultType => do
|
| 189 |
+
let lhs β match_expr resultType with
|
| 190 |
+
| Eq _ lhs _ => pure lhs
|
| 191 |
+
| HEq _ lhs _ _ => pure lhs
|
| 192 |
+
| _ => return type
|
| 193 |
+
let lhsFn := lhs.getAppFn
|
| 194 |
+
let .const declName _ := lhsFn | return type
|
| 195 |
+
let some matcherInfo β getMatcherInfo? declName | return type
|
| 196 |
+
let range := matcherInfo.getDiscrRange
|
| 197 |
+
let mut args := lhs.getAppArgs
|
| 198 |
+
for h in hs do
|
| 199 |
+
match_expr (β inferType h) with
|
| 200 |
+
| f@Eq Ξ± lhs rhs =>
|
| 201 |
+
for i in range do
|
| 202 |
+
if lhs == args[i]! then
|
| 203 |
+
args := args.set! i (mkGenPattern f.constLevels! Ξ± h lhs rhs)
|
| 204 |
+
break
|
| 205 |
+
| f@HEq Ξ± lhs Ξ² rhs =>
|
| 206 |
+
for i in range do
|
| 207 |
+
if lhs == args[i]! then
|
| 208 |
+
args := args.set! i (mkGenHEqPattern f.constLevels! Ξ± Ξ² h lhs rhs)
|
| 209 |
+
break
|
| 210 |
+
| _ => pure ()
|
| 211 |
+
let lhsNew := mkAppN lhsFn args
|
| 212 |
+
let resultTypeFn := resultType.getAppFn
|
| 213 |
+
let resultArgs := resultType.getAppArgs
|
| 214 |
+
let resultType := mkAppN resultTypeFn (resultArgs.set! 1 lhsNew)
|
| 215 |
+
mkForallFVars hs resultType
|
| 216 |
+
|
| 217 |
+
/--
|
| 218 |
+
A heuristic procedure for detecting generalized patterns.
|
| 219 |
+
For example, given the theorem
|
| 220 |
+
```
|
| 221 |
+
theorem Option.pbind_some' {Ξ± Ξ²} {x : Option Ξ±} {a : Ξ±} {f : (a : Ξ±) β x = some a β Option Ξ²}
|
| 222 |
+
(h : x = some a) : pbind x f = f a h
|
| 223 |
+
```
|
| 224 |
+
In the current implementation, we support only occurrences in the resulting type.
|
| 225 |
+
Thus, the following resulting type is generated for the example above:
|
| 226 |
+
```
|
| 227 |
+
pbind (Grind.genPattern h x (some a)) f = f a h
|
| 228 |
+
```
|
| 229 |
+
-/
|
| 230 |
+
private def detectGeneralizedPatterns? (type : Expr) : MetaM Expr := do
|
| 231 |
+
forallTelescopeReducing type fun hs resultType => do
|
| 232 |
+
let isTarget? (lhs : Expr) (rhs : Expr) (s : FVarSubst) : Option (FVarId Γ Expr) := Id.run do
|
| 233 |
+
let .fvar fvarId := lhs | return none
|
| 234 |
+
if !hs.contains lhs then
|
| 235 |
+
return none -- It is a foreign free variable
|
| 236 |
+
if rhs.containsFVar fvarId then
|
| 237 |
+
return none -- It is not a generalization if `rhs` contains it
|
| 238 |
+
if s.contains fvarId then
|
| 239 |
+
return none -- Remark: may want to abort instead, it is probably not a generalization
|
| 240 |
+
let rhs := s.apply rhs
|
| 241 |
+
return some (fvarId, rhs)
|
| 242 |
+
let mut s : FVarSubst := {}
|
| 243 |
+
for h in hs do
|
| 244 |
+
match_expr (β inferType h) with
|
| 245 |
+
| f@Eq Ξ± lhs rhs =>
|
| 246 |
+
let some (fvarId, rhs) := isTarget? lhs rhs s | pure ()
|
| 247 |
+
s := s.insert fvarId <| mkGenPattern f.constLevels! Ξ± h lhs rhs
|
| 248 |
+
| f@HEq Ξ± lhs Ξ² rhs =>
|
| 249 |
+
let some (fvarId, rhs) := isTarget? lhs rhs s | pure ()
|
| 250 |
+
s := s.insert fvarId <| mkGenHEqPattern f.constLevels! Ξ± Ξ² h lhs rhs
|
| 251 |
+
| _ => pure ()
|
| 252 |
+
if s.isEmpty then
|
| 253 |
+
return type
|
| 254 |
+
let resultType' := s.apply resultType
|
| 255 |
+
if resultType' == resultType then
|
| 256 |
+
return type
|
| 257 |
+
mkForallFVars hs resultType'
|
| 258 |
+
|
| 259 |
+
/--
|
| 260 |
+
Given the proof for a proposition to be used as an E-matching theorem,
|
| 261 |
+
infers its type, and preprocess it to identify generalized patterns.
|
| 262 |
+
Recall that we infer these generalized patterns automatically for
|
| 263 |
+
`match` congruence equations.
|
| 264 |
+
-/
|
| 265 |
+
private def inferEMatchProofType (proof : Expr) (gen : Bool) : MetaM Expr := do
|
| 266 |
+
let type β inferType proof
|
| 267 |
+
if (β isMatchCongrEqConst proof) then
|
| 268 |
+
preprocessMatchCongrEqType type
|
| 269 |
+
else if gen then
|
| 270 |
+
detectGeneralizedPatterns? type
|
| 271 |
+
else
|
| 272 |
+
return type
|
| 273 |
+
|
| 274 |
+
-- Configuration for the `grind` normalizer. We want both `zetaDelta` and `zeta`
|
| 275 |
+
private def normConfig : Grind.Config := {}
|
| 276 |
+
theorem normConfig_zeta : normConfig.zeta = true := rfl
|
| 277 |
+
theorem normConfig_zetaDelta : normConfig.zetaDelta = true := rfl
|
| 278 |
+
|
| 279 |
+
def preprocessPattern (pat : Expr) (normalizePattern := true) : MetaM Expr := do
|
| 280 |
+
let pat β instantiateMVars pat
|
| 281 |
+
let pat β unfoldReducible pat
|
| 282 |
+
let pat β if normalizePattern then normalize pat normConfig else pure pat
|
| 283 |
+
let pat β detectOffsets pat
|
| 284 |
+
let pat β foldProjs pat
|
| 285 |
+
return pat
|
| 286 |
+
|
| 287 |
+
inductive Origin where
|
| 288 |
+
/-- A global declaration in the environment. -/
|
| 289 |
+
| decl (declName : Name)
|
| 290 |
+
/-- A local hypothesis. -/
|
| 291 |
+
| fvar (fvarId : FVarId)
|
| 292 |
+
/--
|
| 293 |
+
A proof term provided directly to a call to `grind` where `ref`
|
| 294 |
+
is the provided grind argument. The `id` is a unique identifier for the call.
|
| 295 |
+
-/
|
| 296 |
+
| stx (id : Name) (ref : Syntax)
|
| 297 |
+
/-- It is local, but we don't have a local hypothesis for it. -/
|
| 298 |
+
| local (id : Name)
|
| 299 |
+
deriving Inhabited, Repr
|
| 300 |
+
|
| 301 |
+
/-- A unique identifier corresponding to the origin. -/
|
| 302 |
+
def Origin.key : Origin β Name
|
| 303 |
+
| .decl declName => declName
|
| 304 |
+
| .fvar fvarId => fvarId.name
|
| 305 |
+
| .stx id _ => id
|
| 306 |
+
| .local id => id
|
| 307 |
+
|
| 308 |
+
def Origin.pp [Monad m] [MonadEnv m] [MonadError m] (o : Origin) : m MessageData := do
|
| 309 |
+
match o with
|
| 310 |
+
| .decl declName => return MessageData.ofConst (β mkConstWithLevelParams declName)
|
| 311 |
+
| .fvar fvarId => return mkFVar fvarId
|
| 312 |
+
| .stx _ ref => return ref
|
| 313 |
+
| .local id => return id
|
| 314 |
+
|
| 315 |
+
instance : BEq Origin where
|
| 316 |
+
beq a b := a.key == b.key
|
| 317 |
+
|
| 318 |
+
instance : Hashable Origin where
|
| 319 |
+
hash a := hash a.key
|
| 320 |
+
|
| 321 |
+
inductive EMatchTheoremKind where
|
| 322 |
+
| eqLhs (gen : Bool)
|
| 323 |
+
| eqRhs (gen : Bool)
|
| 324 |
+
| eqBoth (gen : Bool)
|
| 325 |
+
| eqBwd
|
| 326 |
+
| fwd
|
| 327 |
+
| bwd (gen : Bool)
|
| 328 |
+
| leftRight
|
| 329 |
+
| rightLeft
|
| 330 |
+
| default (gen : Bool)
|
| 331 |
+
| user /- pattern specified using `grind_pattern` command -/
|
| 332 |
+
deriving Inhabited, BEq, Repr, Hashable
|
| 333 |
+
|
| 334 |
+
def EMatchTheoremKind.isEqLhs : EMatchTheoremKind β Bool
|
| 335 |
+
| .eqLhs _ => true
|
| 336 |
+
| _ => false
|
| 337 |
+
|
| 338 |
+
def EMatchTheoremKind.isDefault : EMatchTheoremKind β Bool
|
| 339 |
+
| .default _ => true
|
| 340 |
+
| _ => false
|
| 341 |
+
|
| 342 |
+
private def EMatchTheoremKind.toAttribute : EMatchTheoremKind β String
|
| 343 |
+
| .eqLhs true => "[grind = gen]"
|
| 344 |
+
| .eqLhs false => "[grind =]"
|
| 345 |
+
| .eqRhs true => "[grind =_ gen]"
|
| 346 |
+
| .eqRhs false => "[grind =_]"
|
| 347 |
+
| .eqBoth false => "[grind _=_]"
|
| 348 |
+
| .eqBoth true => "[grind _=_ gen]"
|
| 349 |
+
| .eqBwd => "[grind β=]"
|
| 350 |
+
| .fwd => "[grind β]"
|
| 351 |
+
| .bwd false => "[grind β]"
|
| 352 |
+
| .bwd true => "[grind β gen]"
|
| 353 |
+
| .leftRight => "[grind =>]"
|
| 354 |
+
| .rightLeft => "[grind <=]"
|
| 355 |
+
| .default false => "[grind]"
|
| 356 |
+
| .default true => "[grind gen]"
|
| 357 |
+
| .user => "[grind]"
|
| 358 |
+
|
| 359 |
+
private def EMatchTheoremKind.explainFailure : EMatchTheoremKind β String
|
| 360 |
+
| .eqLhs _ => "failed to find pattern in the left-hand side of the theorem's conclusion"
|
| 361 |
+
| .eqRhs _ => "failed to find pattern in the right-hand side of the theorem's conclusion"
|
| 362 |
+
| .eqBoth _ => unreachable! -- eqBoth is a macro
|
| 363 |
+
| .eqBwd => "failed to use theorem's conclusion as a pattern"
|
| 364 |
+
| .fwd => "failed to find patterns in the antecedents of the theorem"
|
| 365 |
+
| .bwd _ => "failed to find patterns in the theorem's conclusion"
|
| 366 |
+
| .leftRight => "failed to find patterns searching from left to right"
|
| 367 |
+
| .rightLeft => "failed to find patterns searching from right to left"
|
| 368 |
+
| .default _ => "failed to find patterns"
|
| 369 |
+
| .user => unreachable!
|
| 370 |
+
|
| 371 |
+
/-- A theorem for heuristic instantiation based on E-matching. -/
|
| 372 |
+
structure EMatchTheorem where
|
| 373 |
+
/--
|
| 374 |
+
It stores universe parameter names for universe polymorphic proofs.
|
| 375 |
+
Recall that it is non-empty only when we elaborate an expression provided by the user.
|
| 376 |
+
When `proof` is just a constant, we can use the universe parameter names stored in the declaration.
|
| 377 |
+
-/
|
| 378 |
+
levelParams : Array Name
|
| 379 |
+
proof : Expr
|
| 380 |
+
numParams : Nat
|
| 381 |
+
patterns : List Expr
|
| 382 |
+
/-- Contains all symbols used in `patterns`. -/
|
| 383 |
+
symbols : List HeadIndex
|
| 384 |
+
origin : Origin
|
| 385 |
+
/-- The `kind` is used for generating the `patterns`. We save it here to implement `grind?`. -/
|
| 386 |
+
kind : EMatchTheoremKind
|
| 387 |
+
deriving Inhabited
|
| 388 |
+
|
| 389 |
+
/-- Set of E-matching theorems. -/
|
| 390 |
+
structure EMatchTheorems where
|
| 391 |
+
/-- The key is a symbol from `EMatchTheorem.symbols`. -/
|
| 392 |
+
private smap : PHashMap Name (List EMatchTheorem) := {}
|
| 393 |
+
/-- Set of theorem ids that have been inserted using `insert`. -/
|
| 394 |
+
private origins : PHashSet Origin := {}
|
| 395 |
+
/-- Theorems that have been marked as erased -/
|
| 396 |
+
private erased : PHashSet Origin := {}
|
| 397 |
+
/-- Mapping from origin to E-matching theorems associated with this origin. -/
|
| 398 |
+
private omap : PHashMap Origin (List EMatchTheorem) := {}
|
| 399 |
+
/-- Priorities for pattern inference -/
|
| 400 |
+
private prios : SymbolPriorities := {}
|
| 401 |
+
deriving Inhabited
|
| 402 |
+
|
| 403 |
+
/--
|
| 404 |
+
Inserts a `thm` with symbols `[s_1, ..., s_n]` to `s`.
|
| 405 |
+
We add `s_1 -> { thm with symbols := [s_2, ..., s_n] }`.
|
| 406 |
+
When `grind` internalizes a term containing symbol `s`, we
|
| 407 |
+
process all theorems `thm` associated with key `s`.
|
| 408 |
+
If their `thm.symbols` is empty, we say they are activated.
|
| 409 |
+
Otherwise, we reinsert into `map`.
|
| 410 |
+
-/
|
| 411 |
+
def EMatchTheorems.insert (s : EMatchTheorems) (thm : EMatchTheorem) : EMatchTheorems := Id.run do
|
| 412 |
+
let .const declName :: syms := thm.symbols
|
| 413 |
+
| unreachable!
|
| 414 |
+
let thm := { thm with symbols := syms }
|
| 415 |
+
let { smap, origins, erased, omap, prios } := s
|
| 416 |
+
let origin := thm.origin
|
| 417 |
+
let origins := origins.insert origin
|
| 418 |
+
let erased := erased.erase origin
|
| 419 |
+
let smap := if let some thms := smap.find? declName then
|
| 420 |
+
smap.insert declName (thm::thms)
|
| 421 |
+
else
|
| 422 |
+
smap.insert declName [thm]
|
| 423 |
+
let omap := if let some thms := omap.find? origin then
|
| 424 |
+
omap.insert origin (thm::thms)
|
| 425 |
+
else
|
| 426 |
+
omap.insert origin [thm]
|
| 427 |
+
return { smap, origins, erased, omap, prios }
|
| 428 |
+
|
| 429 |
+
/-- Returns `true` if `s` contains a theorem with the given origin. -/
|
| 430 |
+
def EMatchTheorems.contains (s : EMatchTheorems) (origin : Origin) : Bool :=
|
| 431 |
+
s.origins.contains origin
|
| 432 |
+
|
| 433 |
+
/-- Mark the theorem with the given origin as `erased` -/
|
| 434 |
+
def EMatchTheorems.erase (s : EMatchTheorems) (origin : Origin) : EMatchTheorems :=
|
| 435 |
+
{ s with erased := s.erased.insert origin, origins := s.origins.erase origin }
|
| 436 |
+
|
| 437 |
+
/-- Returns true if the theorem has been marked as erased. -/
|
| 438 |
+
def EMatchTheorems.isErased (s : EMatchTheorems) (origin : Origin) : Bool :=
|
| 439 |
+
s.erased.contains origin
|
| 440 |
+
|
| 441 |
+
/--
|
| 442 |
+
Retrieves theorems from `s` associated with the given symbol. See `EMatchTheorem.insert`.
|
| 443 |
+
The theorems are removed from `s`.
|
| 444 |
+
-/
|
| 445 |
+
@[inline]
|
| 446 |
+
def EMatchTheorems.retrieve? (s : EMatchTheorems) (sym : Name) : Option (List EMatchTheorem Γ EMatchTheorems) :=
|
| 447 |
+
if let some thms := s.smap.find? sym then
|
| 448 |
+
some (thms, { s with smap := s.smap.erase sym })
|
| 449 |
+
else
|
| 450 |
+
none
|
| 451 |
+
|
| 452 |
+
/--
|
| 453 |
+
Returns theorems associated with the given origin.
|
| 454 |
+
-/
|
| 455 |
+
def EMatchTheorems.find (s : EMatchTheorems) (origin : Origin) : List EMatchTheorem :=
|
| 456 |
+
if let some thms := s.omap.find? origin then
|
| 457 |
+
thms
|
| 458 |
+
else
|
| 459 |
+
[]
|
| 460 |
+
|
| 461 |
+
def EMatchTheorem.getProofWithFreshMVarLevels (thm : EMatchTheorem) : MetaM Expr := do
|
| 462 |
+
if thm.proof.isConst && thm.levelParams.isEmpty then
|
| 463 |
+
let declName := thm.proof.constName!
|
| 464 |
+
let info β getConstInfo declName
|
| 465 |
+
if info.levelParams.isEmpty then
|
| 466 |
+
return thm.proof
|
| 467 |
+
else
|
| 468 |
+
mkConstWithFreshMVarLevels declName
|
| 469 |
+
else if thm.levelParams.isEmpty then
|
| 470 |
+
return thm.proof
|
| 471 |
+
else
|
| 472 |
+
let us β thm.levelParams.mapM fun _ => mkFreshLevelMVar
|
| 473 |
+
return thm.proof.instantiateLevelParamsArray thm.levelParams us
|
| 474 |
+
|
| 475 |
+
private builtin_initialize ematchTheoremsExt : SimpleScopedEnvExtension EMatchTheorem EMatchTheorems β
|
| 476 |
+
registerSimpleScopedEnvExtension {
|
| 477 |
+
addEntry := EMatchTheorems.insert
|
| 478 |
+
initial := {}
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
/-- Returns `true` if `declName` has been tagged as an E-match theorem using `[grind]`. -/
|
| 482 |
+
def isEMatchTheorem (declName : Name) : CoreM Bool := do
|
| 483 |
+
return ematchTheoremsExt.getState (β getEnv) |>.omap.contains (.decl declName)
|
| 484 |
+
|
| 485 |
+
def resetEMatchTheoremsExt : CoreM Unit := do
|
| 486 |
+
modifyEnv fun env => ematchTheoremsExt.modifyState env fun _ => {}
|
| 487 |
+
|
| 488 |
+
/--
|
| 489 |
+
Auxiliary function to expand a pattern containing forbidden application symbols
|
| 490 |
+
into a multi-pattern.
|
| 491 |
+
|
| 492 |
+
This function enhances the usability of the `[grind =]` attribute by automatically handling
|
| 493 |
+
forbidden pattern symbols. For example, consider the following theorem tagged with this attribute:
|
| 494 |
+
```
|
| 495 |
+
getLast?_eq_some_iff {xs : List Ξ±} {a : Ξ±} : xs.getLast? = some a β β ys, xs = ys ++ [a]
|
| 496 |
+
```
|
| 497 |
+
Here, the selected pattern is `xs.getLast? = some a`, but `Eq` is a forbidden pattern symbol.
|
| 498 |
+
Instead of producing an error, this function converts the pattern into a multi-pattern,
|
| 499 |
+
allowing the attribute to be used conveniently.
|
| 500 |
+
|
| 501 |
+
The function recursively expands patterns with forbidden symbols by splitting them
|
| 502 |
+
into their sub-components. If the pattern does not contain forbidden symbols,
|
| 503 |
+
it is returned as-is.
|
| 504 |
+
-/
|
| 505 |
+
partial def splitWhileForbidden (pat : Expr) : List Expr :=
|
| 506 |
+
match_expr pat with
|
| 507 |
+
| Not p => splitWhileForbidden p
|
| 508 |
+
| And pβ pβ => splitWhileForbidden pβ ++ splitWhileForbidden pβ
|
| 509 |
+
| Or pβ pβ => splitWhileForbidden pβ ++ splitWhileForbidden pβ
|
| 510 |
+
| Eq _ lhs rhs => splitWhileForbidden lhs ++ splitWhileForbidden rhs
|
| 511 |
+
| Iff lhs rhs => splitWhileForbidden lhs ++ splitWhileForbidden rhs
|
| 512 |
+
| HEq _ lhs _ rhs => splitWhileForbidden lhs ++ splitWhileForbidden rhs
|
| 513 |
+
| _ => [pat]
|
| 514 |
+
|
| 515 |
+
private def dontCare := mkConst (Name.mkSimple "[grind_dontcare]")
|
| 516 |
+
|
| 517 |
+
def mkGroundPattern (e : Expr) : Expr :=
|
| 518 |
+
mkAnnotation `grind.ground_pat e
|
| 519 |
+
|
| 520 |
+
def groundPattern? (e : Expr) : Option Expr :=
|
| 521 |
+
annotation? `grind.ground_pat e
|
| 522 |
+
|
| 523 |
+
private def isGroundPattern (e : Expr) : Bool :=
|
| 524 |
+
groundPattern? e |>.isSome
|
| 525 |
+
|
| 526 |
+
def isPatternDontCare (e : Expr) : Bool :=
|
| 527 |
+
e == dontCare
|
| 528 |
+
|
| 529 |
+
private def isAtomicPattern (e : Expr) : Bool :=
|
| 530 |
+
e.isBVar || isPatternDontCare e || isGroundPattern e
|
| 531 |
+
|
| 532 |
+
partial def ppPattern (pattern : Expr) : MessageData := Id.run do
|
| 533 |
+
if let some e := groundPattern? pattern then
|
| 534 |
+
return m!"`[{e}]"
|
| 535 |
+
else if isPatternDontCare pattern then
|
| 536 |
+
return m!"_"
|
| 537 |
+
else match pattern with
|
| 538 |
+
| .bvar idx => return m!"#{idx}"
|
| 539 |
+
| _ =>
|
| 540 |
+
if pattern.isAppOfArity ``Grind.offset 2 then
|
| 541 |
+
let lhs := ppArg <| pattern.getArg! 0
|
| 542 |
+
let rhs := ppPattern <| pattern.getArg! 1
|
| 543 |
+
return m!"{lhs} + {rhs}"
|
| 544 |
+
else
|
| 545 |
+
let mut r := m!"{pattern.getAppFn}"
|
| 546 |
+
for arg in pattern.getAppArgs do
|
| 547 |
+
r := r ++ " " ++ ppArg arg
|
| 548 |
+
return r
|
| 549 |
+
where
|
| 550 |
+
ppArg (arg : Expr) : MessageData :=
|
| 551 |
+
if isAtomicPattern arg then
|
| 552 |
+
ppPattern arg
|
| 553 |
+
else
|
| 554 |
+
.paren (ppPattern arg)
|
| 555 |
+
|
| 556 |
+
namespace NormalizePattern
|
| 557 |
+
|
| 558 |
+
structure State where
|
| 559 |
+
symbols : Array HeadIndex := #[]
|
| 560 |
+
symbolSet : Std.HashSet HeadIndex := {}
|
| 561 |
+
bvarsFound : Std.HashSet Nat := {}
|
| 562 |
+
|
| 563 |
+
private structure Context where
|
| 564 |
+
symPrios : SymbolPriorities
|
| 565 |
+
/-- Only symbols with priority `>= minPrio` are considered in patterns. -/
|
| 566 |
+
minPrio : Nat
|
| 567 |
+
|
| 568 |
+
abbrev M := ReaderT Context StateRefT State MetaM
|
| 569 |
+
|
| 570 |
+
/-- Helper declaration for finding bootstrapping issues. See `isCandidateSymbol`. -/
|
| 571 |
+
private abbrev badForPatterns := [``Eq, ``HEq, ``Iff, ``And, ``Or, ``Not]
|
| 572 |
+
|
| 573 |
+
def isCandidateSymbol (declName : Name) (root : Bool) : M Bool := do
|
| 574 |
+
let ctx β read
|
| 575 |
+
let prio := ctx.symPrios.getPrio declName
|
| 576 |
+
-- Priority 0 are never considered, they are treated as forbidden
|
| 577 |
+
if prio == 0 then return false
|
| 578 |
+
-- Remark: uncomment the following code to fix bootstrapping issues
|
| 579 |
+
-- if declName β badForPatterns then
|
| 580 |
+
-- throwError "INSERT `import Init.Grind.Tactics`, otherwise a pattern containing `{declName}` will be used, prio: {prio}"
|
| 581 |
+
-- If it is the root symbol, then we check whether `prio β₯ minPrio`
|
| 582 |
+
if root then
|
| 583 |
+
return prio β₯ ctx.minPrio
|
| 584 |
+
else
|
| 585 |
+
return true
|
| 586 |
+
|
| 587 |
+
private def saveSymbol (h : HeadIndex) : M Unit := do
|
| 588 |
+
if let .const declName := h then
|
| 589 |
+
if declName == ``Grind.genHEqPattern || declName == ``Grind.genPattern then
|
| 590 |
+
-- We do not save gadgets in the list of symbols.
|
| 591 |
+
return ()
|
| 592 |
+
unless (β get).symbolSet.contains h do
|
| 593 |
+
modify fun s => { s with symbols := s.symbols.push h, symbolSet := s.symbolSet.insert h }
|
| 594 |
+
|
| 595 |
+
private def foundBVar (idx : Nat) : M Bool :=
|
| 596 |
+
return (β get).bvarsFound.contains idx
|
| 597 |
+
|
| 598 |
+
private def saveBVar (idx : Nat) : M Unit := do
|
| 599 |
+
modify fun s => { s with bvarsFound := s.bvarsFound.insert idx }
|
| 600 |
+
|
| 601 |
+
inductive PatternArgKind where
|
| 602 |
+
| /-- Argument is relevant for E-matching. -/
|
| 603 |
+
relevant
|
| 604 |
+
| /-- Instance implicit arguments are considered support and handled using `isDefEq`. -/
|
| 605 |
+
instImplicit
|
| 606 |
+
| /-- Proofs are ignored during E-matching. Lean is proof irrelevant. -/
|
| 607 |
+
proof
|
| 608 |
+
| /--
|
| 609 |
+
Types and type formers are mostly ignored during E-matching, and processed using
|
| 610 |
+
`isDefEq`. However, if the argument is of the form `C ..` where `C` is inductive type
|
| 611 |
+
we process it as part of the pattern. Suppose we have `as bs : List Ξ±`, and a pattern
|
| 612 |
+
candidate expression `as ++ bs`, i.e., `@HAppend.hAppend (List Ξ±) (List Ξ±) (List Ξ±) inst as bs`.
|
| 613 |
+
If we completely ignore the types, the pattern will just be
|
| 614 |
+
```
|
| 615 |
+
@HAppend.hAppend _ _ _ _ #1 #0
|
| 616 |
+
```
|
| 617 |
+
This is not ideal because the E-matcher will try it in any goal that contains `++`,
|
| 618 |
+
even if it does not even mention lists.
|
| 619 |
+
-/
|
| 620 |
+
typeFormer
|
| 621 |
+
deriving Repr
|
| 622 |
+
|
| 623 |
+
def PatternArgKind.isSupport : PatternArgKind β Bool
|
| 624 |
+
| .relevant => false
|
| 625 |
+
| _ => true
|
| 626 |
+
|
| 627 |
+
/--
|
| 628 |
+
Returns an array `kinds` s.ts `kinds[i]` is the kind of the corresponding argument.
|
| 629 |
+
|
| 630 |
+
- a type (that is not a proposition) or type former (which has forward dependencies) or
|
| 631 |
+
- a proof, or
|
| 632 |
+
- an instance implicit argument
|
| 633 |
+
|
| 634 |
+
When `kinds[i].isSupport` is `true`, we say the corresponding argument is a "support" argument.
|
| 635 |
+
-/
|
| 636 |
+
def getPatternArgKinds (f : Expr) (numArgs : Nat) : MetaM (Array PatternArgKind) := do
|
| 637 |
+
let pinfos := (β getFunInfoNArgs f numArgs).paramInfo
|
| 638 |
+
forallBoundedTelescope (β inferType f) numArgs fun xs _ => do
|
| 639 |
+
xs.mapIdxM fun idx x => do
|
| 640 |
+
if (β isProp x) then
|
| 641 |
+
return .relevant
|
| 642 |
+
else if (β isProof x) then
|
| 643 |
+
return .proof
|
| 644 |
+
else if (β isTypeFormer x) then
|
| 645 |
+
if h : idx < pinfos.size then
|
| 646 |
+
/-
|
| 647 |
+
We originally wanted to ignore types and type formers in `grind` and treat them as supporting elements.
|
| 648 |
+
Thus, we wanted to always return `.typeFormer`. However, we changed our heuristic because of the following example:
|
| 649 |
+
```
|
| 650 |
+
example {Ξ±} (f : Ξ± β Type) (a : Ξ±) (h : β x, Nonempty (f x)) : Nonempty (f a) := by
|
| 651 |
+
grind
|
| 652 |
+
```
|
| 653 |
+
In this example, we are reasoning about types. Therefore, we adjusted the heuristic as follows:
|
| 654 |
+
a type or type former is considered a supporting element only if it has forward dependencies.
|
| 655 |
+
Note that this is not the case for `Nonempty`.
|
| 656 |
+
-/
|
| 657 |
+
if pinfos[idx].hasFwdDeps then return .typeFormer else return .relevant
|
| 658 |
+
else
|
| 659 |
+
return .typeFormer
|
| 660 |
+
else if (β x.fvarId!.getDecl).binderInfo matches .instImplicit then
|
| 661 |
+
return .instImplicit
|
| 662 |
+
else
|
| 663 |
+
return .relevant
|
| 664 |
+
|
| 665 |
+
private def getPatternFn? (pattern : Expr) (inSupport : Bool) (root : Bool) (argKind : PatternArgKind) : M (Option Expr) := do
|
| 666 |
+
if !pattern.isApp && !pattern.isConst then
|
| 667 |
+
return none
|
| 668 |
+
else match pattern.getAppFn with
|
| 669 |
+
| f@(.const declName _) =>
|
| 670 |
+
if !(β isCandidateSymbol declName root) then
|
| 671 |
+
return none
|
| 672 |
+
if declName == ``Grind.genPattern || declName == ``Grind.genHEqPattern then
|
| 673 |
+
return some f
|
| 674 |
+
if inSupport then
|
| 675 |
+
if argKind matches .typeFormer | .relevant then
|
| 676 |
+
if (β isInductive declName) then
|
| 677 |
+
return some f
|
| 678 |
+
return none
|
| 679 |
+
return some f
|
| 680 |
+
| f@(.fvar _) =>
|
| 681 |
+
if inSupport then return none else return some f
|
| 682 |
+
| _ =>
|
| 683 |
+
return none
|
| 684 |
+
|
| 685 |
+
private partial def go (pattern : Expr) (inSupport : Bool) (root : Bool) : M Expr := do
|
| 686 |
+
if let some (e, k) := isOffsetPattern? pattern then
|
| 687 |
+
let e β goArg e inSupport .relevant
|
| 688 |
+
if e == dontCare then
|
| 689 |
+
return dontCare
|
| 690 |
+
else
|
| 691 |
+
return mkOffsetPattern e k
|
| 692 |
+
let some f β getPatternFn? pattern inSupport root .relevant
|
| 693 |
+
| throwError "invalid pattern, (non-forbidden) application expected{indentExpr pattern}"
|
| 694 |
+
assert! f.isConst || f.isFVar
|
| 695 |
+
unless f.isConstOf ``Grind.eqBwdPattern do
|
| 696 |
+
saveSymbol f.toHeadIndex
|
| 697 |
+
let mut args := pattern.getAppArgs.toVector
|
| 698 |
+
let patternArgKinds β getPatternArgKinds f args.size
|
| 699 |
+
for h : i in [:args.size] do
|
| 700 |
+
let arg := args[i]
|
| 701 |
+
let argKind := patternArgKinds[i]?.getD .relevant
|
| 702 |
+
args := args.set i (β goArg arg (inSupport || argKind.isSupport) argKind)
|
| 703 |
+
return mkAppN f args.toArray
|
| 704 |
+
where
|
| 705 |
+
goArg (arg : Expr) (inSupport : Bool) (argKind : PatternArgKind) : M Expr := do
|
| 706 |
+
if !arg.hasLooseBVars then
|
| 707 |
+
if arg.hasMVar then
|
| 708 |
+
pure dontCare
|
| 709 |
+
else
|
| 710 |
+
return mkGroundPattern (β expandOffsetPatterns arg)
|
| 711 |
+
else match arg with
|
| 712 |
+
| .bvar idx =>
|
| 713 |
+
if inSupport && (β foundBVar idx) then
|
| 714 |
+
pure dontCare
|
| 715 |
+
else
|
| 716 |
+
saveBVar idx
|
| 717 |
+
pure arg
|
| 718 |
+
| _ =>
|
| 719 |
+
if let some _ β getPatternFn? arg inSupport (root := false) argKind then
|
| 720 |
+
go arg inSupport (root := false)
|
| 721 |
+
else
|
| 722 |
+
pure dontCare
|
| 723 |
+
|
| 724 |
+
def main (patterns : List Expr) (symPrios : SymbolPriorities) (minPrio : Nat) : MetaM (List Expr Γ List HeadIndex Γ Std.HashSet Nat) := do
|
| 725 |
+
let (patterns, s) β patterns.mapM (go (inSupport := false) (root := true)) { symPrios, minPrio } |>.run {}
|
| 726 |
+
return (patterns, s.symbols.toList, s.bvarsFound)
|
| 727 |
+
|
| 728 |
+
def normalizePattern (e : Expr) : M Expr := do
|
| 729 |
+
go e (inSupport := false) (root := true)
|
| 730 |
+
|
| 731 |
+
end NormalizePattern
|
| 732 |
+
|
| 733 |
+
/--
|
| 734 |
+
Returns `true` if free variables in `type` are not in `thmVars` or are in `fvarsFound`.
|
| 735 |
+
We use this function to check whether `type` is fully instantiated.
|
| 736 |
+
-/
|
| 737 |
+
private def checkTypeFVars (thmVars : FVarIdSet) (fvarsFound : FVarIdSet) (type : Expr) : Bool :=
|
| 738 |
+
let typeFVars := (collectFVars {} type).fvarIds
|
| 739 |
+
typeFVars.all fun fvarId => !thmVars.contains fvarId || fvarsFound.contains fvarId
|
| 740 |
+
|
| 741 |
+
/--
|
| 742 |
+
Given an type class instance type `instType`, returns true if free variables in input parameters
|
| 743 |
+
1- are not in `thmVars`, or
|
| 744 |
+
2- are in `fvarsFound`.
|
| 745 |
+
Remark: `fvarsFound` is a subset of `thmVars`
|
| 746 |
+
-/
|
| 747 |
+
private def canBeSynthesized (thmVars : FVarIdSet) (fvarsFound : FVarIdSet) (instType : Expr) : MetaM Bool := do
|
| 748 |
+
forallTelescopeReducing instType fun xs type => type.withApp fun classFn classArgs => do
|
| 749 |
+
for x in xs do
|
| 750 |
+
unless checkTypeFVars thmVars fvarsFound (β inferType x) do return false
|
| 751 |
+
forallBoundedTelescope (β inferType classFn) type.getAppNumArgs fun params _ => do
|
| 752 |
+
for param in params, classArg in classArgs do
|
| 753 |
+
let paramType β inferType param
|
| 754 |
+
if !paramType.isAppOf ``semiOutParam && !paramType.isAppOf ``outParam then
|
| 755 |
+
unless checkTypeFVars thmVars fvarsFound classArg do
|
| 756 |
+
return false
|
| 757 |
+
return true
|
| 758 |
+
|
| 759 |
+
/--
|
| 760 |
+
Auxiliary type for the `checkCoverage` function.
|
| 761 |
+
-/
|
| 762 |
+
inductive CheckCoverageResult where
|
| 763 |
+
| /-- `checkCoverage` succeeded -/
|
| 764 |
+
ok
|
| 765 |
+
| /--
|
| 766 |
+
`checkCoverage` failed because some of the theorem parameters are missing,
|
| 767 |
+
`pos` contains their positions
|
| 768 |
+
-/
|
| 769 |
+
missing (pos : List Nat)
|
| 770 |
+
|
| 771 |
+
/--
|
| 772 |
+
After we process a set of patterns, we obtain the set of de Bruijn indices in these patterns.
|
| 773 |
+
We say they are pattern variables. This function checks whether the set of pattern variables is sufficient for
|
| 774 |
+
instantiating the theorem with proof `thmProof`. The theorem has `numParams` parameters.
|
| 775 |
+
The missing parameters:
|
| 776 |
+
1- we may be able to infer them using type inference or type class synthesis, or
|
| 777 |
+
2- they are propositions, and may become hypotheses of the instantiated theorem.
|
| 778 |
+
|
| 779 |
+
For type class instance parameters, we must check whether the free variables in class input parameters are available.
|
| 780 |
+
-/
|
| 781 |
+
private def checkCoverage (thmProof : Expr) (numParams : Nat) (bvarsFound : Std.HashSet Nat) : MetaM CheckCoverageResult := do
|
| 782 |
+
if bvarsFound.size == numParams then return .ok
|
| 783 |
+
forallBoundedTelescope (β inferType thmProof) numParams fun xs _ => do
|
| 784 |
+
assert! numParams == xs.size
|
| 785 |
+
let patternVars := bvarsFound.toList.map fun bidx => xs[numParams - bidx - 1]!.fvarId!
|
| 786 |
+
-- `xs` as a `FVarIdSet`.
|
| 787 |
+
let thmVars : FVarIdSet := RBTree.ofList <| xs.toList.map (Β·.fvarId!)
|
| 788 |
+
-- Collect free variables occurring in `e`, and insert the ones that are in `thmVars` into `fvarsFound`
|
| 789 |
+
let update (fvarsFound : FVarIdSet) (e : Expr) : FVarIdSet :=
|
| 790 |
+
(collectFVars {} e).fvarIds.foldl (init := fvarsFound) fun s fvarId =>
|
| 791 |
+
if thmVars.contains fvarId then s.insert fvarId else s
|
| 792 |
+
-- Theorem variables found so far. We initialize with the variables occurring in patterns
|
| 793 |
+
-- Remark: fvarsFound is a subset of thmVars
|
| 794 |
+
let mut fvarsFound : FVarIdSet := RBTree.ofList patternVars
|
| 795 |
+
for patternVar in patternVars do
|
| 796 |
+
let type β patternVar.getType
|
| 797 |
+
fvarsFound := update fvarsFound type
|
| 798 |
+
if fvarsFound.size == numParams then return .ok
|
| 799 |
+
-- Now, we keep traversing remaining variables and collecting
|
| 800 |
+
-- `processed` contains the variables we have already processed.
|
| 801 |
+
let mut processed : FVarIdSet := RBTree.ofList patternVars
|
| 802 |
+
let mut modified := false
|
| 803 |
+
repeat
|
| 804 |
+
modified := false
|
| 805 |
+
for x in xs do
|
| 806 |
+
let fvarId := x.fvarId!
|
| 807 |
+
unless processed.contains fvarId do
|
| 808 |
+
let xType β inferType x
|
| 809 |
+
if fvarsFound.contains fvarId then
|
| 810 |
+
-- Collect free vars in `x`s type and mark as processed
|
| 811 |
+
fvarsFound := update fvarsFound xType
|
| 812 |
+
processed := processed.insert fvarId
|
| 813 |
+
modified := true
|
| 814 |
+
else if (β isProp xType) then
|
| 815 |
+
-- If `x` is a proposition, and all theorem variables in `x`s type have already been found
|
| 816 |
+
-- add it to `fvarsFound` and mark it as processed.
|
| 817 |
+
if checkTypeFVars thmVars fvarsFound xType then
|
| 818 |
+
fvarsFound := fvarsFound.insert fvarId
|
| 819 |
+
processed := processed.insert fvarId
|
| 820 |
+
modified := true
|
| 821 |
+
else if (β fvarId.getDecl).binderInfo matches .instImplicit then
|
| 822 |
+
-- If `x` is instance implicit, check whether
|
| 823 |
+
-- we have found all free variables needed to synthesize instance
|
| 824 |
+
if (β canBeSynthesized thmVars fvarsFound xType) then
|
| 825 |
+
fvarsFound := fvarsFound.insert fvarId
|
| 826 |
+
fvarsFound := update fvarsFound xType
|
| 827 |
+
processed := processed.insert fvarId
|
| 828 |
+
modified := true
|
| 829 |
+
if fvarsFound.size == numParams then
|
| 830 |
+
return .ok
|
| 831 |
+
if !modified then
|
| 832 |
+
break
|
| 833 |
+
let mut pos := #[]
|
| 834 |
+
for h : i in [:xs.size] do
|
| 835 |
+
let fvarId := xs[i].fvarId!
|
| 836 |
+
unless fvarsFound.contains fvarId do
|
| 837 |
+
pos := pos.push i
|
| 838 |
+
return .missing pos.toList
|
| 839 |
+
|
| 840 |
+
/--
|
| 841 |
+
Given a theorem with proof `proof` and `numParams` parameters, returns a message
|
| 842 |
+
containing the parameters at positions `paramPos`.
|
| 843 |
+
-/
|
| 844 |
+
private def ppParamsAt (proof : Expr) (numParams : Nat) (paramPos : List Nat) : MetaM MessageData := do
|
| 845 |
+
forallBoundedTelescope (β inferType proof) numParams fun xs _ => do
|
| 846 |
+
let mut msg := m!""
|
| 847 |
+
let mut first := true
|
| 848 |
+
for h : i in [:xs.size] do
|
| 849 |
+
if paramPos.contains i then
|
| 850 |
+
let x := xs[i]
|
| 851 |
+
if first then first := false else msg := msg ++ "\n"
|
| 852 |
+
msg := msg ++ m!"{x} : {β inferType x}"
|
| 853 |
+
addMessageContextFull msg
|
| 854 |
+
|
| 855 |
+
private def logPatternWhen (showInfo : Bool) (origin : Origin) (patterns : List Expr) : MetaM Unit := do
|
| 856 |
+
if showInfo then
|
| 857 |
+
logInfo m!"{β origin.pp}: {patterns.map ppPattern}"
|
| 858 |
+
|
| 859 |
+
/--
|
| 860 |
+
Creates an E-matching theorem for a theorem with proof `proof`, `numParams` parameters, and the given set of patterns.
|
| 861 |
+
Pattern variables are represented using de Bruijn indices.
|
| 862 |
+
-/
|
| 863 |
+
def mkEMatchTheoremCore (origin : Origin) (levelParams : Array Name) (numParams : Nat) (proof : Expr)
|
| 864 |
+
(patterns : List Expr) (kind : EMatchTheoremKind) (showInfo := false) : MetaM EMatchTheorem := do
|
| 865 |
+
-- the patterns have already been selected, there is no point in using priorities here
|
| 866 |
+
let (patterns, symbols, bvarFound) β NormalizePattern.main patterns (β getGlobalSymbolPriorities) (minPrio := 1)
|
| 867 |
+
if symbols.isEmpty then
|
| 868 |
+
throwError "invalid pattern for `{β origin.pp}`{indentD (patterns.map ppPattern)}\nthe pattern does not contain constant symbols for indexing"
|
| 869 |
+
trace[grind.ematch.pattern] "{β origin.pp}: {patterns.map ppPattern}"
|
| 870 |
+
if let .missing pos β checkCoverage proof numParams bvarFound then
|
| 871 |
+
let pats : MessageData := m!"{patterns.map ppPattern}"
|
| 872 |
+
throwError "invalid pattern(s) for `{β origin.pp}`{indentD pats}\nthe following theorem parameters cannot be instantiated:{indentD (β ppParamsAt proof numParams pos)}"
|
| 873 |
+
logPatternWhen showInfo origin patterns
|
| 874 |
+
return {
|
| 875 |
+
proof, patterns, numParams, symbols
|
| 876 |
+
levelParams, origin, kind
|
| 877 |
+
}
|
| 878 |
+
|
| 879 |
+
private def getProofFor (declName : Name) : MetaM Expr := do
|
| 880 |
+
let info β getConstInfo declName
|
| 881 |
+
-- For theorems, `isProp` has already been checked at declaration time
|
| 882 |
+
unless wasOriginallyTheorem (β getEnv) declName do
|
| 883 |
+
unless (β isProp info.type) do
|
| 884 |
+
throwError "invalid E-matching theorem `{declName}`, type is not a proposition"
|
| 885 |
+
let us := info.levelParams.map mkLevelParam
|
| 886 |
+
return mkConst declName us
|
| 887 |
+
|
| 888 |
+
/--
|
| 889 |
+
Creates an E-matching theorem for `declName` with `numParams` parameters, and the given set of patterns.
|
| 890 |
+
Pattern variables are represented using de Bruijn indices.
|
| 891 |
+
-/
|
| 892 |
+
def mkEMatchTheorem (declName : Name) (numParams : Nat) (patterns : List Expr) (kind : EMatchTheoremKind) : MetaM EMatchTheorem := do
|
| 893 |
+
mkEMatchTheoremCore (.decl declName) #[] numParams (β getProofFor declName) patterns kind
|
| 894 |
+
|
| 895 |
+
/--
|
| 896 |
+
Given a theorem with proof `proof` and type of the form `β (a_1 ... a_n), lhs = rhs`,
|
| 897 |
+
creates an E-matching pattern for it using `addEMatchTheorem n [lhs]`
|
| 898 |
+
If `normalizePattern` is true, it applies the `grind` simplification theorems and simprocs to the pattern.
|
| 899 |
+
-/
|
| 900 |
+
def mkEMatchEqTheoremCore (origin : Origin) (levelParams : Array Name) (proof : Expr) (normalizePattern : Bool)
|
| 901 |
+
(useLhs : Bool) (gen : Bool) (showInfo := false) : MetaM EMatchTheorem := do
|
| 902 |
+
let (numParams, patterns) β forallTelescopeReducing (β inferEMatchProofType proof gen) fun xs type => do
|
| 903 |
+
let (lhs, rhs) β match_expr type with
|
| 904 |
+
| Eq _ lhs rhs => pure (lhs, rhs)
|
| 905 |
+
| Iff lhs rhs => pure (lhs, rhs)
|
| 906 |
+
| HEq _ lhs _ rhs => pure (lhs, rhs)
|
| 907 |
+
| _ => throwError "invalid E-matching equality theorem, conclusion must be an equality{indentExpr type}"
|
| 908 |
+
let pat := if useLhs then lhs else rhs
|
| 909 |
+
trace[grind.debug.ematch.pattern] "mkEMatchEqTheoremCore: origin: {β origin.pp}, pat: {pat}, useLhs: {useLhs}"
|
| 910 |
+
let pat β preprocessPattern pat normalizePattern
|
| 911 |
+
trace[grind.debug.ematch.pattern] "mkEMatchEqTheoremCore: after preprocessing: {pat}, {β normalize pat normConfig}"
|
| 912 |
+
let pats := splitWhileForbidden (pat.abstract xs)
|
| 913 |
+
return (xs.size, pats)
|
| 914 |
+
mkEMatchTheoremCore origin levelParams numParams proof patterns (if useLhs then .eqLhs gen else .eqRhs gen) (showInfo := showInfo)
|
| 915 |
+
|
| 916 |
+
def mkEMatchEqBwdTheoremCore (origin : Origin) (levelParams : Array Name) (proof : Expr) (showInfo := false) : MetaM EMatchTheorem := do
|
| 917 |
+
let (numParams, patterns) β forallTelescopeReducing (β inferEMatchProofType proof (gen := false)) fun xs type => do
|
| 918 |
+
let_expr f@Eq Ξ± lhs rhs := type
|
| 919 |
+
| throwError "invalid E-matching `β=` theorem, conclusion must be an equality{indentExpr type}"
|
| 920 |
+
let pat β preprocessPattern (mkEqBwdPattern f.constLevels! Ξ± lhs rhs)
|
| 921 |
+
return (xs.size, [pat.abstract xs])
|
| 922 |
+
mkEMatchTheoremCore origin levelParams numParams proof patterns .eqBwd (showInfo := showInfo)
|
| 923 |
+
|
| 924 |
+
/--
|
| 925 |
+
Given theorem with name `declName` and type of the form `β (a_1 ... a_n), lhs = rhs`,
|
| 926 |
+
creates an E-matching pattern for it using `addEMatchTheorem n [lhs]`
|
| 927 |
+
|
| 928 |
+
If `normalizePattern` is true, it applies the `grind` simplification theorems and simprocs to the
|
| 929 |
+
pattern.
|
| 930 |
+
-/
|
| 931 |
+
def mkEMatchEqTheorem (declName : Name) (normalizePattern := true) (useLhs : Bool := true) (gen : Bool := false) (showInfo := false) : MetaM EMatchTheorem := do
|
| 932 |
+
mkEMatchEqTheoremCore (.decl declName) #[] (β getProofFor declName) normalizePattern useLhs gen (showInfo := showInfo)
|
| 933 |
+
|
| 934 |
+
/--
|
| 935 |
+
Adds an E-matching theorem to the environment.
|
| 936 |
+
See `mkEMatchTheorem`.
|
| 937 |
+
-/
|
| 938 |
+
def addEMatchTheorem (declName : Name) (numParams : Nat) (patterns : List Expr) (kind : EMatchTheoremKind) : MetaM Unit := do
|
| 939 |
+
ematchTheoremsExt.add (β mkEMatchTheorem declName numParams patterns kind)
|
| 940 |
+
|
| 941 |
+
/--
|
| 942 |
+
Adds an E-matching equality theorem to the environment.
|
| 943 |
+
See `mkEMatchEqTheorem`.
|
| 944 |
+
-/
|
| 945 |
+
def addEMatchEqTheorem (declName : Name) : MetaM Unit := do
|
| 946 |
+
ematchTheoremsExt.add (β mkEMatchEqTheorem declName)
|
| 947 |
+
|
| 948 |
+
/-- Returns the E-matching theorems registered in the environment. -/
|
| 949 |
+
def getEMatchTheorems : CoreM EMatchTheorems :=
|
| 950 |
+
return ematchTheoremsExt.getState (β getEnv)
|
| 951 |
+
|
| 952 |
+
/-- Returns the types of `xs` that are propositions. -/
|
| 953 |
+
private def getPropTypes (xs : Array Expr) : MetaM (Array Expr) :=
|
| 954 |
+
xs.filterMapM fun x => do
|
| 955 |
+
let type β inferType x
|
| 956 |
+
if (β isProp type) then return some type else return none
|
| 957 |
+
|
| 958 |
+
/-- State for the (pattern) `CollectorM` monad -/
|
| 959 |
+
private structure Collector.State where
|
| 960 |
+
/-- Pattern found so far. -/
|
| 961 |
+
patterns : Array Expr := #[]
|
| 962 |
+
done : Bool := false
|
| 963 |
+
|
| 964 |
+
private structure Collector.Context where
|
| 965 |
+
proof : Expr
|
| 966 |
+
xs : Array Expr
|
| 967 |
+
|
| 968 |
+
/-- Monad for collecting patterns for a theorem. -/
|
| 969 |
+
private abbrev CollectorM := ReaderT Collector.Context $ StateRefT Collector.State NormalizePattern.M
|
| 970 |
+
|
| 971 |
+
/-- Similar to `getPatternFn?`, but operates on expressions that do not contain loose de Bruijn variables. -/
|
| 972 |
+
private def isPatternFnCandidate (f : Expr) : CollectorM Bool := do
|
| 973 |
+
match f with
|
| 974 |
+
| .const declName _ => NormalizePattern.isCandidateSymbol declName (root := true)
|
| 975 |
+
| .fvar .. => return !(β read).xs.contains f
|
| 976 |
+
| _ => return false
|
| 977 |
+
|
| 978 |
+
private def addNewPattern (p : Expr) : CollectorM Unit := do
|
| 979 |
+
trace[grind.debug.ematch.pattern] "found pattern: {ppPattern p}"
|
| 980 |
+
let bvarsFound := (β getThe NormalizePattern.State).bvarsFound
|
| 981 |
+
let done := (β checkCoverage (β read).proof (β read).xs.size bvarsFound) matches .ok
|
| 982 |
+
if done then
|
| 983 |
+
trace[grind.debug.ematch.pattern] "found full coverage"
|
| 984 |
+
modify fun s => { s with patterns := s.patterns.push p, done }
|
| 985 |
+
|
| 986 |
+
/-- Collect the pattern (i.e., de Bruijn) variables in the given pattern. -/
|
| 987 |
+
private def collectPatternBVars (p : Expr) : List Nat :=
|
| 988 |
+
go p |>.run [] |>.2
|
| 989 |
+
where
|
| 990 |
+
go (e : Expr) : StateM (List Nat) Unit := do
|
| 991 |
+
match e with
|
| 992 |
+
| .app f a => go f; go a
|
| 993 |
+
| .mdata _ b => go b
|
| 994 |
+
| .bvar idx => modify fun s => if s.contains idx then s else idx :: s
|
| 995 |
+
| _ => return ()
|
| 996 |
+
|
| 997 |
+
private def diff (s : List Nat) (found : Std.HashSet Nat) : List Nat :=
|
| 998 |
+
if found.isEmpty then s else s.filter fun x => !found.contains x
|
| 999 |
+
|
| 1000 |
+
/--
|
| 1001 |
+
Returns `true` if pattern `p` contains a child `c` such that
|
| 1002 |
+
1- `p` and `c` have the same new pattern variables. We say a pattern variable is new if it is not in `alreadyFound`.
|
| 1003 |
+
2- `c` is not a support argument. See `NormalizePattern.getPatternSupportMask` for definition.
|
| 1004 |
+
3- `c` is not an offset pattern.
|
| 1005 |
+
4- `c` is not a bound variable.
|
| 1006 |
+
-/
|
| 1007 |
+
private def hasChildWithSameNewBVars (p : Expr)
|
| 1008 |
+
(argKinds : Array NormalizePattern.PatternArgKind) (alreadyFound : Std.HashSet Nat) : CoreM Bool := do
|
| 1009 |
+
let s := diff (collectPatternBVars p) alreadyFound
|
| 1010 |
+
for arg in p.getAppArgs, argKind in argKinds do
|
| 1011 |
+
unless argKind.isSupport do
|
| 1012 |
+
unless arg.isBVar do
|
| 1013 |
+
unless isOffsetPattern? arg |>.isSome do
|
| 1014 |
+
let sArg := diff (collectPatternBVars arg) alreadyFound
|
| 1015 |
+
if s β sArg then
|
| 1016 |
+
return true
|
| 1017 |
+
return false
|
| 1018 |
+
|
| 1019 |
+
private partial def collect (e : Expr) : CollectorM Unit := do
|
| 1020 |
+
if (β get).done then return ()
|
| 1021 |
+
match e with
|
| 1022 |
+
| .app .. =>
|
| 1023 |
+
trace[grind.debug.ematch.pattern] "collect: {e}"
|
| 1024 |
+
let f := e.getAppFn
|
| 1025 |
+
let argKinds β NormalizePattern.getPatternArgKinds f e.getAppNumArgs
|
| 1026 |
+
if (β isPatternFnCandidate f) then
|
| 1027 |
+
let saved β getThe NormalizePattern.State
|
| 1028 |
+
try
|
| 1029 |
+
trace[grind.debug.ematch.pattern] "candidate: {e}"
|
| 1030 |
+
let p := e.abstract (β read).xs
|
| 1031 |
+
unless p.hasLooseBVars do
|
| 1032 |
+
trace[grind.debug.ematch.pattern] "skip, does not contain pattern variables"
|
| 1033 |
+
return ()
|
| 1034 |
+
let p β NormalizePattern.normalizePattern p
|
| 1035 |
+
if saved.bvarsFound.size < (β getThe NormalizePattern.State).bvarsFound.size then
|
| 1036 |
+
unless (β hasChildWithSameNewBVars p argKinds saved.bvarsFound) do
|
| 1037 |
+
addNewPattern p
|
| 1038 |
+
return ()
|
| 1039 |
+
trace[grind.debug.ematch.pattern] "skip, no new variables covered"
|
| 1040 |
+
-- restore state and continue search
|
| 1041 |
+
set saved
|
| 1042 |
+
catch ex =>
|
| 1043 |
+
trace[grind.debug.ematch.pattern] "skip, exception during normalization{indentD ex.toMessageData}"
|
| 1044 |
+
-- restore state and continue search
|
| 1045 |
+
set saved
|
| 1046 |
+
let args := e.getAppArgs
|
| 1047 |
+
for arg in args, argKind in argKinds do
|
| 1048 |
+
trace[grind.debug.ematch.pattern] "arg: {arg}, support: {argKind.isSupport}"
|
| 1049 |
+
unless argKind.isSupport do
|
| 1050 |
+
collect arg
|
| 1051 |
+
| .forallE _ d b _ =>
|
| 1052 |
+
if (β pure e.isArrow <&&> isProp d <&&> isProp b) then
|
| 1053 |
+
collect d
|
| 1054 |
+
collect b
|
| 1055 |
+
| _ => return ()
|
| 1056 |
+
|
| 1057 |
+
private def collectPatterns? (proof : Expr) (xs : Array Expr) (searchPlaces : Array Expr) (symPrios : SymbolPriorities) (minPrio : Nat)
|
| 1058 |
+
: MetaM (Option (List Expr Γ List HeadIndex)) := do
|
| 1059 |
+
let go : CollectorM (Option (List Expr)) := do
|
| 1060 |
+
for place in searchPlaces do
|
| 1061 |
+
trace[grind.debug.ematch.pattern] "place: {place}"
|
| 1062 |
+
let place β preprocessPattern place
|
| 1063 |
+
collect place
|
| 1064 |
+
if (β get).done then
|
| 1065 |
+
return some ((β get).patterns.toList)
|
| 1066 |
+
return none
|
| 1067 |
+
let (some ps, s) β go { proof, xs } |>.run' {} { symPrios, minPrio } |>.run {}
|
| 1068 |
+
| return none
|
| 1069 |
+
return some (ps, s.symbols.toList)
|
| 1070 |
+
|
| 1071 |
+
/--
|
| 1072 |
+
Tries to find a ground pattern to activate the theorem.
|
| 1073 |
+
This is used for theorems such as `theorem evenZ : Even 0`.
|
| 1074 |
+
This function is only used if `collectPatterns?` returns `none`.
|
| 1075 |
+
|
| 1076 |
+
Remark: only symbols with priority `>= minPrio` are considered.
|
| 1077 |
+
-/
|
| 1078 |
+
private partial def collectGroundPattern? (proof : Expr) (xs : Array Expr) (searchPlaces : Array Expr) (symPrios: SymbolPriorities) (minPrio : Nat)
|
| 1079 |
+
: MetaM (Option (Expr Γ List HeadIndex)) := do
|
| 1080 |
+
unless (β checkCoverage proof xs.size {}) matches .ok do
|
| 1081 |
+
return none
|
| 1082 |
+
let go? : CollectorM (Option Expr) := do
|
| 1083 |
+
for place in searchPlaces do
|
| 1084 |
+
let place β preprocessPattern place
|
| 1085 |
+
if let some r β visit? place then
|
| 1086 |
+
return r
|
| 1087 |
+
return none
|
| 1088 |
+
let (some p, s) β go? { proof, xs } |>.run' {} { symPrios, minPrio } |>.run {}
|
| 1089 |
+
| return none
|
| 1090 |
+
return some (p, s.symbols.toList)
|
| 1091 |
+
where
|
| 1092 |
+
visit? (e : Expr) : CollectorM (Option Expr) := do
|
| 1093 |
+
match e with
|
| 1094 |
+
| .app .. =>
|
| 1095 |
+
let f := e.getAppFn
|
| 1096 |
+
if (β isPatternFnCandidate f) then
|
| 1097 |
+
let e β NormalizePattern.normalizePattern e
|
| 1098 |
+
return some e
|
| 1099 |
+
else
|
| 1100 |
+
let args := e.getAppArgs
|
| 1101 |
+
for arg in args, argKind in (β NormalizePattern.getPatternArgKinds f args.size) do
|
| 1102 |
+
unless argKind.isSupport do
|
| 1103 |
+
if let some r β visit? arg then
|
| 1104 |
+
return r
|
| 1105 |
+
return none
|
| 1106 |
+
| .forallE _ d b _ =>
|
| 1107 |
+
if (β pure e.isArrow <&&> isProp d <&&> isProp b) then
|
| 1108 |
+
if let some d β visit? d then return d
|
| 1109 |
+
visit? b
|
| 1110 |
+
else
|
| 1111 |
+
return none
|
| 1112 |
+
| _ => return none
|
| 1113 |
+
|
| 1114 |
+
def EMatchTheoremKind.gen : EMatchTheoremKind β Bool
|
| 1115 |
+
| .eqLhs gen => gen
|
| 1116 |
+
| .eqRhs gen => gen
|
| 1117 |
+
| .eqBoth gen => gen
|
| 1118 |
+
| .default gen => gen
|
| 1119 |
+
| .bwd gen => gen
|
| 1120 |
+
| .eqBwd | .fwd | .rightLeft
|
| 1121 |
+
| .leftRight | .user => false
|
| 1122 |
+
|
| 1123 |
+
private def collectUsedPriorities (prios : SymbolPriorities) (searchPlaces : Array Expr) : Array Nat := Id.run do
|
| 1124 |
+
let mut s : Std.HashSet Nat := {}
|
| 1125 |
+
for place in searchPlaces do
|
| 1126 |
+
s := place.foldConsts (init := s) fun declName s =>
|
| 1127 |
+
let prio := prios.getPrio declName
|
| 1128 |
+
if prio > 0 then s.insert prio else s
|
| 1129 |
+
let r := s.toArray
|
| 1130 |
+
if r.isEmpty then
|
| 1131 |
+
return #[eval_prio default]
|
| 1132 |
+
else
|
| 1133 |
+
return r.qsort fun pβ pβ => pβ > pβ
|
| 1134 |
+
|
| 1135 |
+
/--
|
| 1136 |
+
Creates an E-match theorem using the given proof and kind.
|
| 1137 |
+
If `groundPatterns` is `true`, it accepts patterns without pattern variables. This is useful for
|
| 1138 |
+
theorems such as `theorem evenZ : Even 0`. For local theorems, we use `groundPatterns := false`
|
| 1139 |
+
since the theorem is already in the `grind` state and there is nothing to be instantiated.
|
| 1140 |
+
-/
|
| 1141 |
+
def mkEMatchTheoremWithKind?
|
| 1142 |
+
(origin : Origin) (levelParams : Array Name) (proof : Expr) (kind : EMatchTheoremKind) (symPrios : SymbolPriorities)
|
| 1143 |
+
(groundPatterns := true) (showInfo := false) : MetaM (Option EMatchTheorem) := do
|
| 1144 |
+
match kind with
|
| 1145 |
+
| .eqLhs gen =>
|
| 1146 |
+
return (β mkEMatchEqTheoremCore origin levelParams proof (normalizePattern := true) (useLhs := true) (gen := gen) (showInfo := showInfo))
|
| 1147 |
+
| .eqRhs gen =>
|
| 1148 |
+
return (β mkEMatchEqTheoremCore origin levelParams proof (normalizePattern := true) (useLhs := false) (gen := gen) (showInfo := showInfo))
|
| 1149 |
+
| .eqBwd =>
|
| 1150 |
+
return (β mkEMatchEqBwdTheoremCore origin levelParams proof (showInfo := showInfo))
|
| 1151 |
+
| _ =>
|
| 1152 |
+
pure ()
|
| 1153 |
+
let type β inferEMatchProofType proof kind.gen
|
| 1154 |
+
/-
|
| 1155 |
+
Remark: we should not use `forallTelescopeReducing` (with default reducibility) here
|
| 1156 |
+
because it may unfold a definition/abstraction, and then select a suboptimal pattern.
|
| 1157 |
+
Here is an example. Suppose we have
|
| 1158 |
+
```
|
| 1159 |
+
def State.le (Οβ Οβ : State) : Prop := β β¦x : Varβ¦ β¦v : Valβ¦, Οβ.find? x = some v β Οβ.find? x = some v
|
| 1160 |
+
|
| 1161 |
+
infix:50 " βΌ " => State.le
|
| 1162 |
+
```
|
| 1163 |
+
Then, we write the theorem
|
| 1164 |
+
```
|
| 1165 |
+
@[grind] theorem State.join_le_left (Οβ Οβ : State) : Οβ.join Οβ βΌ Οβ := by
|
| 1166 |
+
```
|
| 1167 |
+
We do not want `State.le` to be unfolded and the abstraction exposed.
|
| 1168 |
+
|
| 1169 |
+
That said, we must still reduce `[reducible]` definitions since `grind` unfolds them.
|
| 1170 |
+
-/
|
| 1171 |
+
withReducible <| forallTelescopeReducing type fun xs type => withDefault do
|
| 1172 |
+
let searchPlaces β match kind with
|
| 1173 |
+
| .fwd =>
|
| 1174 |
+
let ps β getPropTypes xs
|
| 1175 |
+
if ps.isEmpty then
|
| 1176 |
+
throwError "invalid `grind` forward theorem, theorem `{β origin.pp}` does not have propositional hypotheses"
|
| 1177 |
+
pure ps
|
| 1178 |
+
| .bwd _ => pure #[type]
|
| 1179 |
+
| .leftRight => pure <| (β getPropTypes xs).push type
|
| 1180 |
+
| .rightLeft => pure <| #[type] ++ (β getPropTypes xs).reverse
|
| 1181 |
+
| .default _ => pure <| #[type] ++ (β getPropTypes xs)
|
| 1182 |
+
| _ => unreachable!
|
| 1183 |
+
go xs searchPlaces
|
| 1184 |
+
where
|
| 1185 |
+
collect (xs : Array Expr) (searchPlaces : Array Expr) : MetaM (Option (List Expr Γ List HeadIndex)) := do
|
| 1186 |
+
let prios := collectUsedPriorities symPrios searchPlaces
|
| 1187 |
+
for minPrio in prios do
|
| 1188 |
+
if let some r β collectPatterns? proof xs searchPlaces symPrios minPrio then
|
| 1189 |
+
return some r
|
| 1190 |
+
else if groundPatterns then
|
| 1191 |
+
if let some (pattern, symbols) β collectGroundPattern? proof xs searchPlaces symPrios minPrio then
|
| 1192 |
+
return some ([pattern], symbols)
|
| 1193 |
+
return none
|
| 1194 |
+
|
| 1195 |
+
go (xs : Array Expr) (searchPlaces : Array Expr) : MetaM (Option EMatchTheorem) := do
|
| 1196 |
+
let some (patterns, symbols) β collect xs searchPlaces | return none
|
| 1197 |
+
let numParams := xs.size
|
| 1198 |
+
trace[grind.ematch.pattern] "{β origin.pp}: {patterns.map ppPattern}"
|
| 1199 |
+
logPatternWhen showInfo origin patterns
|
| 1200 |
+
return some {
|
| 1201 |
+
proof, patterns, numParams, symbols
|
| 1202 |
+
levelParams, origin, kind
|
| 1203 |
+
}
|
| 1204 |
+
|
| 1205 |
+
def mkEMatchTheoremForDecl (declName : Name) (thmKind : EMatchTheoremKind) (prios : SymbolPriorities) (showInfo := false) : MetaM EMatchTheorem := do
|
| 1206 |
+
let some thm β mkEMatchTheoremWithKind? (.decl declName) #[] (β getProofFor declName) thmKind prios (showInfo := showInfo)
|
| 1207 |
+
| throwError "`@{thmKind.toAttribute} theorem {declName}` {thmKind.explainFailure}, consider using different options or the `grind_pattern` command"
|
| 1208 |
+
return thm
|
| 1209 |
+
|
| 1210 |
+
def mkEMatchEqTheoremsForDef? (declName : Name) (showInfo := false) : MetaM (Option (Array EMatchTheorem)) := do
|
| 1211 |
+
let some eqns β getEqnsFor? declName | return none
|
| 1212 |
+
eqns.mapM fun eqn => do
|
| 1213 |
+
mkEMatchEqTheorem eqn (normalizePattern := true) (showInfo := showInfo)
|
| 1214 |
+
|
| 1215 |
+
private def addGrindEqAttr (declName : Name) (attrKind : AttributeKind) (thmKind : EMatchTheoremKind) (useLhs := true) (showInfo := false) : MetaM Unit := do
|
| 1216 |
+
if wasOriginallyTheorem (β getEnv) declName then
|
| 1217 |
+
ematchTheoremsExt.add (β mkEMatchEqTheorem declName (normalizePattern := true) (useLhs := useLhs) (gen := thmKind.gen) (showInfo := showInfo)) attrKind
|
| 1218 |
+
else if let some thms β mkEMatchEqTheoremsForDef? declName (showInfo := showInfo) then
|
| 1219 |
+
unless useLhs do
|
| 1220 |
+
throwError "`{declName}` is a definition, you must only use the left-hand side for extracting patterns"
|
| 1221 |
+
thms.forM (ematchTheoremsExt.add Β· attrKind)
|
| 1222 |
+
else
|
| 1223 |
+
throwError s!"`{thmKind.toAttribute}` attribute can only be applied to equational theorems or function definitions"
|
| 1224 |
+
|
| 1225 |
+
def EMatchTheorems.eraseDecl (s : EMatchTheorems) (declName : Name) : MetaM EMatchTheorems := do
|
| 1226 |
+
let throwErr {Ξ±} : MetaM Ξ± :=
|
| 1227 |
+
throwError "`{declName}` is not marked with the `[grind]` attribute"
|
| 1228 |
+
if !wasOriginallyTheorem (β getEnv) declName then
|
| 1229 |
+
if let some eqns β getEqnsFor? declName then
|
| 1230 |
+
let s := ematchTheoremsExt.getState (β getEnv)
|
| 1231 |
+
unless eqns.all fun eqn => s.contains (.decl eqn) do
|
| 1232 |
+
throwErr
|
| 1233 |
+
return eqns.foldl (init := s) fun s eqn => s.erase (.decl eqn)
|
| 1234 |
+
else
|
| 1235 |
+
throwErr
|
| 1236 |
+
else
|
| 1237 |
+
unless ematchTheoremsExt.getState (β getEnv) |>.contains (.decl declName) do
|
| 1238 |
+
throwErr
|
| 1239 |
+
return s.erase <| .decl declName
|
| 1240 |
+
|
| 1241 |
+
def addEMatchAttr (declName : Name) (attrKind : AttributeKind) (thmKind : EMatchTheoremKind) (prios : SymbolPriorities) (showInfo := false) : MetaM Unit := do
|
| 1242 |
+
match thmKind with
|
| 1243 |
+
| .eqLhs _ =>
|
| 1244 |
+
addGrindEqAttr declName attrKind thmKind (useLhs := true) (showInfo := showInfo)
|
| 1245 |
+
| .eqRhs _ =>
|
| 1246 |
+
addGrindEqAttr declName attrKind thmKind (useLhs := false) (showInfo := showInfo)
|
| 1247 |
+
| .eqBoth _ =>
|
| 1248 |
+
addGrindEqAttr declName attrKind thmKind (useLhs := true) (showInfo := showInfo)
|
| 1249 |
+
addGrindEqAttr declName attrKind thmKind (useLhs := false) (showInfo := showInfo)
|
| 1250 |
+
| _ =>
|
| 1251 |
+
let info β getConstInfo declName
|
| 1252 |
+
if !wasOriginallyTheorem (β getEnv) declName && !info.isCtor && !info.isAxiom then
|
| 1253 |
+
addGrindEqAttr declName attrKind thmKind (showInfo := showInfo)
|
| 1254 |
+
else
|
| 1255 |
+
let thm β mkEMatchTheoremForDecl declName thmKind prios (showInfo := showInfo)
|
| 1256 |
+
ematchTheoremsExt.add thm attrKind
|
| 1257 |
+
|
| 1258 |
+
def eraseEMatchAttr (declName : Name) : MetaM Unit := do
|
| 1259 |
+
/-
|
| 1260 |
+
Remark: consider the following example
|
| 1261 |
+
```
|
| 1262 |
+
attribute [grind] foo -- ok
|
| 1263 |
+
attribute [-grind] foo.eqn_2 -- ok
|
| 1264 |
+
attribute [-grind] foo -- error
|
| 1265 |
+
```
|
| 1266 |
+
One may argue that the correct behavior should be
|
| 1267 |
+
```
|
| 1268 |
+
attribute [grind] foo -- ok
|
| 1269 |
+
attribute [-grind] foo.eqn_2 -- error
|
| 1270 |
+
attribute [-grind] foo -- ok
|
| 1271 |
+
```
|
| 1272 |
+
-/
|
| 1273 |
+
let s := ematchTheoremsExt.getState (β getEnv)
|
| 1274 |
+
let s β s.eraseDecl declName
|
| 1275 |
+
modifyEnv fun env => ematchTheoremsExt.modifyState env fun _ => s
|
| 1276 |
+
|
| 1277 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EqResolution.lean
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.AppBuilder
|
| 8 |
+
import Lean.Meta.MatchUtil
|
| 9 |
+
import Lean.Util.ForEachExpr
|
| 10 |
+
|
| 11 |
+
namespace Lean.Meta.Grind
|
| 12 |
+
/-! A basic "equality resolution" procedure. -/
|
| 13 |
+
|
| 14 |
+
/--
|
| 15 |
+
Similar to `forallMetaTelescopeReducing`, but if `prop` resulting type is of the form `Β¬ p`, it will "convert" it so `p β False`, and
|
| 16 |
+
will return a hypothesis for it and return `False` as the resulting type.
|
| 17 |
+
-/
|
| 18 |
+
private def forallMetaTelescopeReducingAndUnfoldingNot (prop : Expr) : MetaM (Array Expr Γ Expr) := do
|
| 19 |
+
let (ms, _, type) β forallMetaTelescopeReducing prop
|
| 20 |
+
if let some p β matchNot? type then
|
| 21 |
+
let m β mkFreshExprMVar p
|
| 22 |
+
return (ms.push m, mkConst ``False)
|
| 23 |
+
return (ms, type)
|
| 24 |
+
|
| 25 |
+
structure TopSort.State where
|
| 26 |
+
tempMark : Std.HashSet Expr := {}
|
| 27 |
+
permMark : Std.HashSet Expr := {}
|
| 28 |
+
result : Array Expr := #[]
|
| 29 |
+
|
| 30 |
+
abbrev TopSortM := OptionT $ StateT TopSort.State MetaM
|
| 31 |
+
|
| 32 |
+
/--
|
| 33 |
+
Sorts metavariables `ms` using topological sort.
|
| 34 |
+
There is an "edge" from `mβ` to `mβ` if type of `mβ` contains `mβ`.
|
| 35 |
+
We use this function to ensure that after applying equality resolution to
|
| 36 |
+
```
|
| 37 |
+
β x : Nat, p x a β β y : Nat, p y b β x = y β False
|
| 38 |
+
```
|
| 39 |
+
we produce
|
| 40 |
+
```
|
| 41 |
+
β y, p y a β p y b β False
|
| 42 |
+
```
|
| 43 |
+
instead of
|
| 44 |
+
```
|
| 45 |
+
p ?y a β β y, p y b β False
|
| 46 |
+
```
|
| 47 |
+
Recall that in equality resolution we create a meta-variable for each hypothesis.
|
| 48 |
+
Thus, we initially have
|
| 49 |
+
```
|
| 50 |
+
?x : Nat, ?hβ : p ?x a, ?y : Nat, ?hβ : p ?y b, ?hβ : ?x = ?y
|
| 51 |
+
```
|
| 52 |
+
Then, we resolve `?hβ : ?x = ?y` as `?y := ?x` and `?hβ := Eq.refl ?y`.
|
| 53 |
+
But `?hβ` occurs before `?y`. We use topological sort to address this situation.
|
| 54 |
+
If a cycle is detected, it returns `none`.
|
| 55 |
+
-/
|
| 56 |
+
private partial def topsortMVars? (ms : Array Expr) : MetaM (Option (Array Expr)) := do
|
| 57 |
+
let (some _, s) β go.run.run {} | return none
|
| 58 |
+
return some s.result
|
| 59 |
+
where
|
| 60 |
+
go : TopSortM Unit := do
|
| 61 |
+
for m in ms do
|
| 62 |
+
visit m
|
| 63 |
+
|
| 64 |
+
visit (m : Expr) : TopSortM Unit := do
|
| 65 |
+
if (β get).permMark.contains m then
|
| 66 |
+
return ()
|
| 67 |
+
if (β get).tempMark.contains m then
|
| 68 |
+
failure
|
| 69 |
+
modify fun s => { s with tempMark := s.tempMark.insert m }
|
| 70 |
+
visitTypeOf m
|
| 71 |
+
modify fun s => { s with
|
| 72 |
+
result := s.result.push m
|
| 73 |
+
permMark := s.permMark.insert m
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
visitTypeOf (m : Expr) : TopSortM Unit := do
|
| 77 |
+
let type β instantiateMVars (β inferType m)
|
| 78 |
+
type.forEach' fun e => do
|
| 79 |
+
if e.hasExprMVar then
|
| 80 |
+
if e.isMVar && ms.contains e then
|
| 81 |
+
visit e
|
| 82 |
+
return true
|
| 83 |
+
else
|
| 84 |
+
return false
|
| 85 |
+
|
| 86 |
+
private def eqResCore (prop proof : Expr) : MetaM (Option (Expr Γ Expr)) := withNewMCtxDepth do
|
| 87 |
+
/-
|
| 88 |
+
We use `forallMetaTelescopeReducingAndUnfoldingNot` because we want to treat
|
| 89 |
+
```
|
| 90 |
+
β x, Β¬ f x = f a
|
| 91 |
+
```
|
| 92 |
+
as
|
| 93 |
+
```
|
| 94 |
+
β x, f x = f a β False
|
| 95 |
+
```
|
| 96 |
+
recall that `Β¬` is not reducible.
|
| 97 |
+
-/
|
| 98 |
+
let (ms, type) β forallMetaTelescopeReducingAndUnfoldingNot prop
|
| 99 |
+
if ms.isEmpty then return none
|
| 100 |
+
let mut progress := false
|
| 101 |
+
for m in ms do
|
| 102 |
+
let type β inferType m
|
| 103 |
+
let_expr Eq _ lhs rhs β type
|
| 104 |
+
| pure ()
|
| 105 |
+
if (β isDefEq lhs rhs) then
|
| 106 |
+
unless (β m.mvarId!.checkedAssign (β mkEqRefl lhs)) do
|
| 107 |
+
return none
|
| 108 |
+
progress := true
|
| 109 |
+
unless progress do
|
| 110 |
+
return none
|
| 111 |
+
if (β ms.anyM fun m => m.mvarId!.isDelayedAssigned) then
|
| 112 |
+
return none
|
| 113 |
+
let prop' β instantiateMVars type
|
| 114 |
+
let proof' β instantiateMVars (mkAppN proof ms)
|
| 115 |
+
let ms β ms.filterM fun m => return !(β m.mvarId!.isAssigned)
|
| 116 |
+
let some ms β topsortMVars? ms | return none
|
| 117 |
+
let prop' β mkForallFVars ms prop' (binderInfoForMVars := .default)
|
| 118 |
+
let proof' β mkLambdaFVars ms proof'
|
| 119 |
+
return some (prop', proof')
|
| 120 |
+
|
| 121 |
+
/--
|
| 122 |
+
A basic "equality resolution" procedure: Given a proposition `prop` with a proof `proof`, it attempts to resolve equality hypotheses using `isDefEq`. For example, it reduces `β x y, f x = f (g y y) β g x y = y` to `β y, g (g y y) y = y`, and `β (x : Nat), f x β f a` to `False`.
|
| 123 |
+
If successful, the result is a pair `(prop', proof)`, where `prop'` is the simplified proposition,
|
| 124 |
+
and `proof : prop β prop'`
|
| 125 |
+
-/
|
| 126 |
+
def eqResolution (prop : Expr) : MetaM (Option (Expr Γ Expr)) :=
|
| 127 |
+
withLocalDeclD `h prop fun h => do
|
| 128 |
+
let some (prop', proof') β eqResCore prop h
|
| 129 |
+
| return none
|
| 130 |
+
let proof' β mkLambdaFVars #[h] proof'
|
| 131 |
+
return some (prop', proof')
|
| 132 |
+
|
| 133 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ExprPtr.lean
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Expr
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.Grind
|
| 10 |
+
|
| 11 |
+
@[inline] def isSameExpr (a b : Expr) : Bool :=
|
| 12 |
+
-- It is safe to use pointer equality because we hashcons all expressions
|
| 13 |
+
-- inserted into the E-graph
|
| 14 |
+
unsafe ptrEq a b
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
Key for the `ENodeMap`, `ParentMap`, and other maps and sets.
|
| 18 |
+
We use pointer addresses and rely on the fact all internalized expressions
|
| 19 |
+
have been hash-consed, i.e., we have applied `shareCommon`.
|
| 20 |
+
-/
|
| 21 |
+
structure ExprPtr where
|
| 22 |
+
expr : Expr
|
| 23 |
+
|
| 24 |
+
abbrev hashPtrExpr (e : Expr) : UInt64 :=
|
| 25 |
+
unsafe (ptrAddrUnsafe e >>> 3).toUInt64
|
| 26 |
+
|
| 27 |
+
instance : Hashable ExprPtr where
|
| 28 |
+
hash k := hashPtrExpr k.expr
|
| 29 |
+
|
| 30 |
+
instance : BEq ExprPtr where
|
| 31 |
+
beq kβ kβ := isSameExpr kβ.expr kβ.expr
|
| 32 |
+
|
| 33 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Ext.lean
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.Grind
|
| 10 |
+
/-! Extensionality theorems support. -/
|
| 11 |
+
|
| 12 |
+
def instantiateExtTheorem (thm : Ext.ExtTheorem) (e : Expr) : GoalM Unit := withNewMCtxDepth do
|
| 13 |
+
unless (β getGeneration e) < (β getMaxGeneration) do return ()
|
| 14 |
+
let c β mkConstWithFreshMVarLevels thm.declName
|
| 15 |
+
let (mvars, bis, type) β withDefault <| forallMetaTelescopeReducing (β inferType c)
|
| 16 |
+
unless (β isDefEq e type) do
|
| 17 |
+
reportIssue! "failed to apply extensionality theorem `{thm.declName}` for {indentExpr e}\nis not definitionally equal to{indentExpr type}"
|
| 18 |
+
return ()
|
| 19 |
+
-- Instantiate type class instances
|
| 20 |
+
for mvar in mvars, bi in bis do
|
| 21 |
+
if bi.isInstImplicit && !(β mvar.mvarId!.isAssigned) then
|
| 22 |
+
let type β inferType mvar
|
| 23 |
+
unless (β synthesizeInstanceAndAssign mvar type) do
|
| 24 |
+
reportIssue! "failed to synthesize instance when instantiating extensionality theorem `{thm.declName}` for {indentExpr e}"
|
| 25 |
+
return ()
|
| 26 |
+
-- Remark: `proof c mvars` has type `e`
|
| 27 |
+
let proof β instantiateMVars (mkAppN c mvars)
|
| 28 |
+
-- `e` is equal to `False`
|
| 29 |
+
let eEqFalse β mkEqFalseProof e
|
| 30 |
+
-- So, we use `Eq.mp` to build a `proof` of `False`
|
| 31 |
+
let proof := mkApp4 (mkConst ``Eq.mp [levelZero]) e (β getFalseExpr) eEqFalse proof
|
| 32 |
+
let mvars β mvars.filterM fun mvar => return !(β mvar.mvarId!.isAssigned)
|
| 33 |
+
let proof' β instantiateMVars (β mkLambdaFVars mvars proof)
|
| 34 |
+
let prop' β inferType proof'
|
| 35 |
+
if proof'.hasMVar || prop'.hasMVar then
|
| 36 |
+
reportIssue! "failed to apply extensionality theorem `{thm.declName}` for {indentExpr e}\nresulting terms contain metavariables"
|
| 37 |
+
return ()
|
| 38 |
+
trace[grind.ext] "{thm.declName}: {prop'}"
|
| 39 |
+
addNewRawFact proof' prop' ((β getGeneration e) + 1) (.ext thm.declName)
|
| 40 |
+
|
| 41 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ExtAttr.lean
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Tactic.Ext
|
| 8 |
+
|
| 9 |
+
namespace Lean.Meta.Grind
|
| 10 |
+
/-! Grind extensionality attribute to mark which `[ext]` theorems should be used. -/
|
| 11 |
+
|
| 12 |
+
/-- Extensionality theorems that can be used by `grind` -/
|
| 13 |
+
abbrev ExtTheorems := PHashSet Name
|
| 14 |
+
|
| 15 |
+
builtin_initialize extTheoremsExt : SimpleScopedEnvExtension Name ExtTheorems β
|
| 16 |
+
registerSimpleScopedEnvExtension {
|
| 17 |
+
initial := {}
|
| 18 |
+
addEntry := fun s declName => s.insert declName
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
def validateExtAttr (declName : Name) : CoreM Unit := do
|
| 22 |
+
if !(β Ext.isExtTheorem declName) then
|
| 23 |
+
if !(isStructure (β getEnv) declName) then
|
| 24 |
+
throwError "invalid `[grind ext]`, `{declName}` is neither tagged with `[ext]` nor is a structure"
|
| 25 |
+
|
| 26 |
+
def addExtAttr (declName : Name) (attrKind : AttributeKind) : CoreM Unit := do
|
| 27 |
+
validateExtAttr declName
|
| 28 |
+
extTheoremsExt.add declName attrKind
|
| 29 |
+
|
| 30 |
+
private def eraseDecl (s : ExtTheorems) (declName : Name) : CoreM ExtTheorems := do
|
| 31 |
+
if s.contains declName then
|
| 32 |
+
return s.erase declName
|
| 33 |
+
else
|
| 34 |
+
throwError "`{declName}` is not marked with the `[grind ext]` attribute"
|
| 35 |
+
|
| 36 |
+
def eraseExtAttr (declName : Name) : CoreM Unit := do
|
| 37 |
+
let s := extTheoremsExt.getState (β getEnv)
|
| 38 |
+
let s β eraseDecl s declName
|
| 39 |
+
modifyEnv fun env => extTheoremsExt.modifyState env fun _ => s
|
| 40 |
+
|
| 41 |
+
def isExtTheorem (declName : Name) : CoreM Bool := do
|
| 42 |
+
return extTheoremsExt.getState (β getEnv) |>.contains declName
|
| 43 |
+
|
| 44 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ForallProp.lean
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Grind.Lemmas
|
| 8 |
+
import Lean.Meta.Tactic.Grind.Types
|
| 9 |
+
import Lean.Meta.Tactic.Grind.Internalize
|
| 10 |
+
import Lean.Meta.Tactic.Grind.Simp
|
| 11 |
+
import Lean.Meta.Tactic.Grind.EqResolution
|
| 12 |
+
|
| 13 |
+
namespace Lean.Meta.Grind
|
| 14 |
+
/--
|
| 15 |
+
If `parent` is a projection-application `proj_i c`,
|
| 16 |
+
check whether the root of the equivalence class containing `c` is a constructor-application `ctor ... a_i ...`.
|
| 17 |
+
If so, internalize the term `proj_i (ctor ... a_i ...)` and add the equality `proj_i (ctor ... a_i ...) = a_i`.
|
| 18 |
+
-/
|
| 19 |
+
def propagateForallPropUp (e : Expr) : GoalM Unit := do
|
| 20 |
+
let .forallE n p q bi := e | return ()
|
| 21 |
+
trace_goal[grind.debug.forallPropagator] "{e}"
|
| 22 |
+
if !q.hasLooseBVars then
|
| 23 |
+
propagateImpliesUp p q
|
| 24 |
+
else
|
| 25 |
+
unless (β isEqTrue p) do return
|
| 26 |
+
trace_goal[grind.debug.forallPropagator] "isEqTrue, {e}"
|
| 27 |
+
let hβ β mkEqTrueProof p
|
| 28 |
+
let qhβ := q.instantiate1 (mkOfEqTrueCore p hβ)
|
| 29 |
+
let r β preprocess qhβ
|
| 30 |
+
let q := mkLambda n bi p q
|
| 31 |
+
let q' := r.expr
|
| 32 |
+
internalize q' (β getGeneration e)
|
| 33 |
+
trace_goal[grind.debug.forallPropagator] "q': {q'} for{indentExpr e}"
|
| 34 |
+
let hβ β r.getProof
|
| 35 |
+
let h := mkApp5 (mkConst ``Lean.Grind.forall_propagator) p q q' hβ hβ
|
| 36 |
+
pushEq e q' h
|
| 37 |
+
where
|
| 38 |
+
propagateImpliesUp (a b : Expr) : GoalM Unit := do
|
| 39 |
+
unless (β alreadyInternalized b) do return ()
|
| 40 |
+
if (β isEqFalse a <&&> isProp b) then
|
| 41 |
+
-- a = False β (a β b) = True
|
| 42 |
+
pushEqTrue e <| mkApp3 (mkConst ``Grind.imp_eq_of_eq_false_left) a b (β mkEqFalseProof a)
|
| 43 |
+
else if (β isEqTrue a <&&> isProp b) then
|
| 44 |
+
-- a = True β (a β b) = b
|
| 45 |
+
pushEq e b <| mkApp3 (mkConst ``Grind.imp_eq_of_eq_true_left) a b (β mkEqTrueProof a)
|
| 46 |
+
else if (β isEqTrue b <&&> isProp a) then
|
| 47 |
+
-- b = True β (a β b) = True
|
| 48 |
+
pushEqTrue e <| mkApp3 (mkConst ``Grind.imp_eq_of_eq_true_right) a b (β mkEqTrueProof b)
|
| 49 |
+
else if (β isEqFalse b <&&> isEqTrue e <&&> isProp a) then
|
| 50 |
+
-- (a β b) = True β b = False β a = False
|
| 51 |
+
pushEqFalse a <| mkApp4 (mkConst ``Grind.eq_false_of_imp_eq_true) a b (β mkEqTrueProof e) (β mkEqFalseProof b)
|
| 52 |
+
|
| 53 |
+
private def isEqTrueHyp? (proof : Expr) : Option FVarId := Id.run do
|
| 54 |
+
let_expr eq_true _ p := proof | return none
|
| 55 |
+
let .fvar fvarId := p | return none
|
| 56 |
+
return some fvarId
|
| 57 |
+
|
| 58 |
+
/-- Similar to `mkEMatchTheoremWithKind?`, but swallow any exceptions. -/
|
| 59 |
+
private def mkEMatchTheoremWithKind'? (origin : Origin) (proof : Expr) (kind : EMatchTheoremKind) (prios : SymbolPriorities) : MetaM (Option EMatchTheorem) := do
|
| 60 |
+
try
|
| 61 |
+
mkEMatchTheoremWithKind? origin #[] proof kind prios (groundPatterns := false)
|
| 62 |
+
catch _ =>
|
| 63 |
+
return none
|
| 64 |
+
|
| 65 |
+
private def addLocalEMatchTheorems (e : Expr) : GoalM Unit := do
|
| 66 |
+
let proof β mkEqTrueProof e
|
| 67 |
+
let origin β if let some fvarId := isEqTrueHyp? proof then
|
| 68 |
+
pure <| .fvar fvarId
|
| 69 |
+
else
|
| 70 |
+
let idx β modifyGet fun s => (s.ematch.nextThmIdx, { s with ematch.nextThmIdx := s.ematch.nextThmIdx + 1 })
|
| 71 |
+
pure <| .local ((`local).appendIndexAfter idx)
|
| 72 |
+
let proof := mkOfEqTrueCore e proof
|
| 73 |
+
let size := (β get).ematch.newThms.size
|
| 74 |
+
let gen β getGeneration e
|
| 75 |
+
-- TODO: we should have a flag for collecting all unary patterns in a local theorem
|
| 76 |
+
if let some thm β mkEMatchTheoremWithKind'? origin proof .leftRight (β getSymbolPriorities) then
|
| 77 |
+
activateTheorem thm gen
|
| 78 |
+
if let some thm β mkEMatchTheoremWithKind'? origin proof .rightLeft (β getSymbolPriorities) then
|
| 79 |
+
activateTheorem thm gen
|
| 80 |
+
if (β get).ematch.newThms.size == size then
|
| 81 |
+
if let some thm β mkEMatchTheoremWithKind'? origin proof (.default false) (β getSymbolPriorities) then
|
| 82 |
+
activateTheorem thm gen
|
| 83 |
+
if (β get).ematch.newThms.size == size then
|
| 84 |
+
reportIssue! "failed to create E-match local theorem for{indentExpr e}"
|
| 85 |
+
|
| 86 |
+
def propagateForallPropDown (e : Expr) : GoalM Unit := do
|
| 87 |
+
let .forallE n a b bi := e | return ()
|
| 88 |
+
if (β isEqFalse e) then
|
| 89 |
+
if b.hasLooseBVars || !(β isProp a) then
|
| 90 |
+
let Ξ± := a
|
| 91 |
+
let p := b
|
| 92 |
+
-- `e` is of the form `β x : Ξ±, p x`
|
| 93 |
+
-- Add fact `β x : Ξ±, Β¬ p x`
|
| 94 |
+
let u β getLevel Ξ±
|
| 95 |
+
let prop := mkApp2 (mkConst ``Exists [u]) Ξ± (mkLambda n bi Ξ± (mkNot p))
|
| 96 |
+
let proof := mkApp3 (mkConst ``Grind.of_forall_eq_false [u]) Ξ± (mkLambda n bi Ξ± p) (β mkEqFalseProof e)
|
| 97 |
+
addNewRawFact proof prop (β getGeneration e) (.forallProp e)
|
| 98 |
+
else
|
| 99 |
+
let h β mkEqFalseProof e
|
| 100 |
+
pushEqTrue a <| mkApp3 (mkConst ``Grind.eq_true_of_imp_eq_false) a b h
|
| 101 |
+
pushEqFalse b <| mkApp3 (mkConst ``Grind.eq_false_of_imp_eq_false) a b h
|
| 102 |
+
else if (β isEqTrue e) then
|
| 103 |
+
if let some (e', h') β eqResolution e then
|
| 104 |
+
trace_goal[grind.eqResolution] "{e}, {e'}"
|
| 105 |
+
let h := mkOfEqTrueCore e (β mkEqTrueProof e)
|
| 106 |
+
let h' := mkApp h' h
|
| 107 |
+
addNewRawFact h' e' (β getGeneration e) (.forallProp e)
|
| 108 |
+
else
|
| 109 |
+
if b.hasLooseBVars then
|
| 110 |
+
addLocalEMatchTheorems e
|
| 111 |
+
else
|
| 112 |
+
unless (β alreadyInternalized b) do return ()
|
| 113 |
+
if (β isEqFalse b <&&> isProp a) then
|
| 114 |
+
-- (a β b) = True β b = False β a = False
|
| 115 |
+
pushEqFalse a <| mkApp4 (mkConst ``Grind.eq_false_of_imp_eq_true) a b (β mkEqTrueProof e) (β mkEqFalseProof b)
|
| 116 |
+
|
| 117 |
+
builtin_grind_propagator propagateExistsDown βExists := fun e => do
|
| 118 |
+
if (β isEqFalse e) then
|
| 119 |
+
let_expr f@Exists Ξ± p := e | return ()
|
| 120 |
+
let u := f.constLevels!
|
| 121 |
+
let notP := mkApp (mkConst ``Not) (mkApp p (.bvar 0) |>.headBeta)
|
| 122 |
+
let prop := mkForall `x .default Ξ± notP
|
| 123 |
+
let proof := mkApp3 (mkConst ``forall_not_of_not_exists u) Ξ± p (mkOfEqFalseCore e (β mkEqFalseProof e))
|
| 124 |
+
addNewRawFact proof prop (β getGeneration e) (.existsProp e)
|
| 125 |
+
|
| 126 |
+
end Lean.Meta.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Injection.lean
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.CtorRecognizer
|
| 8 |
+
import Lean.Meta.Tactic.Util
|
| 9 |
+
import Lean.Meta.Tactic.Clear
|
| 10 |
+
|
| 11 |
+
namespace Lean.Meta.Grind
|
| 12 |
+
/--
|
| 13 |
+
The `grind` tactic includes an auxiliary `injection?` tactic that is not intended for direct use by users.
|
| 14 |
+
This tactic is automatically applied when introducing non-dependent propositions.
|
| 15 |
+
It differs from the user-facing Lean `injection` tactic in the following ways:
|
| 16 |
+
|
| 17 |
+
- It does not introduce new propositions. Instead, the `grind` tactic preprocessor is responsible for introducing them.
|
| 18 |
+
- It assumes that `fvarId` is the latest local declaration in the current goal.
|
| 19 |
+
- It does not handle cases where the constructors are different because the simplifier already reduces such equalities to `False`.
|
| 20 |
+
- It does not have support for heterogeneous equality. Recall that the simplifier already reduces them to `Eq` if
|
| 21 |
+
the types are definitionally equal.
|
| 22 |
+
-/
|
| 23 |
+
def injection? (mvarId : MVarId) (fvarId : FVarId) : MetaM (Option MVarId) := mvarId.withContext do
|
| 24 |
+
let decl β fvarId.getDecl
|
| 25 |
+
let_expr Eq _ a b := decl.type | return none
|
| 26 |
+
match (β isConstructorAppCore? a), (β isConstructorAppCore? b) with
|
| 27 |
+
| some aCtor, some bCtor =>
|
| 28 |
+
if aCtor.name != bCtor.name then return none -- Simplifier handles this case
|
| 29 |
+
let target β mvarId.getType
|
| 30 |
+
let val β mkNoConfusion target (mkFVar fvarId)
|
| 31 |
+
let .forallE _ targetNew _ _ β whnfD (β inferType val) | return none
|
| 32 |
+
let targetNew := targetNew.headBeta
|
| 33 |
+
let tag β mvarId.getTag
|
| 34 |
+
let mvarNew β mkFreshExprSyntheticOpaqueMVar targetNew tag
|
| 35 |
+
mvarId.assign (mkApp val mvarNew)
|
| 36 |
+
return some (β mvarNew.mvarId!.clear fvarId)
|
| 37 |
+
| _, _ => return none
|
| 38 |
+
|
| 39 |
+
end Lean.Meta.Grind
|