@@ -53,8 +53,24 @@ initialize librarySearchLemmas : DeclCache (DiscrTree Name) ←
53
53
/-- Shortcut for calling `solveByElimImpl`. -/
54
54
def solveByElim (g : MVarId) (depth) := Lean.Tactic.solveByElimImpl false [] depth g
55
55
56
- def librarySearch (goal : MVarId) (lemmas : DiscrTree Name) (solveByElimDepth := 6 ) :
57
- MetaM <| Option (Array <| MetavarContext × List MVarId) := do
56
+ /--
57
+ Try to solve the goal either by:
58
+ * calling `solveByElim`
59
+ * or applying a library lemma then calling `solveByElim` on the resulting goals.
60
+
61
+ If it successfully closes the goal, returns `none`.
62
+ Otherwise, it returns `some a`, where `a : Array (MetavarContext × List MVarId)`,
63
+ with an entry for each library lemma which was successfully applied,
64
+ containing the metavariable context after the application, and a list of the subsidiary goals.
65
+
66
+ (Always succeeds, and the metavariable context stored in the monad is reverted,
67
+ unless the goal was completely solved.)
68
+
69
+ (Note that if `solveByElim` solves some but not all subsidiary goals,
70
+ this is not currently tracked.)
71
+ -/
72
+ def librarySearch (goal : MVarId) (lemmas : DiscrTree Name) (required : List Expr)
73
+ (solveByElimDepth := 6 ) : MetaM <| Option (Array <| MetavarContext × List MVarId) := do
58
74
profileitM Exception "librarySearch" (← getOptions) do
59
75
let ty ← goal.getType
60
76
withTraceNode `Tactic.librarySearch (return m!"{exceptOptionEmoji ·} {ty}" ) do
@@ -65,7 +81,10 @@ def librarySearch (goal : MVarId) (lemmas : DiscrTree Name) (solveByElimDepth :=
65
81
66
82
try
67
83
solveByElim goal solveByElimDepth
68
- return none
84
+ if (← checkRequired) then
85
+ return none
86
+ else
87
+ set state0
69
88
catch _ =>
70
89
set state0
71
90
@@ -79,11 +98,16 @@ def librarySearch (goal : MVarId) (lemmas : DiscrTree Name) (solveByElimDepth :=
79
98
newGoal.withContext do
80
99
trace[Tactic.librarySearch] "proving {← addMessageContextFull (mkMVar newGoal)}"
81
100
solveByElim newGoal solveByElimDepth
82
- pure $ some $ Sum.inr ()
101
+ if (← checkRequired) then
102
+ pure $ some $ Sum.inr ()
103
+ else
104
+ set state0
105
+ pure none
83
106
catch _ =>
84
107
let res := some $ Sum.inl (← getMCtx, newGoals)
108
+ let check ← checkRequired
85
109
set state0
86
- pure res)
110
+ return if check then res else none )
87
111
catch _ =>
88
112
set state0
89
113
pure none
@@ -93,6 +117,10 @@ def librarySearch (goal : MVarId) (lemmas : DiscrTree Name) (solveByElimDepth :=
93
117
| some (Sum.inl suggestion) => suggestions := suggestions.push suggestion
94
118
95
119
pure $ some suggestions
120
+ where
121
+ /-- Verify that the instantiated goal contains each `Expr` in `required` as a sub-expression.
122
+ (Make sure to not reset the state before calling.) -/
123
+ checkRequired : MetaM Bool := return required.all (·.occurs (← instantiateMVars (.mvar goal)))
96
124
97
125
def lines (ls : List MessageData) :=
98
126
MessageData.joinSep ls (MessageData.ofFormat Format.line)
@@ -101,22 +129,22 @@ open Lean.Parser.Tactic
101
129
102
130
-- TODO: implement the additional options for `library_search` from Lean 3,
103
131
-- in particular including additional lemmas
104
- -- with `library_search [X, Y, Z]` or `library_search with attr`,
105
- -- or requiring that a particular hypothesis is used in the solution, with `library_search using h`.
132
+ -- with `library_search [X, Y, Z]` or `library_search with attr`.
106
133
syntax (name := librarySearch') "library_search" (config)? (simpArgs)?
107
- (" using " (colGt binderIdent) +)? : tactic
134
+ (" using " (colGt term), +)? : tactic
108
135
syntax (name := librarySearch!) "library_search!" (config)? (simpArgs)?
109
- (" using " (colGt binderIdent) +)? : tactic
136
+ (" using " (colGt term), +)? : tactic
110
137
111
138
-- For now we only implement the basic functionality.
112
139
-- The full syntax is recognized, but will produce a "Tactic has not been implemented" error.
113
140
114
141
open Elab.Tactic Elab Tactic in
115
- elab_rules : tactic | `(tactic| library_search%$tk) => do
142
+ elab_rules : tactic | `(tactic| library_search%$tk $[using $[$required:term],*]? ) => do
116
143
let mvar ← getMainGoal
117
144
let (_, goal) ← (← getMainGoal).intros
118
145
goal.withContext do
119
- if let some suggestions ← librarySearch goal (← librarySearchLemmas.get) then
146
+ let required := (← (required.getD #[]).mapM getFVarId).toList.map .fvar
147
+ if let some suggestions ← librarySearch goal (← librarySearchLemmas.get) required then
120
148
for suggestion in suggestions do
121
149
withMCtx suggestion.1 do
122
150
addExactSuggestion tk (← instantiateMVars (mkMVar mvar))
@@ -129,7 +157,7 @@ elab tk:"library_search%" : term <= expectedType => do
129
157
let goal ← mkFreshExprMVar expectedType
130
158
let (_, introdGoal) ← goal.mvarId!.intros
131
159
introdGoal.withContext do
132
- if let some suggestions ← librarySearch introdGoal (← librarySearchLemmas.get) then
160
+ if let some suggestions ← librarySearch introdGoal (← librarySearchLemmas.get) [] then
133
161
for suggestion in suggestions do
134
162
withMCtx suggestion.1 do
135
163
addTermSuggestion tk (← instantiateMVars goal)
0 commit comments