Skip to content

Commit 793a85e

Browse files
committed
cleanup the PR
1 parent e58a811 commit 793a85e

3 files changed

Lines changed: 34 additions & 22 deletions

File tree

src/Lean/Meta/Basic.lean

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,18 @@ We should also investigate the impact on memory consumption.
376376
-/
377377
abbrev DefEqCache := PersistentHashMap DefEqCacheKey Bool
378378

379+
/--
380+
A `DefEqTransCache` is a `DefEqCache` that is only valid in the original `MetavarContext`.
381+
It stores of the `numAssignments` from that original `MetavarContext`.
382+
If the `numAssignments` in the `MetavarContext` has increased, we invalidate this cache.
383+
And when we revert the metavariable context in `checkpointDefEq`, if the `numAssignments`
384+
in the original `MetavarContext` is smaller than in the cache, we revert the cache to its original.
385+
-/
386+
structure DefEqTransCache where
387+
cache : DefEqCache := {}
388+
numAssignments : Nat := 0
389+
deriving Inhabited
390+
379391
/--
380392
Cache datastructures for type inference, type class resolution, whnf, and definitional equality.
381393
-/
@@ -384,7 +396,7 @@ structure Cache where
384396
funInfo : FunInfoCache := {}
385397
synthInstance : SynthInstanceCache := {}
386398
whnf : WhnfCache := {}
387-
defEqTrans : DefEqCache × Nat := ({}, 0) -- transient cache for terms containing mvars or using nonstandard configuration options, it is valid as long as the count matches `MetavarContext.numAssignments`.
399+
defEqTrans : DefEqTransCache := {} -- transient cache for terms containing mvars or using nonstandard configuration options, it is valid as long as the count matches `MetavarContext.numAssignments`.
388400
defEqPerm : DefEqCache := {} -- permanent cache for terms not containing mvars and using standard configuration options
389401
deriving Inhabited
390402

@@ -646,9 +658,9 @@ def resetCache : MetaM Unit :=
646658

647659
@[inline] def modifyDefEqTransientCache (numAssignments : Nat) (f : DefEqCache → DefEqCache) : MetaM Unit :=
648660
modifyCache fun c =>
649-
let (transCache, numAssignmentsOld) := c.defEqTrans
661+
let transCache, numAssignmentsOld := c.defEqTrans
650662
let transCache := if numAssignments == numAssignmentsOld then transCache else {}
651-
{ c with defEqTrans := (f transCache, numAssignments) }
663+
{ c with defEqTrans := f transCache, numAssignments }
652664

653665
@[inline] def modifyDefEqPermCache (f : DefEqCache → DefEqCache) : MetaM Unit :=
654666
modifyCache fun ⟨c1, c2, c3, c4, c5, defeqPerm⟩ => ⟨c1, c2, c3, c4, c5, f defeqPerm⟩
@@ -667,7 +679,7 @@ def mkInfoCacheKey (expr : Expr) (nargs? : Option Nat) : MetaM InfoCacheKey :=
667679
return { expr, nargs?, configKey := (← read).configKey }
668680

669681
@[inline] def resetDefEqTransientCache : MetaM Unit :=
670-
modify fun s => { s with cache.defEqTrans := ({}, s.mctx.numAssignments) }
682+
modify fun s => { s with cache.defEqTrans := {}, s.mctx.numAssignments }
671683

672684
@[inline] def resetDefEqPermCaches : MetaM Unit :=
673685
modifyDefEqPermCache fun _ => {}
@@ -2369,13 +2381,13 @@ partial def processPostponed (mayPostpone : Bool := true) (exceptionOnFailure :=
23692381
return true
23702382
else
23712383
-- The transient cache needs to be reverted if it assumes an assignments that is being reverted.
2372-
let invalidCache := s.meta.mctx.numAssignments < (← get).cache.defEqTrans.2
2373-
s.restore (transCache := invalidCache)
2384+
let isInvalidCache := s.meta.mctx.numAssignments != (← get).cache.defEqTrans.numAssignments
2385+
s.restore (transCache := isInvalidCache)
23742386
return false
23752387
else
23762388
-- The transient cache needs to be reverted if it assumes an assignments that is being reverted.
2377-
let invalidCache := s.meta.mctx.numAssignments < (← get).cache.defEqTrans.2
2378-
s.restore (transCache := invalidCache)
2389+
let isInvalidCache := s.meta.mctx.numAssignments != (← get).cache.defEqTrans.numAssignments
2390+
s.restore (transCache := isInvalidCache)
23792391
return false
23802392
catch ex =>
23812393
s.restore
@@ -2416,8 +2428,8 @@ def isExprDefEq (t s : Expr) : MetaM Bool :=
24162428
We have tried in the past to track when the result was independent of the `MetavarContext` state
24172429
but it was not effective. It is more important to cache aggressively inside of a single `isDefEq`
24182430
call because some of the heuristics create many similar subproblems.
2419-
See issue #1102 for an example that triggers an exponential blowup if we don't use this more
2420-
aggressive form of caching.
2431+
See issue #1102 and `tests/lean/run/defEqTransCache.lean` for examples that trigger an exponential blowup
2432+
if we don't use this more aggressive form of caching.
24212433
-/
24222434
resetDefEqTransientCache
24232435
checkpointDefEq (mayPostpone := true) <| Meta.isExprDefEqAux t s

src/Lean/Meta/ExprDefEq.lean

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ private partial def isDefEqArgs (f : Expr) (args₁ args₂ : Array Expr) : Meta
316316
let info := finfo.paramInfo[i]!
317317
if info.isInstImplicit then
318318
unless (← withInferTypeConfig <| Meta.isExprDefEqAux a₁ a₂) do
319-
return false
319+
return false
320320
else
321321
unless (← Meta.isExprDefEqAux a₁ a₂) do
322322
return false
@@ -2085,7 +2085,7 @@ private def mkCacheKey (t s : Expr) : MetaM DefEqCacheKeyInfo := do
20852085
private def getCachedResult (keyInfo : DefEqCacheKeyInfo) : MetaM LBool := do
20862086
let cache ← match keyInfo.kind with
20872087
| .transient numAssignments =>
2088-
let (cache, numAssignmentsCache) := (← get).cache.defEqTrans
2088+
let cache, numAssignmentsCache := (← get).cache.defEqTrans
20892089
if numAssignments == numAssignmentsCache then
20902090
pure cache
20912091
else
@@ -2102,14 +2102,14 @@ private def cacheResult (keyInfo : DefEqCacheKeyInfo) (result : Bool) : MetaM Un
21022102
| .transient numAssignmentsOld =>
21032103
/-
21042104
If the result is `false`, we cache it at `numAssignmentsOld`.
2105-
If the result is `true`, we check that the number of assignments hasn't increased.
2105+
If the result is `true`, we only cache it if the number of assignments hasn't increase.
21062106
-/
21072107
if !result then
21082108
modifyDefEqTransientCache numAssignmentsOld fun c => c.insert key result
21092109
else
21102110
let numAssignmentsNew := (← getMCtx).numAssignments
21112111
if numAssignmentsOld == numAssignmentsNew then
2112-
modifyDefEqTransientCache numAssignmentsNew fun c => c.insert key result
2112+
modifyDefEqTransientCache numAssignmentsOld fun c => c.insert key result
21132113

21142114
private def whnfCoreAtDefEq (e : Expr) : MetaM Expr := do
21152115
if backward.isDefEq.lazyWhnfCore.get (← getOptions) then

tests/lean/run/defEqTransCache.lean

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import Lean
22
/-!
33
Previously, unification wouldn't be very careful with the `isDefEq` cache for terms containing metavariables.
44
- This is mostly problematic because erasing the cache leads to exponential slowdowns (`test1` & `test2`)
5-
- but in some cases it leads to metavariable assignments leaking into places where they shouldn't be,
6-
which either causes unification to fail where it should succeed (`test3`)
7-
or to succeed where it is expected to fail.
8-
5+
- but in some cases it lead to metavariable assignments leaking into places where they shouldn't be,
6+
which either caused unification to fail where it should succeed (`test3`)
7+
or to succeed where it is expected to fail (which happened in one mathlib proof).
98
-/
9+
1010
set_option maxHeartbeats 1000
1111

1212
namespace test1
@@ -16,10 +16,10 @@ class A (n : Nat) where
1616
instance [A n] : A (n+1) where
1717
x := A.x n
1818

19-
theorem test [A 0] : A.x 100 = sorry := sorry
19+
theorem test [A 0] : A.x 100 = 0 := sorry
2020

21-
-- Previously, this example was exponentially slow
22-
example [A 1] : A.x 100 = sorry := by
21+
-- This rewrite should fail. Previously, it failed exponentially slowly
22+
example [A 1] : A.x 100 = 0 := by
2323
fail_if_success rw [@test]
2424
sorry
2525
end test1
@@ -66,7 +66,7 @@ elab "unfold_head" e:term : term => do
6666
let e ← Elab.Term.elabTerm e none
6767
unfoldDefinition e
6868

69-
-- we use `unfold_head` in order to get the raw kernel projection `·.1` instead of the projection funtcion `A.x`.
69+
-- use `unfold_head` to get the raw kernel projection `·.1` instead of the projection funtcion `A.x`
7070
def test {α} (i : B α) : unfold_head i.toA.x := sorry
7171

7272
-- Previously, in this example the unification failed,

0 commit comments

Comments
 (0)