@@ -21,9 +21,8 @@ import Mathlib.Control.Basic
21
21
Suggestions are printed as `rw [h]` or `rw [←h]`.
22
22
23
23
## Future work
24
- It would be nice to have `rw? at h`.
25
24
26
- We could also try discharging side goals via `assumption` or `solve_by_elim`.
25
+ We could try discharging side goals via `assumption` or `solve_by_elim`.
27
26
28
27
-/
29
28
@@ -135,13 +134,11 @@ This core function returns a monadic list, to allow the caller to decide how lon
135
134
See also `rewrites` for a more convenient interface.
136
135
-/
137
136
def rewritesCore (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name × Bool × Nat) s)
138
- (goal : MVarId) : ListM MetaM RewriteResult := ListM.squash do
139
- let type ← instantiateMVars (← goal.getType)
137
+ (goal : MVarId) (target : Expr) : ListM MetaM RewriteResult := ListM.squash do
140
138
141
139
-- Get all lemmas which could match some subexpression
142
- -- DiscrTree.getSubexpressionMatches
143
- let candidates := (← lemmas.1 .getSubexpressionMatches type)
144
- ++ (← lemmas.2 .getSubexpressionMatches type)
140
+ let candidates := (← lemmas.1 .getSubexpressionMatches target)
141
+ ++ (← lemmas.2 .getSubexpressionMatches target)
145
142
146
143
-- Sort them by our preferring weighting
147
144
-- (length of discriminant key, doubled for the forward implication)
@@ -154,7 +151,7 @@ def rewritesCore (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name
154
151
let candidates := ListM.ofList candidates.toList
155
152
pure <| candidates.filterMapM fun ⟨lem, symm, weight⟩ => do
156
153
trace[Tactic.rewrites] "considering {if symm then " ←" else ""}{lem}"
157
- let some result ← try ? do goal.rewrite type (← mkConstWithFreshMVarLevels lem) symm
154
+ let some result ← try ? do goal.rewrite target (← mkConstWithFreshMVarLevels lem) symm
158
155
| return none
159
156
return if result.mvarIds.isEmpty then
160
157
some ⟨lem, symm, weight, result, none⟩
@@ -164,17 +161,17 @@ def rewritesCore (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name
164
161
165
162
/-- Find lemmas which can rewrite the goal. -/
166
163
def rewrites (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name × Bool × Nat) s)
167
- (goal : MVarId) (max : Nat := 20 ) (leavePercentHeartbeats : Nat := 10 ) :
168
- MetaM (List RewriteResult) := do
169
- let results ← rewritesCore lemmas goal
164
+ (goal : MVarId) (target : Expr) (stop_at_rfl : Bool := False ) (max : Nat := 20 )
165
+ (leavePercentHeartbeats : Nat := 10 ) : MetaM (List RewriteResult) := do
166
+ let results ← rewritesCore lemmas goal target
170
167
-- Don't use too many heartbeats.
171
168
|>.whileAtLeastHeartbeatsPercent leavePercentHeartbeats
172
169
-- Stop if we find a rewrite after which `with_reducible rfl` would succeed.
173
- |>.mapM RewriteResult.computeRfl
174
- |>.takeUpToFirst (fun r => r.rfl? = some true )
170
+ |>.mapM RewriteResult.computeRfl -- TODO could simply not compute this if stop_at_rfl is False
171
+ |>.takeUpToFirst (fun r => stop_at_rfl && r.rfl? = some true )
175
172
-- Bound the number of results.
176
173
|>.takeAsList max
177
- return match results.filter (fun r => r.rfl? = some true ) with
174
+ return match results.filter (fun r => stop_at_rfl && r.rfl? = some true ) with
178
175
| [] =>
179
176
-- TODO consider sorting the results,
180
177
-- e.g. if we use solveByElim to fill arguments,
@@ -192,27 +189,52 @@ open Lean.Parser.Tactic
192
189
Suggestions are printed as `rw [h]` or `rw [←h]`.
193
190
`rw?!` is the "I'm feeling lucky" mode, and will run the first rewrite it finds.
194
191
-/
195
- syntax (name := rewrites') "rw?" "!" ? : tactic
192
+ syntax (name := rewrites') "rw?" "!" ? (ppSpace location)? : tactic
196
193
197
194
open Elab.Tactic Elab Tactic in
198
195
elab_rules : tactic |
199
- `(tactic| rw?%$tk $[!%$lucky]?) => do
196
+ `(tactic| rw?%$tk $[!%$lucky]? $[$loc]?) => do
197
+ let lems ← rewriteLemmas.get
198
+ reportOutOfHeartbeats `rewrites tk
200
199
let goal ← getMainGoal
201
- goal.withContext do
202
- let results ← rewrites (← rewriteLemmas.get) goal
203
- reportOutOfHeartbeats `rewrites tk
204
- if results.isEmpty then
205
- throwError "rw? could not find any lemmas which can rewrite the goal"
206
- for r in results do
207
- let newGoal := if r.rfl? = some true then Expr.lit (.strVal "no goals" ) else r.result.eNew
208
- addRewriteSuggestion tk (← mkConstWithFreshMVarLevels r.name) r.symm newGoal
209
- if lucky.isSome then
210
- match results[0 ]? with
211
- | some r => do
212
- replaceMainGoal
213
- ((← goal.replaceTargetEq r.result.eNew r.result.eqProof) :: r.result.mvarIds)
214
- evalTactic (← `(tactic| rfl))
215
- | _ => failure
216
-
217
- @[inherit_doc rewrites'] macro "rw?!" : tactic =>
218
- `(tactic| rw? !)
200
+ -- TODO fix doc of core to say that * fails only if all failed
201
+ withLocation (expandOptLocation (Lean.mkOptionalNode loc))
202
+ fun f => do
203
+ let some a ← f.findDecl? | return
204
+ if a.isImplementationDetail then return
205
+ let target ← instantiateMVars (← f.getType)
206
+ let results ← rewrites lems goal target (stop_at_rfl := false )
207
+ reportOutOfHeartbeats `rewrites tk
208
+ if results.isEmpty then
209
+ throwError "Could not find any lemmas which can rewrite the hypothesis {
210
+ ← f.getUserName}"
211
+ for r in results do
212
+ addRewriteSuggestion tk (← mkConstWithFreshMVarLevels r.name) r.symm
213
+ r.result.eNew (loc? := .some (.fvar f)) (origSpan? := ← getRef)
214
+ if lucky.isSome then
215
+ match results[0 ]? with
216
+ | some r => do
217
+ let replaceResult ← goal.replaceLocalDecl f r.result.eNew r.result.eqProof
218
+ replaceMainGoal (replaceResult.mvarId :: r.result.mvarIds)
219
+ | _ => failure
220
+ do
221
+ let target ← instantiateMVars (← goal.getType)
222
+ let results ← rewrites lems goal target (stop_at_rfl := true )
223
+ reportOutOfHeartbeats `rewrites tk
224
+ if results.isEmpty then
225
+ throwError "Could not find any lemmas which can rewrite the goal"
226
+ for r in results do
227
+ let newGoal := if r.rfl? = some true then Expr.lit (.strVal "no goals" ) else r.result.eNew
228
+ addRewriteSuggestion tk (← mkConstWithFreshMVarLevels r.name) r.symm
229
+ newGoal (origSpan? := ← getRef)
230
+ if lucky.isSome then
231
+ match results[0 ]? with
232
+ | some r => do
233
+ replaceMainGoal
234
+ ((← goal.replaceTargetEq r.result.eNew r.result.eqProof) :: r.result.mvarIds)
235
+ evalTactic (← `(tactic| try rfl))
236
+ | _ => failure
237
+ (λ _ => throwError "Failed to find a rewrite for some location" )
238
+
239
+ @[inherit_doc rewrites'] macro "rw?!" h:(ppSpace location)? : tactic =>
240
+ `(tactic| rw? ! $[$h]?)
0 commit comments