mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-02 13:39:03 +00:00
fixes #25851
## Summary: `--panics:on` drops `nimErr_` check after closure calls
(#25851)
### Bug
With `--exceptions:goto` and `--panics:on`, the compiler skipped the
`nimErr_` check after indirect closure calls whose result flows directly
into another call (e.g., `result.add elem(src)`). A raise inside the
closure was silently swallowed — the loop continued, and the next
`raise` hit the already-set `nimInErrorMode` flag, overflowing its
`bool` storage into `OverflowDefect`.
### Root Cause
**ast.nim** — `canRaise` checked `fn.typ.n[0].len < effectListLen` first
(false after the expansion) and then `exceptionEffects != nil` (also
false, nil), so it returned `false` — meaning "cannot raise." The C
codegen trusted this and omitted the `nimErr_` check.
### Fix
**ast.nim** — `canRaise` now treats `nil` `exceptionEffects` as "unknown
→ can raise" (`exceptionEffects == nil` as an additional true
condition). This is defense-in-depth: even if some other path expands
the list but leaves `exceptionEffects` nil (e.g., a type with `{.tags.}`
but no `{.raises.}`), the error check is still emitted.
### Test
tclosure_err_panic_goto.nim — exercises the double-trigger pattern
(`drawBool` sets the error flag → closure call must propagate it) with
`matrix: "; --panics:on"` covering both exception modes.
(cherry picked from commit 88a18de44f)
37 lines
1.1 KiB
Nim
37 lines
1.1 KiB
Nim
discard """
|
||
matrix: "; --panics:on"
|
||
"""
|
||
# issue #25851: --panics:on must not drop the nimErr_ check after a closure
|
||
# call whose result is consumed directly (e.g. `result.add elem(src)`).
|
||
# Regression from #25295.
|
||
|
||
type
|
||
Overrun = object of CatchableError
|
||
Source = object
|
||
data: seq[bool]
|
||
cursor: int
|
||
ElemFn = proc(src: var Source): bool {.closure.}
|
||
|
||
proc drawBool(src: var Source): bool =
|
||
if src.cursor >= src.data.len: raise newException(Overrun, "exhausted")
|
||
result = src.data[src.cursor]; inc src.cursor
|
||
|
||
proc listRun(elem: ElemFn, src: var Source): seq[bool] =
|
||
result = @[]
|
||
while true:
|
||
if not src.drawBool(): break
|
||
result.add elem(src) # closure call – the result flows straight
|
||
# into `add`, which previously caused the
|
||
# compiler to skip the nimErr_ check.
|
||
|
||
let elem: ElemFn = proc(src: var Source): bool = src.drawBool()
|
||
|
||
# Both --panics:on and --panics:off must propagate the Overrun.
|
||
var caught = false
|
||
try:
|
||
var src = Source(data: @[true])
|
||
discard listRun(elem, src)
|
||
except Overrun:
|
||
caught = true
|
||
doAssert caught, "Overrun exception was swallowed"
|