Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve case-split heuristic used in grind #6658

Merged
merged 3 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Lean/Meta/Tactic/Grind/Split.lean
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ private def checkIffStatus (e a b : Expr) : GoalM CaseSplitStatus := do
else
return .notReady

/-- Returns `true` is `c` is congruent to a case-split that was already performed. -/
private def isCongrToPrevSplit (c : Expr) : GoalM Bool := do
(← get).resolvedSplits.foldM (init := false) fun flag { expr := c' } => do
if flag then
return true
else
return isCongruent (← get).enodes c c'

private def checkCaseSplitStatus (e : Expr) : GoalM CaseSplitStatus := do
match_expr e with
| Or a b => checkDisjunctStatus e a b
Expand All @@ -85,6 +93,8 @@ private def checkCaseSplitStatus (e : Expr) : GoalM CaseSplitStatus := do
if (← isResolvedCaseSplit e) then
trace[grind.debug.split] "split resolved: {e}"
return .resolved
if (← isCongrToPrevSplit e) then
return .resolved
if let some info := isMatcherAppCore? (← getEnv) e then
return .ready info.numAlts
let .const declName .. := e.getAppFn | unreachable!
Expand Down Expand Up @@ -163,6 +173,7 @@ def splitNext : GrindTactic := fun goal => do
| return none
let gen ← getGeneration c
let genNew := if numCases > 1 || isRec then gen+1 else gen
markCaseSplitAsResolved c
trace_goal[grind.split] "{c}, generation: {gen}"
let mvarIds ← if (← isMatcherApp c) then
casesMatch (← get).mvarId c
Expand Down
5 changes: 3 additions & 2 deletions src/Lean/Meta/Tactic/Grind/Types.lean
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ structure Goal where
splitCandidates : List Expr := []
/-- Number of splits performed to get to this goal. -/
numSplits : Nat := 0
/-- Case-splits that do not have to be performed anymore. -/
/-- Case-splits that have already been performed, or that do not have to be performed anymore. -/
resolvedSplits : PHashSet ENodeKey := {}
/-- Next local E-match theorem idx. -/
nextThmIdx : Nat := 0
Expand Down Expand Up @@ -879,7 +879,8 @@ def isResolvedCaseSplit (e : Expr) : GoalM Bool :=

/--
Mark `e` as a case-split that does not need to be performed anymore.
Remark: we currently use this feature to disable `match`-case-splits
Remark: we currently use this feature to disable `match`-case-splits.
Remark: we also use this feature to record the case-splits that have already been performed.
-/
def markCaseSplitAsResolved (e : Expr) : GoalM Unit := do
unless (← isResolvedCaseSplit e) do
Expand Down
50 changes: 50 additions & 0 deletions tests/lean/run/grind_split_issue.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
variable (d : Nat) in
inductive X : Nat → Prop
| f {s : Nat} : X s
| g {s : Nat} : X d → X s

/--
error: `grind` failed
case grind.1
c : Nat
q : X c 0
s : Nat
h✝ : 0 = s
h : HEq ⋯ ⋯
⊢ False
[grind] Diagnostics
[facts] Asserted facts
[prop] X c 0
[prop] 0 = s
[prop] HEq ⋯ ⋯
[eqc] True propositions
[prop] X c 0
[prop] X c s
[eqc] Equivalence classes
[eqc] {s, 0}
case grind.2
c : Nat
q : X c 0
s : Nat
a✝¹ a✝ : X c c
h✝ : 0 = s
h : HEq ⋯ ⋯
⊢ False
[grind] Diagnostics
[facts] Asserted facts
[prop] X c 0
[prop] X c c
[prop] 0 = s
[prop] HEq ⋯ ⋯
[eqc] True propositions
[prop] X c 0
[prop] X c c
[prop] X c s
[eqc] Equivalence classes
[eqc] {s, 0}
[issues] Issues
[issue] this goal was not fully processed due to previous failures, threshold: `(failures := 1)`
-/
#guard_msgs (error) in
example {c : Nat} (q : X c 0) : False := by
grind
Loading