Skip to content

Commit 0b83d65

Browse files
kim-emclaude
andcommitted
feat: exact? +try? uses try? as a discharger
This PR adds a `+try?` option to `exact?` and `apply?` tactics, similar to the existing `+grind` option. When `+try?` is enabled, `try?` is used as a fallback discharger for subgoals that `solve_by_elim` cannot close. The proof is wrapped in `Try.Marker` so suggestions display `(by try?)` instead of the complex proof term. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent fdabf46 commit 0b83d65

File tree

5 files changed

+104
-4
lines changed

5 files changed

+104
-4
lines changed

src/Init/Tactics.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,9 @@ structure LibrarySearchConfig where
16971697
/-- If true, use `grind` as a discharger for subgoals that cannot be closed
16981698
by `solve_by_elim` alone. -/
16991699
grind : Bool := false
1700+
/-- If true, use `try?` as a discharger for subgoals that cannot be closed
1701+
by `solve_by_elim` alone. -/
1702+
try? : Bool := false
17001703

17011704
/--
17021705
Searches environment for definitions or theorems that can solve the goal using `exact`
@@ -1707,6 +1710,7 @@ used by `exact?` when closing the goal. This is most useful if there are multip
17071710
ways to resolve the goal, and one wants to guide which lemma is used.
17081711
17091712
Use `+grind` to enable `grind` as a fallback discharger for subgoals.
1713+
Use `+try?` to enable `try?` as a fallback discharger for subgoals.
17101714
-/
17111715
syntax (name := exact?) "exact?" optConfig (" using " (colGt ident),+)? : tactic
17121716

@@ -1718,6 +1722,7 @@ The optional `using` clause provides identifiers in the local context that must
17181722
used when closing the goal.
17191723
17201724
Use `+grind` to enable `grind` as a fallback discharger for subgoals.
1725+
Use `+try?` to enable `try?` as a fallback discharger for subgoals.
17211726
-/
17221727
syntax (name := apply?) "apply?" optConfig (" using " (colGt term),+)? : tactic
17231728

src/Init/Try.lean

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,18 @@ macro "∎" : tactic => `(tactic| try?)
8282
/-- `∎` (typed as `\qed`) is a macro that expands to `by try? (wrapWithBy := true)` in term mode.
8383
The `wrapWithBy` config option causes suggestions to be prefixed with `by`. -/
8484
macro "∎" : term => `(by try? (wrapWithBy := true))
85+
86+
namespace Lean.Try
87+
88+
/--
89+
Marker for try?-solved subproofs in `exact? +try?` suggestions.
90+
When `exact?` uses try? as a discharger, it wraps the proof in this marker
91+
so that the unexpander can replace it with `(by try?)` in the suggestion.
92+
-/
93+
@[inline] def Marker {α : Sort u} (a : α) : α := a
94+
95+
@[app_unexpander Marker]
96+
meta def markerUnexpander : PrettyPrinter.Unexpander := fun _ => do
97+
`(by try?)
98+
99+
end Lean.Try

src/Lean/Elab/Tactic/LibrarySearch.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def exact? (ref : Syntax) (config : Parser.Tactic.LibrarySearchConfig)
3838
goal.withContext do
3939
let required := (← (required.getD #[]).mapM getFVarId).toList.map .fvar
4040
let tactic := fun goals =>
41-
solveByElim required (exfalso := false) goals (maxDepth := 6) (grind := config.grind)
41+
solveByElim required (exfalso := false) goals (maxDepth := 6) (grind := config.grind) (try? := config.try?)
4242
let allowFailure := fun g => do
4343
let g ← g.withContext (instantiateMVars (.mvar g))
4444
return required.all fun e => e.occurs g

src/Lean/Meta/Tactic/LibrarySearch.lean

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public import Lean.Meta.Tactic.SolveByElim
1111
public import Lean.Meta.Tactic.Grind.Main
1212
public import Lean.Util.Heartbeats
1313
import Init.Grind.Util
14+
import Init.Try
15+
import Lean.Elab.Tactic.Basic
1416

1517
public section
1618

@@ -62,22 +64,54 @@ def grindDischarger (mvarId : MVarId) : MetaM (Option (List MVarId)) := do
6264
catch _ =>
6365
return none
6466

67+
/--
68+
A discharger that tries `try?` on the goal.
69+
The proof is wrapped in `Try.Marker` so that suggestions display `(by try?)`
70+
instead of the complex proof term.
71+
Returns `some []` if try? succeeds, `none` otherwise (leaving the goal as a subgoal).
72+
-/
73+
def tryDischarger (mvarId : MVarId) : MetaM (Option (List MVarId)) := do
74+
try
75+
-- Apply the marker wrapper, creating a subgoal for try? to solve
76+
let type ← mvarId.getType
77+
let u ← getLevel type
78+
let markerExpr := mkApp (.const ``Lean.Try.Marker [u]) type
79+
let [subgoal] ← mvarId.apply markerExpr
80+
| return none
81+
-- Run try? via TacticM to solve the subgoal
82+
-- We suppress the "Try this" messages since we're using try? as a discharger
83+
let tacStx ← `(tactic| try?)
84+
let remainingGoals ← Elab.Term.TermElabM.run' <| Elab.Tactic.run subgoal do
85+
-- Suppress info messages from try?
86+
let initialLog ← Core.getMessageLog
87+
Elab.Tactic.evalTactic tacStx
88+
Core.setMessageLog initialLog
89+
if remainingGoals.isEmpty then
90+
return some []
91+
else
92+
return none
93+
catch _ =>
94+
return none
95+
6596
/--
6697
Wrapper for calling `Lean.Meta.SolveByElim.solveByElim with
6798
appropriate arguments for library search.
6899
69100
If `grind` is true, `grind` will be used as a fallback discharger for subgoals
70101
that cannot be closed by other means.
102+
If `try?` is true, `try?` will be used as a fallback discharger (via grind internally).
71103
-/
72104
def solveByElim (required : List Expr) (exfalso : Bool) (goals : List MVarId)
73-
(maxDepth : Nat) (grind : Bool := false) := do
105+
(maxDepth : Nat) (grind : Bool := false) (try? : Bool := false) := do
74106
let cfg : SolveByElimConfig :=
75107
{ maxDepth, exfalso := exfalso, symm := true, commitIndependentGoals := true,
76108
transparency := ← getTransparency,
77109
-- `constructor` has been observed to significantly slow down `exact?` in Mathlib.
78110
constructor := false }
79-
-- Add grind as a fallback discharger (tried after intro/constructor fail)
80-
let cfg := if grind then cfg.withDischarge grindDischarger else cfg
111+
-- Add grind or try? as a fallback discharger (tried after intro/constructor fail)
112+
let cfg := if try? then cfg.withDischarge tryDischarger
113+
else if grind then cfg.withDischarge grindDischarger
114+
else cfg
81115
let ⟨lemmas, ctx⟩ ← SolveByElim.mkAssumptionSet false false [] [] #[]
82116
let cfg := if !required.isEmpty then cfg.requireUsingAll required else cfg
83117
SolveByElim.solveByElim cfg lemmas ctx goals

tests/lean/run/library_search_grind.lean

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,49 @@ info: Try this:
4646
#guard_msgs in
4747
example (x : Nat) (h₁ : x < 30) (h₂ : 8 < x) : x.log2 < 6 := by
4848
exact? +grind
49+
50+
51+
inductive MyList (α : Type _) where
52+
| nil : MyList α
53+
| cons : α → MyList α → MyList α
54+
55+
axiom MyListProp : MyList α → Prop
56+
axiom MyListProp2 : MyList α → Prop
57+
58+
@[grind .] axiom mylist_nil : MyListProp (MyList.nil : MyList α)
59+
@[grind .] axiom mylist_cons : ∀ (x : α) (xs : MyList α), MyListProp xs → MyListProp (MyList.cons x xs)
60+
61+
/--
62+
info: Try these:
63+
[apply] (induction xs) <;> grind
64+
[apply] (induction xs) <;> grind only [mylist_nil, mylist_cons]
65+
[apply] ·
66+
induction xs
67+
· grind => instantiate only [mylist_nil]
68+
· grind => instantiate only [mylist_cons]
69+
-/
70+
#guard_msgs in
71+
example (xs : MyList α) : MyListProp xs := by
72+
try?
73+
74+
axiom qux (xs : MyList α) (p : MyListProp xs) : MyListProp2 xs
75+
76+
/--
77+
info: Try these:
78+
[apply] (induction xs) <;> grind
79+
[apply] (induction xs) <;> grind only [mylist_nil, mylist_cons]
80+
[apply] ·
81+
induction xs
82+
· grind => instantiate only [mylist_nil]
83+
· grind => instantiate only [mylist_cons]
84+
-/
85+
#guard_msgs in
86+
example (xs : MyList α) : MyListProp2 xs := by
87+
exact qux xs (by try?)
88+
89+
/--
90+
info: Try this:
91+
[apply] exact qux xs (by try?)
92+
-/
93+
example (xs : MyList α) : MyListProp2 xs := by
94+
exact? +try?

0 commit comments

Comments
 (0)