Skip to content

Commit 6656084

Browse files
yglukhovAraq
andauthored
Returning or yielding from a closureiter must restore "external" exception, but `popCurrentException` from `blockLeaveActions` was getting in the way. So now `blockLeaveActions` doesn't emit `popCurrentException` for returns in closureiters. I'm not a fan of this "abstraction leakage", but don't see a better solution yet. Any input is much appreciated. --------- Co-authored-by: Andreas Rumpf <[email protected]>
1 parent 0486a2d commit 6656084

File tree

3 files changed

+74
-21
lines changed

3 files changed

+74
-21
lines changed

compiler/ccgstmts.nim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ proc genState(p: BProc, n: PNode) =
226226
elif n0.kind == nkStrLit:
227227
p.s(cpsStmts).addLabel(n0.strVal)
228228

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

@@ -258,7 +258,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
258258

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

@@ -557,7 +557,8 @@ proc genReturnStmt(p: BProc, t: PNode) =
557557
if (t[0].kind != nkEmpty): genStmts(p, t[0])
558558
blockLeaveActions(p,
559559
howManyTrys = p.nestedTryStmts.len,
560-
howManyExcepts = p.inExceptBlockLen)
560+
howManyExcepts = p.inExceptBlockLen,
561+
isReturnStmt = true)
561562
if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
562563
# If we're in a finally block, and we came here by exception
563564
# consume it before we return.

compiler/closureiters.nim

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,14 @@ type
174174
tempVarId: int # unique name counter
175175
hasExceptions: bool # Does closure have yield in try?
176176
curExcLandingState: PNode
177-
curExceptLevel: int
178177
curFinallyLevel: int
179178
idgen: IdGenerator
180179
varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
181180
finallyPathLen: PNode # int literal
182181

182+
nullifyCurExc: PNode # Empty node, if no yields in tries
183+
restoreExternExc: PNode # Empty node, id no yields in tries
184+
183185
const
184186
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
185187
nkCommentStmt, nkMixinStmt, nkBindStmt, nkTypeOfExpr} + procDefs
@@ -311,7 +313,7 @@ proc hasYields(n: PNode): bool =
311313
break
312314

313315
proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
314-
# :curEcx = nil
316+
# :curExc = nil
315317
let curExc = ctx.newCurExcAccess()
316318
curExc.info = info
317319
let nilnode = newNodeIT(nkNilLit, info, getSysType(ctx.g, info, tyNil))
@@ -862,7 +864,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
862864
retStmt.flags.incl(nfNoRewrite)
863865

864866
let ifBody = newTree(nkIfStmt,
865-
newTree(nkElifBranch, excNilCmp, retStmt),
867+
newTree(nkElifBranch, excNilCmp, newTree(nkStmtList, ctx.newRestoreExternException(), retStmt)),
866868
newTree(nkElse,
867869
newTree(nkStmtList,
868870
newTreeI(nkRaiseStmt, info, ctx.g.emptyNode))))
@@ -917,16 +919,15 @@ proc transformBreakStmt(ctx: var Ctx, n: PNode): PNode =
917919
result = n
918920

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

926928
# Returns prevent exception propagation
927-
result.add(ctx.newNullifyCurExc(n.info))
929+
result.add(ctx.nullifyCurExc)
928930

929-
result.add(ctx.newRestoreExternException())
930931

931932
var finallyChain = newSeq[PNode]()
932933

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

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

995997
of nkYieldStmt:
996998
result = addGotoOut(result, gotoOut)
997-
if ctx.curExceptLevel > 0 or ctx.curFinallyLevel > 0:
998-
result = newTree(nkStmtList, ctx.newRestoreExternException(), result)
999+
result = newTree(nkStmtList, ctx.restoreExternExc, result)
9991000

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

11091110
if exceptBody.kind != nkEmpty:
1110-
inc ctx.curExceptLevel
11111111
ctx.curExcLandingState = if finallyBody.kind != nkEmpty: finallyLabel
11121112
else: oldExcLandingState
11131113
discard ctx.newState(exceptBody, false, exceptLabel)
@@ -1116,7 +1116,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
11161116
exceptBody = ctx.addElseToExcept(exceptBody, normalOut)
11171117
# echo "EXCEPT: ", renderTree(exceptBody)
11181118
exceptBody = ctx.transformClosureIteratorBody(exceptBody, tryOut)
1119-
inc ctx.curExceptLevel
11201119

11211120
ctx.curExcLandingState = oldExcLandingState
11221121

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

14701469
ctx.curExcLandingState = ctx.newStateLabel()
14711470
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
1471+
1472+
1473+
ctx.nullifyCurExc = newTree(nkStmtList)
1474+
ctx.restoreExternExc = newTree(nkStmtList)
1475+
14721476
var n = n.toStmtList
14731477
# echo "transformed into ", n
14741478

14751479
discard ctx.newState(n, false, nil)
14761480

1477-
let finalState = ctx.newStateLabel()
1478-
let gotoOut = newTree(nkGotoState, finalState)
1481+
let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
14791482

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

1490-
let finalStateBody = newTree(nkStmtList)
14911493
if ctx.hasExceptions:
1492-
finalStateBody.add(ctx.newRestoreExternException())
1493-
finalStateBody.add(newTree(nkGotoState, g.newIntLit(n.info, -1)))
1494-
discard ctx.newState(finalStateBody, true, finalState)
1494+
ctx.nullifyCurExc.add(ctx.newNullifyCurExc(fn.info))
1495+
ctx.restoreExternExc.add(ctx.newRestoreExternException())
14951496

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

1513-
caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
1514+
caseDispatcher.add newTreeI(nkElse, n.info,
1515+
newTree(nkStmtList, ctx.restoreExternExc,
1516+
newTreeI(nkReturnStmt, n.info, g.emptyNode)))
15141517

15151518
result = wrapIntoStateLoop(ctx, caseDispatcher)
15161519
result = liftLocals(ctx, result)

tests/iter/tyieldintry.nim

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,3 +798,52 @@ block: #25202
798798
doAssert(checkpoints1 == checkpoints2)
799799

800800
p()
801+
802+
block: #25261
803+
iterator y(): int {.closure.} =
804+
try:
805+
try:
806+
raise newException(CatchableError, "Error")
807+
except CatchableError:
808+
return 123
809+
yield 0
810+
finally:
811+
discard
812+
813+
let w = y
814+
doAssert(w() == 123)
815+
doAssert(getCurrentExceptionMsg() == "")
816+
817+
try:
818+
raise newException(ValueError, "Outer error")
819+
except:
820+
doAssert(getCurrentExceptionMsg() == "Outer error")
821+
let w = y
822+
doAssert(w() == 123)
823+
doAssert(getCurrentExceptionMsg() == "Outer error")
824+
doAssert(getCurrentExceptionMsg() == "")
825+
826+
block:
827+
# Looks almost like above, but last finally changed to except
828+
iterator y(): int {.closure.} =
829+
try:
830+
try:
831+
raise newException(CatchableError, "Error")
832+
except CatchableError:
833+
return 123
834+
yield 0
835+
except:
836+
discard
837+
838+
let w = y
839+
doAssert(w() == 123)
840+
doAssert(getCurrentExceptionMsg() == "")
841+
842+
try:
843+
raise newException(ValueError, "Outer error")
844+
except:
845+
doAssert(getCurrentExceptionMsg() == "Outer error")
846+
let w = y
847+
doAssert(w() == 123)
848+
doAssert(getCurrentExceptionMsg() == "Outer error")
849+
doAssert(getCurrentExceptionMsg() == "")

0 commit comments

Comments
 (0)