Skip to content

Commit d6b397a

Browse files
committed
fix: panic in tactic analysis framework (#31472)
The test added in this PR causes a panic on current mathlib, the panic being this one: https://github.com/leanprover-community/mathlib4/blob/32fb6832e1009174e2787a2cbbc7669399175c61/Mathlib/Lean/ContextInfo.lean#L72 This PR fixes the panic. The issue is in the tactic analysis framework's `terminalReplacement` linter builder. The idea with `terminalReplacement` is to try all `old_tactic` occurrences to see if swapping in `new_tactic` finishes at that point. If `new_tactic` works but doesn't finish it is supposed to print the new goal(s) produced by `new_tactic`; this is the case that was causing the panic. The issue was that the `MetaM` call(s) to infer the type(s) of the new goal(s) were being routed through the `Lean.Elab.ContextInfo.runTactic` function, which panics if (as will typically be the case) one of these goals represents nontrivial progress from the previous goal state. I have fixed this by storing the types of the new goals at the time they are generated, so we don't need `Lean.Elab.ContextInfo.runTactic` to cook up a new `MetaM` instance.
1 parent 05b317a commit d6b397a

File tree

4 files changed

+36
-10
lines changed

4 files changed

+36
-10
lines changed

Mathlib/Lean/ContextInfo.lean

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,15 @@ def runTactic (ctx : ContextInfo) (i : TacticInfo) (goal : MVarId) (x : MVarId
7878
let goal ← Meta.mkFreshExprSyntheticOpaqueMVar type
7979
x goal.mvarId!
8080

81-
/-- Run tactic code, given by a piece of syntax, in the context of an infotree node. -/
82-
def runTacticCode (ctx : ContextInfo) (i : TacticInfo) (goal : MVarId) (code : Syntax) :
83-
CommandElabM (List MVarId) := do
81+
/-- Run tactic code, given by a piece of syntax, in the context of an infotree node.
82+
The optional `MetaM` argument `m` performs postprocessing on the goals produced. -/
83+
def runTacticCode (ctx : ContextInfo) (i : TacticInfo) (goal : MVarId) (code : Syntax)
84+
(m : Σ α : Type, MVarId → MetaM α := ⟨MVarId, pure⟩) :
85+
CommandElabM (List m.1) := do
8486
let termCtx ← liftTermElabM read
8587
let termState ← liftTermElabM get
86-
ctx.runTactic i goal fun goal =>
87-
Lean.Elab.runTactic' (ctx := termCtx) (s := termState) goal code
88+
ctx.runTactic i goal fun goal => do
89+
let newGoals ← Lean.Elab.runTactic' (ctx := termCtx) (s := termState) goal code
90+
newGoals.mapM m.2
8891

8992
end Lean.Elab.ContextInfo

Mathlib/Tactic/TacticAnalysis.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,8 @@ def Config.ofComplex (config : ComplexConfig) : Config where
354354
end ComplexConfig
355355

356356
end Mathlib.TacticAnalysis
357+
358+
/-- A dummy option for testing the tactic analysis framework -/
359+
register_option linter.tacticAnalysis.dummy : Bool := {
360+
defValue := false
361+
}

Mathlib/Tactic/TacticAnalysis/Declarations.lean

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,11 @@ def terminalReplacement (oldTacticName newTacticName : String) (oldTacticKind :
5151
test ctxI i stx goal := do
5252
let tac ← newTactic ctxI i stx
5353
try
54-
let goals ← ctxI.runTacticCode i goal tac
55-
match goals with
54+
let goalTypes ← ctxI.runTacticCode i goal tac ⟨Expr, MVarId.getType'⟩
55+
match goalTypes with
5656
| [] => return .success tac
5757
| _ => do
58-
let goalsMessages ← goals.mapM fun g => do
59-
let e ← ctxI.runTactic i g <| fun g => do instantiateMVars (← g.getType)
60-
pure m!"⊢ {MessageData.ofExpr e}\n"
58+
let goalsMessages := goalTypes.map fun e => m!"⊢ {MessageData.ofExpr e}\n"
6159
return .remainingGoals tac goalsMessages
6260
catch _e =>
6361
let name ← mkAuxDeclName `extracted

MathlibTest/TacticAnalysis.lean

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@ example : 1 + 1 = 2 := by
1515

1616
end omega
1717

18+
@[tacticAnalysis linter.tacticAnalysis.dummy]
19+
def foo : Mathlib.TacticAnalysis.Config :=
20+
Mathlib.TacticAnalysis.terminalReplacement "simp" "simp only" ``Lean.Parser.Tactic.simp
21+
(fun _ _ _ => `(tactic| simp only))
22+
(reportSuccess := true) (reportFailure := true)
23+
24+
/--
25+
warning: `simp only` left unsolved goals where `simp` succeeded.
26+
Original tactic:
27+
simp
28+
Replacement tactic:
29+
simp only
30+
Unsolved goals:
31+
[⊢ (List.map (fun x => x + 1) [1, 2, 3]).sum = 9 ]
32+
-/
33+
#guard_msgs in
34+
set_option linter.tacticAnalysis.dummy true in
35+
example : List.sum ([1,2,3].map fun x ↦ x + 1) = 9 := by
36+
simp
37+
1838
end terminalReplacement
1939

2040
section rwMerge

0 commit comments

Comments
 (0)