ZAIDX11 commited on
Commit
9a7b1f5
Β·
verified Β·
1 Parent(s): 19faf57

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .gitattributes +3 -0
  2. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/CheckTactic.lean +24 -0
  3. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MVarRenaming.lean +36 -0
  4. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatchPatternAttr.lean +37 -0
  5. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherApp/Basic.lean +80 -0
  6. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherApp/Transform.lean +443 -0
  7. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/MatcherInfo.lean +134 -0
  8. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Match/Value.lean +27 -0
  9. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AC.lean +7 -0
  10. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AC/Main.lean +242 -0
  11. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Acyclic.lean +60 -0
  12. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Apply.lean +377 -0
  13. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Assert.lean +145 -0
  14. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Assumption.lean +34 -0
  15. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/AuxLemma.lean +62 -0
  16. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Backtrack.lean +205 -0
  17. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Cases.lean +361 -0
  18. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Cleanup.lean +75 -0
  19. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Clear.lean +73 -0
  20. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Congr.lean +118 -0
  21. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Constructor.lean +49 -0
  22. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Contradiction.lean +232 -0
  23. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Delta.lean +43 -0
  24. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/ElimInfo.lean +281 -0
  25. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/ExposeNames.lean +66 -0
  26. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Ext.lean +85 -0
  27. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FVarSubst.lean +84 -0
  28. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunInd.lean +1715 -0
  29. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunIndCollect.lean +115 -0
  30. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/FunIndInfo.lean +86 -0
  31. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Generalize.lean +152 -0
  32. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind.lean +89 -0
  33. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/AlphaShareCommon.lean +112 -0
  34. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Arith.lean +15 -0
  35. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Attr.lean +125 -0
  36. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Beta.lean +77 -0
  37. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Canon.lean +211 -0
  38. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Cases.lean +191 -0
  39. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/CasesMatch.lean +82 -0
  40. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Core.lean +431 -0
  41. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Ctor.lean +60 -0
  42. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Diseq.lean +86 -0
  43. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EMatch.lean +577 -0
  44. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EMatchTheorem.lean +1277 -0
  45. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/EqResolution.lean +133 -0
  46. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ExprPtr.lean +33 -0
  47. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/Ext.lean +41 -0
  48. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ExtAttr.lean +44 -0
  49. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Meta/Tactic/Grind/ForallProp.lean +126 -0
  50. 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