Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions compiler/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ proc genState(p: BProc, n: PNode) =
elif n0.kind == nkStrLit:
p.s(cpsStmts).addLabel(n0.strVal)

proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt = false) =
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts.

Expand Down Expand Up @@ -258,9 +258,9 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =

# Pop exceptions that was handled by the
# except-blocks we are in
if noSafePoints notin p.flags:
for i in countdown(howManyExcepts-1, 0):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))
if noSafePoints notin p.flags and not (isReturnStmt and isClosureIterator(p.prc.typ)):
for i in countdown(howManyExcepts-1, 0):
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "popCurrentException"))

proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later
Expand Down Expand Up @@ -556,7 +556,8 @@ proc genReturnStmt(p: BProc, t: PNode) =
if (t[0].kind != nkEmpty): genStmts(p, t[0])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen)
howManyExcepts = p.inExceptBlockLen,
isReturnStmt = true)
if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
# If we're in a finally block, and we came here by exception
# consume it before we return.
Expand Down
39 changes: 21 additions & 18 deletions compiler/closureiters.nim
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,14 @@ type
tempVarId: int # unique name counter
hasExceptions: bool # Does closure have yield in try?
curExcLandingState: PNode
curExceptLevel: int
curFinallyLevel: int
idgen: IdGenerator
varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
finallyPathLen: PNode # int literal

nullifyCurExc: PNode # Empty node, if no yields in tries
restoreExternExc: PNode # Empty node, id no yields in tries

const
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
nkCommentStmt, nkMixinStmt, nkBindStmt, nkTypeOfExpr} + procDefs
Expand Down Expand Up @@ -311,7 +313,7 @@ proc hasYields(n: PNode): bool =
break

proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
# :curEcx = nil
# :curExc = nil
let curExc = ctx.newCurExcAccess()
curExc.info = info
let nilnode = newNodeIT(nkNilLit, info, getSysType(ctx.g, info, tyNil))
Expand Down Expand Up @@ -862,7 +864,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
retStmt.flags.incl(nfNoRewrite)

let ifBody = newTree(nkIfStmt,
newTree(nkElifBranch, excNilCmp, retStmt),
newTree(nkElifBranch, excNilCmp, newTree(nkStmtList, ctx.newRestoreExternException(), retStmt)),
newTree(nkElse,
newTree(nkStmtList,
newTreeI(nkRaiseStmt, info, ctx.g.emptyNode))))
Expand Down Expand Up @@ -917,16 +919,15 @@ proc transformBreakStmt(ctx: var Ctx, n: PNode): PNode =
result = n

proc transformReturnStmt(ctx: var Ctx, n: PNode): PNode =
# "Returning" involves jumping along all the cureent finally path.
# "Returning" involves jumping along all the current finally path.
# The last finally should exit to state 0 which is a special case for last exit
# (either return or propagating exception to the caller).
# It is eccounted for in newEndFinallyNode.
result = newNodeI(nkStmtList, n.info)

# Returns prevent exception propagation
result.add(ctx.newNullifyCurExc(n.info))
result.add(ctx.nullifyCurExc)

result.add(ctx.newRestoreExternException())

var finallyChain = newSeq[PNode]()

Expand All @@ -950,6 +951,7 @@ proc transformReturnStmt(ctx: var Ctx, n: PNode): PNode =
result.add(ctx.newJumpAlongFinallyChain(finallyChain, n.info))
else:
# There are no (split) finallies on the path, so we can return right away
result.add(ctx.restoreExternExc)
result.add(n)

proc transformBreaksAndReturns(ctx: var Ctx, n: PNode): PNode =
Expand All @@ -960,7 +962,7 @@ proc transformBreaksAndReturns(ctx: var Ctx, n: PNode): PNode =
# of nkContinueStmt: # By this point all relevant continues should be
# lowered to breaks in transf.nim.
of nkReturnStmt:
if ctx.curFinallyLevel > 0 and nfNoRewrite notin n.flags:
if nfNoRewrite notin n.flags:
result = ctx.transformReturnStmt(n)
else:
for i in 0..<n.len:
Expand Down Expand Up @@ -994,8 +996,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode

of nkYieldStmt:
result = addGotoOut(result, gotoOut)
if ctx.curExceptLevel > 0 or ctx.curFinallyLevel > 0:
result = newTree(nkStmtList, ctx.newRestoreExternException(), result)
result = newTree(nkStmtList, ctx.restoreExternExc, result)

of nkElse, nkElseExpr:
result[0] = addGotoOut(result[0], gotoOut)
Expand Down Expand Up @@ -1107,7 +1108,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
tryBody = ctx.transformClosureIteratorBody(tryBody, tryOut)

if exceptBody.kind != nkEmpty:
inc ctx.curExceptLevel
ctx.curExcLandingState = if finallyBody.kind != nkEmpty: finallyLabel
else: oldExcLandingState
discard ctx.newState(exceptBody, false, exceptLabel)
Expand All @@ -1116,7 +1116,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
exceptBody = ctx.addElseToExcept(exceptBody, normalOut)
# echo "EXCEPT: ", renderTree(exceptBody)
exceptBody = ctx.transformClosureIteratorBody(exceptBody, tryOut)
inc ctx.curExceptLevel

ctx.curExcLandingState = oldExcLandingState

Expand Down Expand Up @@ -1469,13 +1468,17 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:

ctx.curExcLandingState = ctx.newStateLabel()
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)


ctx.nullifyCurExc = newTree(nkStmtList)
ctx.restoreExternExc = newTree(nkStmtList)

var n = n.toStmtList
# echo "transformed into ", n

discard ctx.newState(n, false, nil)

let finalState = ctx.newStateLabel()
let gotoOut = newTree(nkGotoState, finalState)
let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))

var ns = false
n = ctx.lowerStmtListExprs(n, ns)
Expand All @@ -1487,11 +1490,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
# Splitting transformation
discard ctx.transformClosureIteratorBody(n, gotoOut)

let finalStateBody = newTree(nkStmtList)
if ctx.hasExceptions:
finalStateBody.add(ctx.newRestoreExternException())
finalStateBody.add(newTree(nkGotoState, g.newIntLit(n.info, -1)))
discard ctx.newState(finalStateBody, true, finalState)
ctx.nullifyCurExc.add(ctx.newNullifyCurExc(fn.info))
ctx.restoreExternExc.add(ctx.newRestoreExternException())

# Assign state label indexes
for i in 0 .. ctx.states.high:
Expand All @@ -1510,7 +1511,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
let body = ctx.transformStateAssignments(s.body)
caseDispatcher.add newTreeI(nkOfBranch, body.info, s.label, body)

caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
caseDispatcher.add newTreeI(nkElse, n.info,
newTree(nkStmtList, ctx.restoreExternExc,
newTreeI(nkReturnStmt, n.info, g.emptyNode)))

result = wrapIntoStateLoop(ctx, caseDispatcher)
result = liftLocals(ctx, result)
Expand Down
49 changes: 49 additions & 0 deletions tests/iter/tyieldintry.nim
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,52 @@ block: #25202
doAssert(checkpoints1 == checkpoints2)

p()

block: #25261
iterator y(): int {.closure.} =
try:
try:
raise newException(CatchableError, "Error")
except CatchableError:
return 123
yield 0
finally:
discard

let w = y
doAssert(w() == 123)
doAssert(getCurrentExceptionMsg() == "")

try:
raise newException(ValueError, "Outer error")
except:
doAssert(getCurrentExceptionMsg() == "Outer error")
let w = y
doAssert(w() == 123)
doAssert(getCurrentExceptionMsg() == "Outer error")
doAssert(getCurrentExceptionMsg() == "")

block:
# Looks almost like above, but last finally changed to except
iterator y(): int {.closure.} =
try:
try:
raise newException(CatchableError, "Error")
except CatchableError:
return 123
yield 0
except:
discard

let w = y
doAssert(w() == 123)
doAssert(getCurrentExceptionMsg() == "")

try:
raise newException(ValueError, "Outer error")
except:
doAssert(getCurrentExceptionMsg() == "Outer error")
let w = y
doAssert(w() == 123)
doAssert(getCurrentExceptionMsg() == "Outer error")
doAssert(getCurrentExceptionMsg() == "")