@@ -166,13 +166,16 @@ type
166166 tmpResultSym: PSym # Used when we return, but finally has to interfere
167167 finallyPathSym: PSym
168168 curExcLevelSym: PSym # Current exception level (because exceptions are stacked)
169+ curExcSym: PSym # Current exception
170+ externExcSym: PSym # Extern exception: what would getCurrentException() return outside of closure iter
169171
170172 states: seq [State ] # The resulting states. Label is int literal.
171173 finallyPathStack: seq [FinallyTarget ] # Stack of split blocks, whiles and finallies
172174 stateLoopLabel: PSym # Label to break on, when jumping between states.
173175 tempVarId: int # unique name counter
174176 hasExceptions: bool # Does closure have yield in try?
175177 curExcLandingState: PNode
178+ curExceptLevel: int
176179 curFinallyLevel: int
177180 idgen: IdGenerator
178181 varStates: Table [ItemId , int ] # Used to detect if local variable belongs to multiple states
@@ -242,6 +245,12 @@ proc newFinallyPathAssign(ctx: var Ctx, level: int, label: PNode, info: TLineInf
242245 let fp = newFinallyPathAccess (ctx, level, info)
243246 result = newTree (nkAsgn, fp, label)
244247
248+ proc newCurExcAccess (ctx: var Ctx ): PNode =
249+ if ctx.curExcSym.isNil:
250+ let getCurExc = ctx.g.callCodegenProc (" getCurrentException" )
251+ ctx.curExcSym = ctx.newEnvVar (" :curExc" , getCurExc.typ)
252+ ctx.newEnvVarAccess (ctx.curExcSym)
253+
245254proc newCurExcLevelAccess (ctx: var Ctx ): PNode =
246255 if ctx.curExcLevelSym.isNil:
247256 ctx.curExcLevelSym = ctx.newEnvVar (" :curExcLevel" , ctx.g.getSysType (ctx.fn.info, tyInt16))
@@ -284,6 +293,15 @@ proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = n
284293 assert (not typ.isNil, " Temp var needs a type" )
285294 parent.add (ctx.newTempVarDef (result , initialValue))
286295
296+ proc newExternExcAccess (ctx: var Ctx ): PNode =
297+ if ctx.externExcSym == nil :
298+ ctx.externExcSym = newSym (skVar, getIdent (ctx.g.cache, " :externExc" ), ctx.idgen, ctx.fn, ctx.fn.info)
299+ ctx.externExcSym.typ = ctx.curExcSym.typ
300+ newSymNode (ctx.externExcSym, ctx.fn.info)
301+
302+ proc newRestoreExternException (ctx: var Ctx ): PNode =
303+ ctx.g.callCodegenProc (" closureIterSetExc" , ctx.fn.info, ctx.newExternExcAccess ())
304+
287305proc hasYields (n: PNode ): bool =
288306 # TODO : This is very inefficient. It traverses the node, looking for nkYieldStmt.
289307 case n.kind
@@ -298,8 +316,15 @@ proc hasYields(n: PNode): bool =
298316 result = true
299317 break
300318
301- proc newNullifyCurExcLevel (ctx: var Ctx , info: TLineInfo , decrement = false ): PNode =
302- # :curEcx = 0
319+ proc newNullifyCurExc (ctx: var Ctx , info: TLineInfo ): PNode =
320+ # :curEcx = nil
321+ let curExc = ctx.newCurExcAccess ()
322+ curExc.info = info
323+ let nilnode = newNodeIT (nkNilLit, info, getSysType (ctx.g, info, tyNil))
324+ result = newTree (nkAsgn, curExc, nilnode)
325+
326+ proc newNullifyCurExcLevel (ctx: var Ctx , info: TLineInfo ): PNode =
327+ # :curEcxLevel = 0
303328 let curExc = ctx.newCurExcLevelAccess ()
304329 curExc.info = info
305330 let nilnode = ctx.g.newIntLit (info, 0 )
@@ -344,17 +369,20 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
344369 else :
345370 ifBranch = newNodeI (nkElse, c.info)
346371
347- ifBranch.add (newTreeI (nkStmtList, c.info, ctx.newChangeCurExcLevel (c.info, - 1 ), c[^ 1 ]))
372+ ifBranch.add (c[^ 1 ])
373+ # ifBranch.add(newTreeI(nkStmtList, c.info, ctx.newChangeCurExcLevel(c.info, -1), c[^1]))
348374 ifStmt.add (ifBranch)
349375
350376 if ifStmt.len != 0 :
351377 result = newTree (nkStmtList, ifStmt)
378+ # result = newTree(nkStmtList, ctx.newChangeCurExcLevel(n.info, -1), ifStmt)
352379 else :
353380 result = ctx.g.emptyNode
354381
355- proc addElseToExcept (ctx: var Ctx , n, gotoOut: PNode ) =
382+ proc addElseToExcept (ctx: var Ctx , n, gotoOut: PNode ): PNode =
356383 # We should adjust finallyPath to gotoOut if exception is handled
357384 # if there is no finally node next to this except, gotoOut must be nil
385+ result = n
358386 if n.kind == nkStmtList:
359387 if n[0 ].kind == nkIfStmt and n[0 ][^ 1 ].kind != nkElse:
360388 # Not all cases are covered, which means exception is not handled
@@ -377,10 +405,12 @@ proc addElseToExcept(ctx: var Ctx, n, gotoOut: PNode) =
377405 # raised one.
378406 n.add newTree (nkCall,
379407 newSymNode (ctx.g.getCompilerProc (" popCurrentException" )))
408+ n.add ctx.newNullifyCurExc (n.info)
380409 if gotoOut != nil :
381410 # We have a finally node following this except block, and exception is handled
382411 # Configure its path to continue normally
383412 n.add (ctx.newFinallyPathAssign (ctx.curFinallyLevel - 1 , gotoOut[0 ], n.info))
413+
384414
385415proc getFinallyNode (ctx: var Ctx , n: PNode ): PNode =
386416 result = n[^ 1 ]
@@ -837,9 +867,9 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
837867
838868 let excNilCmp = newTreeIT (nkCall,
839869 info, ctx.g.getSysType (info, tyBool),
840- newSymNode (ctx.g.getSysMagic (info, " ==" , mEqI ), info),
841- ctx.newCurExcLevelAccess (),
842- ctx.g. newIntLit ( info, 0 ))
870+ newSymNode (ctx.g.getSysMagic (info, " ==" , mEqRef ), info),
871+ ctx.newCurExcAccess (),
872+ newNodeIT (nkNilLit, info, getSysType ( ctx.g, info, tyNil) ))
843873
844874 let retStmt =
845875 block :
@@ -919,6 +949,7 @@ proc transformReturnStmt(ctx: var Ctx, n: PNode): PNode =
919949
920950 # Returns prevent exception propagation
921951 result .add (ctx.newNullifyCurExcLevel (n.info))
952+ result .add (ctx.newNullifyCurExc (n.info))
922953
923954 var finallyChain = newSeq [PNode ]()
924955
@@ -986,6 +1017,8 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
9861017
9871018 of nkYieldStmt:
9881019 result = addGotoOut (result , gotoOut)
1020+ if ctx.curExceptLevel > 0 or ctx.curFinallyLevel > 0 :
1021+ result = newTree (nkStmtList, ctx.newRestoreExternException (), result )
9891022
9901023 of nkElse, nkElseExpr:
9911024 result [0 ] = addGotoOut (result [0 ], gotoOut)
@@ -1055,7 +1088,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
10551088 result .add (tryLabel)
10561089 var tryBody = toStmtList (n[0 ])
10571090
1058- let exceptBody = ctx.collectExceptState (n)
1091+ var exceptBody = ctx.collectExceptState (n)
10591092 var finallyBody = ctx.getFinallyNode (n)
10601093 var exceptLabel, finallyLabel = ctx.g.emptyNode
10611094
@@ -1094,28 +1127,27 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
10941127 inc ctx.curFinallyLevel
10951128 ctx.finallyPathStack.add (FinallyTarget (n: n[^ 1 ], label: finallyLabel))
10961129
1097- if ctx.transformClosureIteratorBody (tryBody, tryOut) != tryBody:
1098- internalError (ctx.g.config, " transformClosureIteratorBody != tryBody" )
1130+ tryBody = ctx.transformClosureIteratorBody (tryBody, tryOut)
10991131
11001132 if exceptBody.kind != nkEmpty:
1133+ inc ctx.curExceptLevel
11011134 ctx.curExcLandingState = if finallyBody.kind != nkEmpty: finallyLabel
11021135 else : oldExcLandingState
11031136 discard ctx.newState (exceptBody, false , exceptLabel)
11041137
11051138 let normalOut = if finallyBody.kind != nkEmpty: gotoOut else : nil
1106- ctx.addElseToExcept (exceptBody, normalOut)
1139+ exceptBody = ctx.addElseToExcept (exceptBody, normalOut)
11071140 # echo "EXCEPT: ", renderTree(exceptBody)
1108- if ctx.transformClosureIteratorBody (exceptBody, tryOut) != exceptBody:
1109- internalError ( ctx.g.config, " transformClosureIteratorBody != exceptBody " )
1141+ exceptBody = ctx.transformClosureIteratorBody (exceptBody, tryOut)
1142+ inc ctx.curExceptLevel
11101143
11111144 ctx.curExcLandingState = oldExcLandingState
11121145
11131146 if finallyBody.kind != nkEmpty:
11141147 discard ctx.finallyPathStack.pop ()
11151148 discard ctx.newState (finallyBody, false , finallyLabel)
11161149 let finallyExit = newTree (nkGotoState, ctx.newFinallyPathAccess (ctx.curFinallyLevel - 1 , finallyBody.info))
1117- if ctx.transformClosureIteratorBody (finallyBody, finallyExit) != finallyBody:
1118- internalError (ctx.g.config, " transformClosureIteratorBody != finallyBody" )
1150+ finallyBody = ctx.transformClosureIteratorBody (finallyBody, finallyExit)
11191151 dec ctx.curFinallyLevel
11201152
11211153 of nkGotoState, nkForStmt:
@@ -1209,25 +1241,16 @@ proc newExceptBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
12091241 result = newNodeI (nkStmtList, info)
12101242
12111243 let intTyp = ctx.g.getSysType (info, tyInt)
1212- let boolTyp = ctx.g.getSysType (info, tyBool)
12131244
12141245 # :state = exceptionTable[:state]
12151246 result .add ctx.newStateAssgn (
12161247 newTreeIT (nkBracketExpr, info, intTyp,
12171248 ctx.createExceptionTable (),
12181249 ctx.newStateAccess ()))
12191250
1220- # if :state == 0: raise
1221- block :
1222- let cond = newTreeIT (nkCall, info, boolTyp,
1223- ctx.g.getSysMagic (info, " ==" , mEqI).newSymNode (),
1224- ctx.newStateAccess (),
1225- newIntTypeNode (0 , intTyp))
1251+ let getCurExc = ctx.g.callCodegenProc (" getCurrentException" )
1252+ result .add newTree (nkFastAsgn, ctx.newCurExcAccess (), getCurExc)
12261253
1227- let raiseStmt = newTree (nkRaiseStmt, ctx.g.emptyNode)
1228- let ifBranch = newTree (nkElifBranch, cond, raiseStmt)
1229- let ifStmt = newTree (nkIfStmt, ifBranch)
1230- result .add (ifStmt)
12311254
12321255proc wrapIntoTryExcept (ctx: var Ctx , n: PNode ): PNode {.inline .} =
12331256 # Generates code:
@@ -1236,11 +1259,12 @@ proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
12361259 # body
12371260 # except:
12381261 # :state = exceptionTable[:state]
1239- # if :state == 0:
1240- # raise
1241- # :tmp = getCurrentException()
1262+ # :curExc = getCurrentException()
1263+ # if :state == 0:
1264+ # raise
12421265 #
1243- # pushCurrentException(:tmp)
1266+ # pushCurrentException(:curExc)
1267+ # inc :curExcLevel
12441268
12451269 let tryBody = newTree (nkStmtList, n)
12461270 let exceptBody = ctx.newExceptBody (ctx.fn.info)
@@ -1251,8 +1275,25 @@ proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
12511275 let tempExc = ctx.newTempVar (getCurExc.typ, result )
12521276 result .add newTree (nkTryStmt, tryBody, exceptBranch)
12531277 exceptBody.add ctx.newTempVarAsgn (tempExc, getCurExc)
1278+ # exceptBody.add newTree(nkFastAsgn, ctx.newCurExcAccess(), getCurExc)
1279+
1280+ # if :state == 0: raise
1281+ block :
1282+ let boolTyp = ctx.g.getSysType (ctx.fn.info, tyBool)
1283+ let intTyp = ctx.g.getSysType (ctx.fn.info, tyInt)
1284+ let cond = newTreeIT (nkCall, ctx.fn.info, boolTyp,
1285+ ctx.g.getSysMagic (ctx.fn.info, " ==" , mEqI).newSymNode (),
1286+ ctx.newStateAccess (),
1287+ newIntTypeNode (0 , intTyp))
1288+
1289+ let raiseStmt = newTree (nkRaiseStmt, ctx.newCurExcAccess ())
1290+ let ifBody = newTree (nkStmtList, ctx.newRestoreExternException (), raiseStmt)
1291+ let ifBranch = newTree (nkElifBranch, cond, ifBody)
1292+ let ifStmt = newTree (nkIfStmt, ifBranch)
1293+ result .add (ifStmt)
12541294
1255- result .add newTree (nkCall, newSymNode (ctx.g.getCompilerProc (" pushCurrentException" )), ctx.newTempVarAccess (tempExc))
1295+ # result.add newTree(nkCall, newSymNode(ctx.g.getCompilerProc("pushCurrentException")), ctx.newTempVarAccess(tempExc))
1296+ result .add newTree (nkCall, newSymNode (ctx.g.getCompilerProc (" pushCurrentException" )), ctx.newCurExcAccess ())
12561297 result .add ctx.newChangeCurExcLevel (n.info, 1 )
12571298
12581299proc wrapIntoStateLoop (ctx: var Ctx , n: PNode ): PNode =
@@ -1276,6 +1317,17 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
12761317 blockStmt.add (blockBody)
12771318 loopBody.add (blockStmt)
12781319
1320+ if ctx.hasExceptions:
1321+ let res = newNodeI (nkStmtList, n.info)
1322+ let getCurExc = ctx.g.callCodegenProc (" getCurrentException" )
1323+ discard ctx.newExternExcAccess ()
1324+ res.add (ctx.newTempVarDef (ctx.externExcSym, getCurExc))
1325+ # let externExc = ctx.newTempVar(getCurExc.typ, res, getCurExc)
1326+ let setCurExc = ctx.g.callCodegenProc (" closureIterSetExc" , n.info, ctx.newCurExcAccess ())
1327+ res.add (setCurExc)
1328+ res.add (result )
1329+ result = res
1330+
12791331proc countStateOccurences (ctx: var Ctx , n: PNode , stateOccurences: var openArray [int ]) =
12801332 # # Find all nkGotoState(stateIdx) nodes that do not follow nkYield.
12811333 # # For every such node increment stateOccurences[stateIdx]
@@ -1381,7 +1433,7 @@ proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
13811433 case n.kind
13821434 of nkSym:
13831435 let s = n.sym
1384- if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn:
1436+ if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym :
13851437 let vs = c.varStates.getOrDefault (s.itemId, localNotSeen)
13861438 if vs == localNotSeen: # First seing this variable
13871439 c.varStates[s.itemId] = stateIdx
@@ -1458,7 +1510,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
14581510 # echo "transformed into ", n
14591511
14601512 discard ctx.newState (n, false , nil )
1461- let gotoOut = newTree (nkGotoState, g.newIntLit (n.info, - 1 ))
1513+
1514+ let finalState = ctx.newStateLabel ()
1515+ let gotoOut = newTree (nkGotoState, finalState)
14621516
14631517 var ns = false
14641518 n = ctx.lowerStmtListExprs (n, ns)
@@ -1470,6 +1524,12 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
14701524 # Splitting transformation
14711525 discard ctx.transformClosureIteratorBody (n, gotoOut)
14721526
1527+ let finalStateBody = newTree (nkStmtList)
1528+ if ctx.hasExceptions:
1529+ finalStateBody.add (ctx.newRestoreExternException ())
1530+ finalStateBody.add (newTree (nkGotoState, g.newIntLit (n.info, - 1 )))
1531+ discard ctx.newState (finalStateBody, true , finalState)
1532+
14731533 # Assign state label indexes
14741534 for i in 0 .. ctx.states.high:
14751535 ctx.states[i].label.intVal = i
0 commit comments