Skip to content

Commit 088f8b0

Browse files
eric-wieserKha
andauthored
fix: teach Exception.isRuntime to detect nested errors (#11490)
This PR prevents `try` swallowing heartbeat errors from nested `simp` calls, and more generally ensures the `isRuntime` flag is propagated by `throwNestedTacticEx`. This prevents the behavior of proofs (especially those using `aesop`) being affected by the current recursion depth or heartbeat limit. This breaks a single caller in Mathlib where `simp` uses a lemma of the form `x = f (g x)` and stack overflows, which can be fixed by generalizing over `g x`. Closes #7811. --------- Co-authored-by: Sebastian Ullrich <[email protected]>
1 parent 30ea417 commit 088f8b0

File tree

5 files changed

+64
-11
lines changed

5 files changed

+64
-11
lines changed

src/Lean/CoreM.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,10 @@ export Core (CoreM mkFreshUserName checkSystem withCurrHeartbeats)
647647
This function is a bit hackish. The heartbeat exception should probably be an internal exception.
648648
We used a similar hack at `Exception.isMaxRecDepth` -/
649649
def Exception.isMaxHeartbeat (ex : Exception) : Bool :=
650-
ex matches Exception.error _ (.tagged `runtime.maxHeartbeats _)
650+
if let Exception.error _ msg := ex then
651+
msg.stripNestedTags.kind == `runtime.maxHeartbeats
652+
else
653+
false
651654

652655
/-- Creates the expression `d → b` -/
653656
def mkArrow (d b : Expr) : CoreM Expr :=

src/Lean/Exception.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,10 @@ but it is also produced by `MacroM` which implemented in the prelude, and intern
232232
been defined yet.
233233
-/
234234
def Exception.isMaxRecDepth (ex : Exception) : Bool :=
235-
ex matches error _ (.tagged `runtime.maxRecDepth _)
235+
if let Exception.error _ msg := ex then
236+
msg.stripNestedTags.kind == `runtime.maxRecDepth
237+
else
238+
false
236239

237240
/--
238241
Increment the current recursion depth and then execute `x`.

src/Lean/Log.lean

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ If `msg` is tagged as a named error, appends the error description widget displa
8686
corresponding error name and explanation link. Otherwise, returns `msg` unaltered.
8787
-/
8888
private def MessageData.appendDescriptionWidgetIfNamed (msg : MessageData) : MessageData :=
89-
let kind := stripNestedTags msg.kind
90-
match errorNameOfKind? kind with
89+
match msg.stripNestedTags.errorName? with
9190
| some errorName =>
9291
let url := manualRoot ++ s!"find/?domain={errorExplanationManualDomain}&name={errorName}"
9392
let inst := {
@@ -102,13 +101,6 @@ private def MessageData.appendDescriptionWidgetIfNamed (msg : MessageData) : Mes
102101
-- console output
103102
msg.composePreservingKind <| .ofWidget inst .nil
104103
| none => msg
105-
where
106-
/-- Remove any `` `nested `` name components prepended by `throwNestedTacticEx`. -/
107-
stripNestedTags : Name → Name
108-
| .str p "nested" => stripNestedTags p
109-
| .str p s => .str (stripNestedTags p) s
110-
| .num p n => .num (stripNestedTags p) n
111-
| .anonymous => .anonymous
112104

113105
/--
114106
Log the message `msgData` at the position provided by `ref` with the given `severity`.

src/Lean/Message.lean

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,23 @@ and `throwNamedError`.
457457
def MessageData.tagWithErrorName (msg : MessageData) (name : Name) : MessageData :=
458458
.tagged (kindOfErrorName name) msg
459459

460+
/-- Strip the `` `nested`` prefix components added to tags by `throwNestedTacticEx`. -/
461+
def MessageData.stripNestedTags : MessageData → MessageData
462+
| .withContext ctx msg => .withContext ctx msg.stripNestedTags
463+
| .withNamingContext ctx msg => .withNamingContext ctx msg.stripNestedTags
464+
| .tagged n msg => .tagged (stripNestedNamePrefix n) msg
465+
| msg => msg
466+
where
467+
stripNestedNamePrefix : Name → Name
468+
| .anonymous => .anonymous
469+
| .str p s =>
470+
let p' := stripNestedNamePrefix p
471+
if p'.isAnonymous && s == "nested" then
472+
.anonymous
473+
else
474+
.str p' s
475+
| .num p n => .num (stripNestedNamePrefix p) n
476+
460477
/--
461478
If the provided name is labeled as a diagnostic name, removes the label and returns the
462479
corresponding diagnostic name.

tests/lean/run/7811.lean

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Lean
2+
3+
4+
-- This should not be swallowed by the `try`
5+
/--
6+
warning: Possibly looping simp theorem: `← Nat.add_zero 1`
7+
8+
Hint: You can disable a simp theorem from the default simp set by passing `- theoremName` to `simp`.
9+
---
10+
error: Tactic `simp` failed with a nested error:
11+
maximum recursion depth has been reached
12+
use `set_option maxRecDepth <num>` to increase limit
13+
use `set_option diagnostics true` to get diagnostic information
14+
-/
15+
#guard_msgs in
16+
set_option maxRecDepth 200 in-- low to make this fast
17+
example : 1 = sorry := by
18+
try simp [← Nat.add_zero 1]
19+
20+
open Lean
21+
22+
/--
23+
error: Tactic `foo` failed with a nested error:
24+
maximum recursion depth has been reached
25+
use `set_option maxRecDepth <num>` to increase limit
26+
use `set_option diagnostics true` to get diagnostic information
27+
-/
28+
#guard_msgs in
29+
run_meta show MetaM Unit from do
30+
try
31+
-- Throw a recursion depth error wrapped in a nested error
32+
tryCatchRuntimeEx
33+
(throwMaxRecDepthAt .missing)
34+
(fun e =>
35+
Meta.throwNestedTacticEx `foo e)
36+
catch e =>
37+
-- We didn't use `tryCatchRuntimeEx` here so we shouldn't catch it.
38+
throwError m!"Should not be caught"

0 commit comments

Comments
 (0)