Skip to content

Commit 3a76a36

Browse files
committed
fix: non-chronological backtracking for Action
1 parent f4ea880 commit 3a76a36

File tree

2 files changed

+35
-41
lines changed

2 files changed

+35
-41
lines changed

src/Lean/Meta/Tactic/Grind/Split.lean

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -246,47 +246,34 @@ private def casesWithTrace (mvarId : MVarId) (major : Expr) : GoalM (List MVarId
246246

247247
namespace Action
248248

249-
private def isTargetFalse (mvarId : MVarId) : MetaM Bool := do
250-
return (← mvarId.getType).isFalse
251-
252-
private def getFalseProof? (mvarId : MVarId) : MetaM (Option Expr) := mvarId.withContext do
253-
let proof ← instantiateMVars (mkMVar mvarId)
254-
if (← isTargetFalse mvarId) then
255-
return some proof
256-
else if proof.isAppOfArity ``False.elim 2 || proof.isAppOfArity ``False.casesOn 2 then
257-
return some proof.appArg!
258-
else
259-
return none
260-
261249
/--
262-
Returns the maximum free variable id occurring in `e`
250+
Given a `mvarId` associated with a subgoal created by `splitCore`, inspects the
251+
proof term assigned to `mvarId` and tries to extract the proof of `False` that does not
252+
depend on hypotheses introduced in the subgoal.
253+
For example: suppose the subgoal is of the form `p → q → False` where `p` and `q` are new
254+
hypotheses introduced during case analysis. If the proof is of the form `fun _ _ => h`, returns
255+
`some h`.
263256
-/
264-
private def findMaxFVarIdx? (e : Expr) : MetaM (Option Nat) := do
265-
let go (e : Expr) : StateT (Option Nat) MetaM Bool := do
266-
unless e.hasFVar do return false
267-
let .fvar fvarId := e | return true
268-
let localDecl ← fvarId.getDecl
269-
modify fun
270-
| none => some localDecl.index
271-
| some index => some (max index localDecl.index)
272-
return false
273-
let (_, s?) ← e.forEach' go |>.run none
274-
return s?
275-
276-
/--
277-
Returns `some falseProof` if we can use non-chronological backtracking with `subgoal`.
278-
That is, `subgoal` was closed using `falseProof`, but its proof does not use any of the
279-
new hypotheses. A hypothesis is new if its `index >= oldNumIndices`.
280-
-/
281-
private def useNCB? (oldNumIndices : Nat) (subgoal : Goal) : MetaM (Option Expr) := do
282-
let some falseProof ← getFalseProof? subgoal.mvarId
283-
| return none
284-
let some max ← subgoal.withContext <| findMaxFVarIdx? falseProof
285-
| return some falseProof -- Proof actually closes any pending split
286-
if max < oldNumIndices then
287-
return some falseProof
288-
else
289-
return none
257+
private def getFalseProof? (mvarId : MVarId) : MetaM (Option Expr) := mvarId.withContext do
258+
let proof ← instantiateMVars (mkMVar mvarId)
259+
go proof
260+
where
261+
go (proof : Expr) : MetaM (Option Expr) := do
262+
match_expr proof with
263+
| False.elim _ p => return some p
264+
| False.casesOn _ p => return some p
265+
| id α p => if α.isFalse then return some p else return none
266+
| _ =>
267+
/-
268+
**Note**: `intros` tactics may hide the `False` proof behind a `casesOn`
269+
For example: suppose the subgoal has a type of the form `p₁ → q₁ ∧ q₂ → p₂ → False`
270+
The proof will be of the form `fun _ h => h.casesOn (fun _ _ => hf)` where `hf` is the proof
271+
of `False` we are looking for.
272+
Non-chronological backtracking currently fails in this kind of example.
273+
-/
274+
let .lam _ _ b _ := proof | return none
275+
if b.hasLooseBVars then return none
276+
go b
290277

291278
/--
292279
Performs a case-split using `c`.
@@ -325,12 +312,16 @@ private def splitCore (c : SplitInfo) (numCases : Nat) (isRec : Bool) (stopAtFir
325312
else
326313
stuckNew := stuckNew ++ gs
327314
| .closed seq =>
328-
if let some falseProof ← useNCB? numIndices subgoal then
315+
if let some falseProof ← getFalseProof? subgoal.mvarId then
329316
goal.mvarId.assignFalseProof falseProof
330317
return .closed seq
331318
else
332319
seqNew := seqNew.push seq
333-
goal.mvarId.assign (← instantiateMVars (mkMVar mvarId))
320+
if (← goal.mvarId.getType).isFalse then
321+
/- **Note**: We add the marker to assist `getFalseExpr?` -/
322+
goal.mvarId.assign (mkExpectedPropHint (← instantiateMVars (mkMVar mvarId)) (mkConst ``False))
323+
else
324+
goal.mvarId.assign (← instantiateMVars (mkMVar mvarId))
334325
if stuckNew.isEmpty then
335326
if traceEnabled then
336327
let seqListNew ← if h : seqNew.size = 1 then

src/Lean/Meta/Tactic/Grind/Types.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,9 @@ If type of `mvarId` is not `False`, then use `False.elim`.
12491249
def _root_.Lean.MVarId.assignFalseProof (mvarId : MVarId) (falseProof : Expr) : MetaM Unit := mvarId.withContext do
12501250
let target ← mvarId.getType
12511251
if target.isFalse then
1252+
/-
1253+
**Note**: We add the marker to assist `getFalseExpr?` used to implement
1254+
non-chronological backtracking. -/
12521255
mvarId.assign (mkExpectedPropHint falseProof (mkConst ``False))
12531256
else
12541257
mvarId.assign (← mkFalseElim target falseProof)

0 commit comments

Comments
 (0)