Skip to content

Commit 531d84c

Browse files
committed
Forgot to add file…
1 parent e4caa5e commit 531d84c

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/-
2+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Joachim Breitner
5+
-/
6+
7+
prelude
8+
import Lean.Meta.Tactic.Simp.Types
9+
10+
namespace Lean.Meta.Simp
11+
12+
def currentlyLoopChecking : SimpM Bool := do
13+
return !(← getContext).loopCheckStack.isEmpty
14+
15+
def getLoopCache (thm : SimpTheorem) : SimpM (Option LoopProtectionResult) := do
16+
return (← get).loopProtectionCache.lookup? thm
17+
18+
def setLoopCache (thm : SimpTheorem) (r : LoopProtectionResult) : SimpM Unit := do
19+
modifyThe State fun s => { s with loopProtectionCache := s.loopProtectionCache.insert thm r }
20+
21+
def setLoopCacheOkIfUnset (thm : SimpTheorem) : SimpM Unit := do
22+
unless (← getLoopCache thm).isSome do
23+
setLoopCache thm .ok
24+
25+
def setLoopCacheLoop (loop : Array SimpTheorem): SimpM Unit := do
26+
let thm := loop[0]!
27+
assert! (← getLoopCache thm).isNone
28+
assert! loop.size > 0
29+
setLoopCache thm (.loop loop)
30+
31+
def unlessWarnedBefore (thm : SimpTheorem) (k : SimpM Unit) : SimpM Unit := do
32+
unless (← get).loopProtectionCache.warned thm do
33+
modifyThe State fun s => { s with loopProtectionCache := s.loopProtectionCache.setWarned thm }
34+
k
35+
36+
def mkLoopWarningMsg (thms : Array SimpTheorem) : SimpM MessageData := do
37+
let mut msg := m!""
38+
if thms.size = 1 then
39+
msg := msg ++ m!"Ignoring looping simp theorem: {← ppOrigin thms[0]!.origin}"
40+
else
41+
msg := msg ++ m! "Ignoring jointly looping simp theorems: \
42+
{.andList (← thms.mapM (ppOrigin ·.origin)).toList}"
43+
msg := msg ++ .hint' m!"You can disable a simp theorem from the default simp set by \
44+
passing `- theoremName` to `simp`."
45+
msg := msg ++ .hint' m!"You can disable this check using `simp -loopProtection`."
46+
pure msg
47+
48+
private def rotations (a : Array α) : Array (Array α) := Id.run do
49+
let mut r : Array (Array α) := #[]
50+
for i in [:a.size] do
51+
r := r.push (a[i:] ++ a[:i])
52+
return r
53+
54+
def checkLoops (thm : SimpTheorem) : SimpM Bool := do
55+
let cfg ← getConfig
56+
-- No loop checking when disabled or in single pass mode
57+
if !cfg.loopProtection || cfg.singlePass then return true
58+
59+
60+
-- Permutating and local theorems are never checked, so accept when starting
61+
-- a loop check, and ignore when inside a loop check
62+
if thm.perm || thm.proof.hasFVar then
63+
return !(← currentlyLoopChecking)
64+
65+
-- Check cache
66+
if (← getLoopCache thm).isNone then
67+
withTraceNode `Meta.Tactic.simp.loopProtection (fun _ => return m!"loop-checking {← ppSimpTheorem thm}") do
68+
69+
-- Checking for a loop
70+
let seenThms := (← getContext).loopCheckStack
71+
if let some idx := seenThms.idxOf? thm then
72+
let loopThms := (seenThms.take (idx + 1)).toArray.reverse
73+
assert! loopThms[0]! == thm
74+
trace[Meta.Tactic.simp.loopProtection] "loop detected: {.andList (← loopThms.mapM (ppOrigin ·.origin)).toList}"
75+
(rotations loopThms).forM setLoopCacheLoop
76+
else
77+
-- Check the right-hand side
78+
withPushingLoopCheck thm do
79+
withFreshCache do
80+
let type ← inferType (← thm.getValue)
81+
forallTelescopeReducing type fun _xs type => do
82+
let rhs := (← whnf type).appArg!
83+
-- We ignore the result for now. We could return it to `tryTheoremCore` to avoid
84+
-- re-simplifying the right-hand side, but that would require some more refactoring
85+
let _ ← simp rhs
86+
-- If we made it this far without finding a loop, this theorem is fine
87+
setLoopCacheOkIfUnset thm
88+
89+
-- Now the cache tells us if this was looping
90+
if let some (.loop thms) ← getLoopCache thm then
91+
-- Only when this is the starting point and we have not warned before: report the loop
92+
unless (← currentlyLoopChecking) do
93+
unlessWarnedBefore thm do
94+
if let .stx _ ref := thm.origin then
95+
logWarningAt ref (← mkLoopWarningMsg thms)
96+
else
97+
logWarning (← mkLoopWarningMsg thms)
98+
return false
99+
else
100+
return true

0 commit comments

Comments
 (0)