Skip to content

Commit 746206c

Browse files
authored
feat: hover information for grind anchors (#10779)
This PR implements hover information for `grind` anchors. Anchors are stable hash codes for referencing terms in the grind state. The anchors will be used when auto generating tactic scripts. The hover display the following information: 1- In the `instantiate` tactic, it displays the type of the theorem being instantiated. <img width="952" height="125" alt="image" src="https://github.com/user-attachments/assets/be949b87-cf9b-4f75-abe0-17751295de93" /> 2- In the `cases` tactic, the hover information depends on the kind of case-split. a) Proposition <img width="1019" height="125" alt="image" src="https://github.com/user-attachments/assets/253e2927-f18e-49ab-a8fc-2144657406d8" /> b) A hypotheses. In this case, you can opt to replace the anchor with the hypothesis' name if it is accessible. <img width="1019" height="178" alt="image" src="https://github.com/user-attachments/assets/858b3751-4ef9-492d-a42f-c0743753a7de" /> c) A term. The hover displays just the type, by `grind` logs a silent information with additional information <img width="1376" height="148" alt="image" src="https://github.com/user-attachments/assets/30078ca4-a886-49d9-912e-866f3567b0da" />
1 parent 88141a0 commit 746206c

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

src/Lean/Elab/Tactic/Grind/BuiltinTactic.lean

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def evalCheck (tacticName : Name) (k : GoalM Bool)
104104
@[builtin_grind_tactic ac] def evalAC : GrindTactic := fun _ => do
105105
evalCheck `ac AC.check AC.pp?
106106

107+
def logTheoremAnchor (proof : Expr) : TermElabM Unit := do
108+
let stx ← getRef
109+
Term.addTermInfo' stx proof
110+
107111
def ematchThms (thms : Array EMatchTheorem) : GrindTacticM Unit := do
108112
let progress ← liftGoalM <| if thms.isEmpty then ematch else ematchTheorems thms
109113
unless progress do
@@ -135,19 +139,24 @@ def elabAnchor (anchor : TSyntax `hexnum) : CoreM (Nat × UInt64) := do
135139
ematchThms thms
136140
| _ => throwUnsupportedSyntax
137141
where
138-
collectThms (numDigits : Nat) (anchorPrefix : UInt64) (thms : PArray EMatchTheorem) : StateT (Array EMatchTheorem) GrindM Unit := do
142+
collectThms (numDigits : Nat) (anchorPrefix : UInt64) (thms : PArray EMatchTheorem) : StateT (Array EMatchTheorem) GrindTacticM Unit := do
143+
let mut found : Std.HashSet Expr := {}
139144
for thm in thms do
140145
-- **Note**: `anchors` are cached using pointer addresses, if this is a performance issue, we should
141146
-- cache the theorem types.
142147
let type ← inferType thm.proof
143-
let anchor ← getAnchor type
148+
let anchor ← liftGrindM <| getAnchor type
144149
if isAnchorPrefix numDigits anchorPrefix anchor then
150+
-- **Note**: We display the anchor term at most once.
151+
unless found.contains type do
152+
logTheoremAnchor thm.proof
153+
found := found.insert type
145154
modify (·.push thm)
146155

147-
elabLocalEMatchTheorem (anchor : TSyntax `hexnum) : GrindTacticM (Array EMatchTheorem) := do
156+
elabLocalEMatchTheorem (anchor : TSyntax `hexnum) : GrindTacticM (Array EMatchTheorem) := withRef anchor do
148157
let (numDigits, anchorPrefix) ← elabAnchor anchor
149158
let goal ← getMainGoal
150-
let thms ← liftGrindM do StateT.run' (s := #[]) do
159+
let thms ← StateT.run' (s := #[]) do
151160
collectThms numDigits anchorPrefix goal.ematch.thms
152161
collectThms numDigits anchorPrefix goal.ematch.newThms
153162
get
@@ -214,20 +223,47 @@ where
214223
| .cases _ | .intro | .inj | .ext | .symbol _ =>
215224
throwError "invalid modifier"
216225

226+
def logAnchor (numDigits : Nat) (anchorPrefix : UInt64) (e : Expr) : TermElabM Unit := do
227+
let stx ← getRef
228+
if e.isFVar || e.isConst then
229+
/-
230+
**Note**: When `e` is a constant or free variable, the hover displays `e`
231+
-/
232+
Term.addTermInfo' stx e
233+
else if (← isType e) then
234+
/-
235+
**Note**: If `e` is a type, we create a fake `sorry` to force `e` to be displayed
236+
when we hover over the anchor.
237+
We wrap the `sorry` with `id` to ensure the hover will not display `sorry : e`
238+
-/
239+
let e ← mkSorry e (synthetic := false)
240+
let e ← mkId e
241+
Term.addTermInfo' stx e
242+
else
243+
/-
244+
**Note**: only the `e`s type is displayed when hovering over the anchor.
245+
We add a silent info with the anchor declaration.
246+
-/
247+
Term.addTermInfo' stx e
248+
logAt (severity := .information) (isSilent := true) stx
249+
m!"#{anchorToString numDigits anchorPrefix} := {e}"
250+
217251
@[builtin_grind_tactic cases] def evalCases : GrindTactic := fun stx => do
218252
match stx with
219253
| `(grind| cases #$anchor:hexnum) =>
220254
let (numDigits, val) ← elabAnchor anchor
221255
let goal ← getMainGoal
222256
let candidates := goal.split.candidates
223-
let (goals, genNew) ← liftSearchM do
257+
let (e, goals, genNew) ← liftSearchM do
224258
for c in candidates do
259+
let e := c.getExpr
225260
let anchor ← getAnchor c.getExpr
226261
if isAnchorPrefix numDigits val anchor then
227262
let some result ← split? c
228263
| throwError "`cases` tactic failed, case-split is not ready{indentExpr c.getExpr}"
229-
return result
264+
return (e, result)
230265
throwError "`cases` tactic failed, invalid anchor"
266+
goal.withContext <| withRef anchor <| logAnchor numDigits val e
231267
let goals ← goals.filterMapM fun goal => do
232268
let (goal, _) ← liftGrindM <| SearchM.run goal do
233269
intros genNew

tests/lean/run/grind_interactive.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ example : (∀ x, q x) → (∀ x, p x → p (f x)) → p x → p (f (f x)) := b
414414
show_thms
415415
instantiate #bfb8
416416

417+
example : (∀ x, q x) → (∀ x, p x → p (f x)) → p x → p (f (f x)) := by
418+
grind =>
419+
show_thms
420+
instantiate #bfb8
421+
417422
/-- error: no local theorems -/
418423
#guard_msgs in
419424
example : (∀ x, q x) → (∀ x, p x → p (f x)) → p x → p (f (f x)) := by

0 commit comments

Comments
 (0)