Compare commits

...

2 Commits

Author SHA1 Message Date
Leonardo de Moura
96dc5a2ea3 chore: update test 2025-06-03 17:05:14 -07:00
Leonardo de Moura
ae0a7cc467 fix: equality resolution bug 2025-06-03 17:03:48 -07:00
3 changed files with 82 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
prelude
import Lean.Meta.AppBuilder
import Lean.Meta.MatchUtil
import Lean.Util.ForEachExpr
namespace Lean.Meta.Grind
/-! A basic "equality resolution" procedure. -/
@@ -21,6 +22,67 @@ private def forallMetaTelescopeReducingAndUnfoldingNot (prop : Expr) : MetaM (Ar
return (ms.push m, mkConst ``False)
return (ms, type)
structure TopSort.State where
tempMark : Std.HashSet Expr := {}
permMark : Std.HashSet Expr := {}
result : Array Expr := #[]
abbrev TopSortM := OptionT $ StateT TopSort.State MetaM
/--
Sorts metavariables `ms` using topological sort.
There is an "edge" from `m₁` to `m₂` if type of `m₁` contains `m₂`.
We use this function to ensure that after applying equality resolution to
```
∀ x : Nat, p x a → ∀ y : Nat, p y b → x = y → False
```
we produce
```
∀ y, p y a → p y b → False
```
instead of
```
p ?y a → ∀ y, p y b → False
```
Recall that in equality resolution we create a meta-variable for each hypothesis.
Thus, we initially have
```
?x : Nat, ?h₁ : p ?x a, ?y : Nat, ?h₂ : p ?y b, ?h₃ : ?x = ?y
```
Then, we resolve `?h₃ : ?x = ?y` as `?y := ?x` and `?h₃ := Eq.refl ?y`.
But `?h₁` occurs before `?y`. We use topological sort to address this situation.
If a cycle is detected, it returns `none`.
-/
private partial def topsortMVars? (ms : Array Expr) : MetaM (Option (Array Expr)) := do
let (some _, s) go.run.run {} | return none
return some s.result
where
go : TopSortM Unit := do
for m in ms do
visit m
visit (m : Expr) : TopSortM Unit := do
if ( get).permMark.contains m then
return ()
if ( get).tempMark.contains m then
failure
modify fun s => { s with tempMark := s.tempMark.insert m }
visitTypeOf m
modify fun s => { s with
result := s.result.push m
permMark := s.permMark.insert m
}
visitTypeOf (m : Expr) : TopSortM Unit := do
let type instantiateMVars ( inferType m)
type.forEach' fun e => do
if e.hasExprMVar then
if e.isMVar && ms.contains e then
visit e
return true
else
return false
private def eqResCore (prop proof : Expr) : MetaM (Option (Expr × Expr)) := withNewMCtxDepth do
/-
We use `forallMetaTelescopeReducingAndUnfoldingNot` because we want to treat
@@ -51,6 +113,7 @@ private def eqResCore (prop proof : Expr) : MetaM (Option (Expr × Expr)) := wit
let prop' instantiateMVars type
let proof' instantiateMVars (mkAppN proof ms)
let ms ms.filterM fun m => return !( m.mvarId!.isAssigned)
let some ms topsortMVars? ms | return none
let prop' mkForallFVars ms prop' (binderInfoForMVars := .default)
let proof' mkLambdaFVars ms proof'
return some (prop', proof')

View File

@@ -21,6 +21,6 @@ example (ls : Array Unit) : Option Clause :=
example (ls : Array Unit) : Option Clause :=
ls.foldl folder (some ) |>.map fun map =>
-- FIXME: Commenting this out gives an unknown metavariable error in `grind`!
-- have mapnodup := map.distinct_keys
-- The following example is still failing, but
-- we don't get the unknown metavar bug anymore
map.toList, by grind

View File

@@ -0,0 +1,17 @@
set_option grind.warning false
/--
trace: [grind.eqResolution] ∀ (x : Nat), p x a → ∀ (y : Nat), p y b → ¬x = y, ∀ (y : Nat), p y a → p y b → False
[grind.ematch.instance] local_0: p c a → ¬p c b
-/
#guard_msgs (trace) in
example
(p : Nat Nat Prop)
(a b c : Nat)
(h : x, p x a y, p y b x y)
(h₁ : p c a)
(h₂ : p c b)
: False := by
set_option trace.grind.eqResolution true in
set_option trace.grind.ematch.instance true in
grind