Skip to content

Commit 0a43d12

Browse files
kim-emkbuzzardnomeataChrisHughes24eric-wieser
committed
chore: fix bug in rw? (#6088)
This fixes a problem I encountered in another tactic, where the rewrites are not being tried with the original `MetavarContext`. Co-authored-by: Kevin Buzzard <k.buzzard@imperial.ac.uk> Co-authored-by: Joachim Breitner <mail@joachim-breitner.de> Co-authored-by: Chris Hughes <chrishughes24@gmail.com> Co-authored-by: Eric Wieser <wieser.eric@gmail.com> Co-authored-by: Anatole Dedecker <anatolededecker@gmail.com> Co-authored-by: Thomas Browning <tb65536@uw.edu> Co-authored-by: Oliver Nash <github@olivernash.org> Co-authored-by: sgouezel <sebastien.gouezel@univ-rennes1.fr> Co-authored-by: Alex J Best <alex.j.best@gmail.com> Co-authored-by: Xavier Roblot <46200072+xroblot@users.noreply.github.com> Co-authored-by: michaellee94 <michael_lee1@brown.edu> Co-authored-by: Yuma Mizuno <mizuno.y.aj@gmail.com> Co-authored-by: Markus Himmel <markus@himmel-villmar.de> Co-authored-by: Scott Morrison <scott.morrison@gmail.com> Co-authored-by: Peiran Wu <15968905+wupr@users.noreply.github.com> Co-authored-by: Moritz Firsching <moritz.firsching@gmail.com> Co-authored-by: Yury G. Kudryashov <urkud@urkud.name> Co-authored-by: Ruben Van de Velde <65514131+Ruben-VandeVelde@users.noreply.github.com> Co-authored-by: Anne Baanen <vierkantor@vierkantor.com> Co-authored-by: Bolton Bailey <bolton.bailey@gmail.com> Co-authored-by: Frédéric Dupuis <dupuisf@iro.umontreal.ca> Co-authored-by: Moritz Doll <moritz.doll@googlemail.com> Co-authored-by: kkytola <kalle.kytola@aalto.fi> Co-authored-by: Joël Riou <joel.riou@universite-paris-saclay.fr> Co-authored-by: damiano <adomani@gmail.com> Co-authored-by: Kyle Miller <kmill31415@gmail.com> Co-authored-by: Rémy Degenne <remydegenne@gmail.com> Co-authored-by: Jz Pan <acme_pjz@hotmail.com> Co-authored-by: Eric Rodriguez <ericrboidi@gmail.com>
1 parent 9b9a1a5 commit 0a43d12

File tree

2 files changed

+38
-17
lines changed

2 files changed

+38
-17
lines changed

Mathlib/Tactic/Rewrites.lean

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,21 @@ structure RewriteResult where
118118
/-- Can the new goal in `result` be closed by `with_reducible rfl`? -/
119119
-- This is an `Option` so that it can be computed lazily.
120120
rfl? : Option Bool
121+
/-- The metavariable context after the rewrite.
122+
This needs to be stored as part of the result so we can backtrack the state. -/
123+
mctx : MetavarContext
121124

122125
/-- Update a `RewriteResult` by filling in the `rfl?` field if it is currently `none`,
123126
to reflect whether the remaining goal can be closed by `with_reducible rfl`. -/
124127
def RewriteResult.computeRfl (r : RewriteResult) : MetaM RewriteResult := do
125128
if let some _ := r.rfl? then
126129
return r
127130
try
128-
(← mkFreshExprMVar r.result.eNew).mvarId!.rfl
129-
pure { r with rfl? := some true }
131+
withoutModifyingState <| withMCtx r.mctx do
132+
-- We use `withReducible` here to follow the behaviour of `rw`.
133+
withReducible (← mkFreshExprMVar r.result.eNew).mvarId!.rfl
134+
-- We do not need to record the updated `MetavarContext` here.
135+
pure { r with rfl? := some true }
130136
catch _ =>
131137
pure { r with rfl? := some false }
132138

@@ -136,16 +142,18 @@ Find lemmas which can rewrite the goal.
136142
This core function returns a monadic list, to allow the caller to decide how long to search.
137143
See also `rewrites` for a more convenient interface.
138144
-/
145+
-- We need to supply the current `MetavarContext` (which will be reused for each lemma application)
146+
-- because `MLList.squash` executes lazily,
147+
-- so there is no opportunity for `← getMCtx` to record the context at the call site.
139148
def rewritesCore (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name × Bool × Nat) s)
140-
(goal : MVarId) (target : Expr) : MLList MetaM RewriteResult := MLList.squash fun _ => do
141-
149+
(ctx : MetavarContext) (goal : MVarId) (target : Expr) :
150+
MLList MetaM RewriteResult := MLList.squash fun _ => do
142151
-- Get all lemmas which could match some subexpression
143152
let candidates := (← lemmas.1.getSubexpressionMatches target)
144153
++ (← lemmas.2.getSubexpressionMatches target)
145154

146155
-- Sort them by our preferring weighting
147156
-- (length of discriminant key, doubled for the forward implication)
148-
149157
let candidates := candidates.insertionSort fun r s => r.2.2 > s.2.2
150158

151159
-- Now deduplicate. We can't use `Array.deduplicateSorted` as we haven't completely sorted,
@@ -167,34 +175,34 @@ def rewritesCore (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name
167175

168176
-- Lift to a monadic list, so the caller can decide how much of the computation to run.
169177
let lazy := MLList.ofList deduped.toList
170-
pure <| lazy.filterMapM fun ⟨lem, symm, weight⟩ => do
178+
pure <| lazy.filterMapM fun ⟨lem, symm, weight⟩ => withMCtx ctx do
171179
trace[Tactic.rewrites] "considering {if symm then "" else ""}{lem}"
172180
let some result ← try? do goal.rewrite target (← mkConstWithFreshMVarLevels lem) symm
173181
| return none
174182
return if result.mvarIds.isEmpty then
175-
some ⟨lem, symm, weight, result, none⟩
183+
some ⟨lem, symm, weight, result, none, ← getMCtx
176184
else
177185
-- TODO Perhaps allow new goals? Try closing them with solveByElim?
178186
none
179187

180188
/-- Find lemmas which can rewrite the goal. -/
181189
def rewrites (lemmas : DiscrTree (Name × Bool × Nat) s × DiscrTree (Name × Bool × Nat) s)
182-
(goal : MVarId) (target : Expr) (stop_at_rfl : Bool := False) (max : Nat := 20)
190+
(goal : MVarId) (target : Expr) (stopAtRfl : Bool := false) (max : Nat := 20)
183191
(leavePercentHeartbeats : Nat := 10) : MetaM (List RewriteResult) := do
184-
let results ← rewritesCore lemmas goal target
192+
let results ← rewritesCore lemmas (← getMCtx) goal target
185193
-- Don't report duplicate results.
186194
-- (TODO: we later pretty print results; save them here?)
187195
-- (TODO: a config flag to disable this,
188196
-- if distinct-but-pretty-print-the-same results are desirable?)
189197
|>.dedupBy (fun r => do pure <| (← ppExpr r.result.eNew).pretty)
190198
-- Stop if we find a rewrite after which `with_reducible rfl` would succeed.
191-
|>.mapM RewriteResult.computeRfl -- TODO could simply not compute this if `stop_at_rfl` is False
192-
|>.takeUpToFirst (fun r => stop_at_rfl && r.rfl? = some true)
199+
|>.mapM RewriteResult.computeRfl -- TODO could simply not compute this if `stopAtRfl` is False
200+
|>.takeUpToFirst (fun r => stopAtRfl && r.rfl? = some true)
193201
-- Don't use too many heartbeats.
194202
|>.whileAtLeastHeartbeatsPercent leavePercentHeartbeats
195203
-- Bound the number of results.
196204
|>.takeAsList max
197-
return match results.filter (fun r => stop_at_rfl && r.rfl? = some true) with
205+
return match results.filter (fun r => stopAtRfl && r.rfl? = some true) with
198206
| [] =>
199207
-- TODO consider sorting the results,
200208
-- e.g. if we use solveByElim to fill arguments,
@@ -226,39 +234,41 @@ elab_rules : tactic |
226234
let some a ← f.findDecl? | return
227235
if a.isImplementationDetail then return
228236
let target ← instantiateMVars (← f.getType)
229-
let results ← rewrites lems goal target (stop_at_rfl := false)
237+
let results ← rewrites lems goal target (stopAtRfl := false)
230238
reportOutOfHeartbeats `rewrites tk
231239
if results.isEmpty then
232240
throwError "Could not find any lemmas which can rewrite the hypothesis {
233241
← f.getUserName}"
234-
for r in results do
242+
for r in results do withMCtx r.mctx do
235243
addRewriteSuggestion tk [(← mkConstWithFreshMVarLevels r.name, r.symm)]
236244
r.result.eNew (loc? := .some (.fvar f)) (origSpan? := ← getRef)
237245
if lucky.isSome then
238246
match results[0]? with
239247
| some r => do
248+
setMCtx r.mctx
240249
let replaceResult ← goal.replaceLocalDecl f r.result.eNew r.result.eqProof
241250
replaceMainGoal (replaceResult.mvarId :: r.result.mvarIds)
242251
| _ => failure
243252
-- See https://github.com/leanprover/lean4/issues/2150
244253
do withMainContext do
245254
let target ← instantiateMVars (← goal.getType)
246-
let results ← rewrites lems goal target (stop_at_rfl := true)
255+
let results ← rewrites lems goal target (stopAtRfl := true)
247256
reportOutOfHeartbeats `rewrites tk
248257
if results.isEmpty then
249258
throwError "Could not find any lemmas which can rewrite the goal"
250-
for r in results do
259+
for r in results do withMCtx r.mctx do
251260
let newGoal := if r.rfl? = some true then Expr.lit (.strVal "no goals") else r.result.eNew
252261
addRewriteSuggestion tk [(← mkConstWithFreshMVarLevels r.name, r.symm)]
253262
newGoal (origSpan? := ← getRef)
254263
if lucky.isSome then
255264
match results[0]? with
256265
| some r => do
266+
setMCtx r.mctx
257267
replaceMainGoal
258268
((← goal.replaceTargetEq r.result.eNew r.result.eqProof) :: r.result.mvarIds)
259269
evalTactic (← `(tactic| try rfl))
260270
| _ => failure
261-
(λ _ => throwError "Failed to find a rewrite for some location")
271+
(fun _ => throwError "Failed to find a rewrite for some location")
262272

263273
@[inherit_doc rewrites'] macro "rw?!" h:(ppSpace location)? : tactic =>
264274
`(tactic| rw? ! $[$h]?)

test/rewrites.lean

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ set_option autoImplicit true
1515
-- `build/lib/MathlibExtras/Rewrites.extra`
1616
-- so that the cache is rebuilt.
1717

18+
set_option autoImplicit true
19+
1820
/--
1921
info: Try this: rw [@List.map_append]
2022
-- "no goals"
@@ -148,3 +150,12 @@ info: Try this: rw [f_eq]
148150
lemma test : f n = f m := by
149151
rw?
150152
rw [f_eq, f_eq]
153+
154+
155+
def zero : Nat := 0
156+
157+
-- This used to (incorrectly!) succeed because `rw?` would try `rfl`,
158+
-- rather than `withReducible` `rfl`.
159+
example : zero = 0 := by
160+
rw?!
161+
sorry

0 commit comments

Comments
 (0)