fixes #22122; raise effects for complex expressions (#25845)

fixes #22122

The root cause is in the effect tracker: raise was recording the whole
conditional expression as one exception source, so semantic checking
only saw the widened common base type instead of the concrete exception
classes from each branch.
This commit is contained in:
ringabout
2026-06-09 04:59:16 +08:00
committed by GitHub
parent b6842c144d
commit e942da94b5
2 changed files with 95 additions and 1 deletions

View File

@@ -497,6 +497,26 @@ proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
if not isDefectException(e.typ):
throws(a.exc, e, comesFrom)
proc addRaiseEffectsFromExpr(a: PEffects, e, comesFrom: PNode) =
if e.isNil:
return
let x = skipConvCastAndClosure(e)
case x.kind
of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr:
if x.len > 0:
addRaiseEffectsFromExpr(a, x.lastSon, comesFrom)
of nkIfExpr, nkIfStmt:
for branch in items(x):
if branch.len > 0:
addRaiseEffectsFromExpr(a, branch.lastSon, comesFrom)
of nkCaseStmt:
for i in 1..<x.len:
let branch = x[i]
if branch.len > 0:
addRaiseEffectsFromExpr(a, branch.lastSon, comesFrom)
else:
addRaiseEffect(a, x, x)
proc addTag(a: PEffects, e, comesFrom: PNode) =
var aa = a.tags
for i in 0..<aa.len:
@@ -1326,7 +1346,7 @@ proc track(tracked: PEffects, n: PNode) =
if n[0].kind != nkEmpty:
n[0].info = n.info
#throws(tracked.exc, n[0])
addRaiseEffect(tracked, n[0], n)
addRaiseEffectsFromExpr(tracked, n[0], n)
for i in 0..<n.safeLen:
track(tracked, n[i])
createTypeBoundOps(tracked, n[0].typ, n.info)

View File

@@ -0,0 +1,74 @@
from std/os import osLastError, osErrorMsg, OSErrorCode, raiseOSError,
newOSError, `==`
{.push raises: [].}
const
EPERM* = OSErrorCode(1)
ECONNABORTED* = OSErrorCode(53)
ETIMEDOUT* = OSErrorCode(60)
ENOTCONN* = OSErrorCode(107)
EMFILE* = OSErrorCode(24)
ENFILE* = OSErrorCode(23)
ENOBUFS* = OSErrorCode(55)
ENOMEM* = OSErrorCode(12)
type
AsyncError* = object of CatchableError
TransportErrorBase* = object of AsyncError
TransportOsError* = object of TransportErrorBase
code*: OSErrorCode
TransportTooManyError* = object of TransportErrorBase
TransportAbortedError* = object of TransportErrorBase
template getConnectionAbortedError*(
code: OSErrorCode
): ref TransportAbortedError =
let msg =
case code
of OSErrorCode(0), ECONNABORTED:
"[ECONNABORTED] Connection has been aborted before being accepted"
of EPERM:
"[EPERM] Firewall rules forbid connection"
of ETIMEDOUT:
"[ETIMEDOUT] Operation has been timed out"
of ENOTCONN:
"[ENOTCONN] Transport endpoint is not connected"
else:
"[" & $int(code) & "] Connection has been aborted"
newException(TransportAbortedError, msg)
template getTransportTooManyError*(
code = OSErrorCode(0)
): ref TransportTooManyError =
let msg =
case code
of OSErrorCode(0):
"Too many open transports"
of EMFILE:
"[EMFILE] Too many open files in the process"
of ENFILE:
"[ENFILE] Too many open files in system"
of ENOBUFS:
"[ENOBUFS] No buffer space available"
of ENOMEM:
"[ENOMEM] Not enough memory availble"
else:
"[" & $int(code) & "] Too many open transports"
newException(TransportTooManyError, msg)
template getTransportError*(ecode: OSErrorCode): untyped =
case ecode
of ECONNABORTED, EPERM, ETIMEDOUT, ENOTCONN:
getConnectionAbortedError(ecode)
of EMFILE, ENFILE, ENOBUFS, ENOMEM:
getTransportTooManyError(ecode)
else:
(ref TransportOsError)(code: ecode,
msg: "(" & $int(ecode) & ") " & osErrorMsg(ecode))
proc raiseTransportError*(err: OSErrorCode) {.
raises: [TransportAbortedError, TransportTooManyError, TransportOsError],
noreturn.} =
## Raises transport specific OS error.
raise getTransportError(err)