closureiters.nim 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2018 Nim Contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This file implements closure iterator transformations.
  10. # The main idea is to split the closure iterator body to top level statements.
  11. # The body is split by yield statement.
  12. #
  13. # Example:
  14. # while a > 0:
  15. # echo "hi"
  16. # yield a
  17. # dec a
  18. #
  19. # Should be transformed to:
  20. # STATE0:
  21. # if a > 0:
  22. # echo "hi"
  23. # :state = 1 # Next state
  24. # return a # yield
  25. # else:
  26. # :state = 2 # Next state
  27. # break :stateLoop # Proceed to the next state
  28. # STATE1:
  29. # dec a
  30. # :state = 0 # Next state
  31. # break :stateLoop # Proceed to the next state
  32. # STATE2:
  33. # :state = -1 # End of execution
  34. # The transformation should play well with lambdalifting, however depending
  35. # on situation, it can be called either before or after lambdalifting
  36. # transformation. As such we behave slightly differently, when accessing
  37. # iterator state, or using temp variables. If lambdalifting did not happen,
  38. # we just create local variables, so that they will be lifted further on.
  39. # Otherwise, we utilize existing env, created by lambdalifting.
  40. # Lambdalifting treats :state variable specially, it should always end up
  41. # as the first field in env. Currently C codegen depends on this behavior.
  42. # One special subtransformation is nkStmtListExpr lowering.
  43. # Example:
  44. # template foo(): int =
  45. # yield 1
  46. # 2
  47. #
  48. # iterator it(): int {.closure.} =
  49. # if foo() == 2:
  50. # yield 3
  51. #
  52. # If a nkStmtListExpr has yield inside, it has first to be lowered to:
  53. # yield 1
  54. # :tmpSlLower = 2
  55. # if :tmpSlLower == 2:
  56. # yield 3
  57. # nkTryStmt Transformations:
  58. # If the iter has an nkTryStmt with a yield inside
  59. # - the closure iter is promoted to have exceptions (ctx.hasExceptions = true)
  60. # - exception table is created. This is a const array, where
  61. # `abs(exceptionTable[i])` is a state idx to which we should jump from state
  62. # `i` should exception be raised in state `i`. For all states in `try` block
  63. # the target state is `except` block. For all states in `except` block
  64. # the target state is `finally` block. For all other states there is no
  65. # target state (0, as the first block can never be neither except nor finally).
  66. # `exceptionTable[i]` is < 0 if `abs(exceptionTable[i])` is except block,
  67. # and > 0, for finally block.
  68. # - local variable :curExc is created
  69. # - the iter body is wrapped into a
  70. # try:
  71. # closureIterSetupExc(:curExc)
  72. # ...body...
  73. # catch:
  74. # :state = exceptionTable[:state]
  75. # if :state == 0: raise # No state that could handle exception
  76. # :unrollFinally = :state > 0 # Target state is finally
  77. # if :state < 0:
  78. # :state = -:state
  79. # :curExc = getCurrentException()
  80. #
  81. # nkReturnStmt within a try/except/finally now has to behave differently as we
  82. # want the nearest finally block to be executed before the return, thus it is
  83. # transformed to:
  84. # :tmpResult = returnValue (if return doesn't have a value, this is skipped)
  85. # :unrollFinally = true
  86. # goto nearestFinally (or -1 if not exists)
  87. #
  88. # Example:
  89. #
  90. # try:
  91. # yield 0
  92. # raise ...
  93. # except:
  94. # yield 1
  95. # return 3
  96. # finally:
  97. # yield 2
  98. #
  99. # Is transformed to (yields are left in place for example simplicity,
  100. # in reality the code is subdivided even more, as described above):
  101. #
  102. # STATE0: # Try
  103. # yield 0
  104. # raise ...
  105. # :state = 2 # What would happen should we not raise
  106. # break :stateLoop
  107. # STATE1: # Except
  108. # yield 1
  109. # :tmpResult = 3 # Return
  110. # :unrollFinally = true # Return
  111. # :state = 2 # Goto Finally
  112. # break :stateLoop
  113. # :state = 2 # What would happen should we not return
  114. # break :stateLoop
  115. # STATE2: # Finally
  116. # yield 2
  117. # if :unrollFinally: # This node is created by `newEndFinallyNode`
  118. # if :curExc.isNil:
  119. # return :tmpResult
  120. # else:
  121. # closureIterSetupExc(nil)
  122. # raise
  123. # state = -1 # Goto next state. In this case we just exit
  124. # break :stateLoop
  125. import
  126. ast, msgs, idents,
  127. renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
  128. type
  129. Ctx = object
  130. g: ModuleGraph
  131. fn: PSym
  132. stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
  133. tmpResultSym: PSym # Used when we return, but finally has to interfere
  134. unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return)
  135. curExcSym: PSym # Current exception
  136. states: seq[PNode] # The resulting states. Every state is an nkState node.
  137. blockLevel: int # Temp used to transform break and continue stmts
  138. stateLoopLabel: PSym # Label to break on, when jumping between states.
  139. exitStateIdx: int # index of the last state
  140. tempVarId: int # unique name counter
  141. tempVars: PNode # Temp var decls, nkVarSection
  142. exceptionTable: seq[int] # For state `i` jump to state `exceptionTable[i]` if exception is raised
  143. hasExceptions: bool # Does closure have yield in try?
  144. curExcHandlingState: int # Negative for except, positive for finally
  145. nearestFinally: int # Index of the nearest finally block. For try/except it
  146. # is their finally. For finally it is parent finally. Otherwise -1
  147. const
  148. nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
  149. nkCommentStmt} + procDefs
  150. proc newStateAccess(ctx: var Ctx): PNode =
  151. if ctx.stateVarSym.isNil:
  152. result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
  153. getStateField(ctx.g, ctx.fn), ctx.fn.info)
  154. else:
  155. result = newSymNode(ctx.stateVarSym)
  156. proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode =
  157. # Creates state assignment:
  158. # :state = toValue
  159. newTree(nkAsgn, ctx.newStateAccess(), toValue)
  160. proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
  161. # Creates state assignment:
  162. # :state = stateNo
  163. ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
  164. proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
  165. result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.fn, ctx.fn.info)
  166. result.typ = typ
  167. assert(not typ.isNil)
  168. if not ctx.stateVarSym.isNil:
  169. # We haven't gone through labmda lifting yet, so just create a local var,
  170. # it will be lifted later
  171. if ctx.tempVars.isNil:
  172. ctx.tempVars = newNodeI(nkVarSection, ctx.fn.info)
  173. addVar(ctx.tempVars, newSymNode(result))
  174. else:
  175. let envParam = getEnvParam(ctx.fn)
  176. # let obj = envParam.typ.lastSon
  177. result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache)
  178. proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
  179. if ctx.stateVarSym.isNil:
  180. result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info)
  181. else:
  182. result = newSymNode(s)
  183. proc newTmpResultAccess(ctx: var Ctx): PNode =
  184. if ctx.tmpResultSym.isNil:
  185. ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0])
  186. ctx.newEnvVarAccess(ctx.tmpResultSym)
  187. proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
  188. if ctx.unrollFinallySym.isNil:
  189. ctx.unrollFinallySym = ctx.newEnvVar(":unrollFinally", ctx.g.getSysType(info, tyBool))
  190. ctx.newEnvVarAccess(ctx.unrollFinallySym)
  191. proc newCurExcAccess(ctx: var Ctx): PNode =
  192. if ctx.curExcSym.isNil:
  193. ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException").typ)
  194. ctx.newEnvVarAccess(ctx.curExcSym)
  195. proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
  196. # Creates a new state, adds it to the context fills out `gotoOut` so that it
  197. # will goto this state.
  198. # Returns index of the newly created state
  199. result = ctx.states.len
  200. let resLit = ctx.g.newIntLit(n.info, result)
  201. let s = newNodeI(nkState, n.info)
  202. s.add(resLit)
  203. s.add(n)
  204. ctx.states.add(s)
  205. ctx.exceptionTable.add(ctx.curExcHandlingState)
  206. if not gotoOut.isNil:
  207. assert(gotoOut.len == 0)
  208. gotoOut.add(ctx.g.newIntLit(gotoOut.info, result))
  209. proc toStmtList(n: PNode): PNode =
  210. result = n
  211. if result.kind notin {nkStmtList, nkStmtListExpr}:
  212. result = newNodeI(nkStmtList, n.info)
  213. result.add(n)
  214. proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
  215. # Make sure `n` is a stmtlist, and ends with `gotoOut`
  216. result = toStmtList(n)
  217. if result.len == 0 or result[^1].kind != nkGotoState:
  218. result.add(gotoOut)
  219. proc newTempVar(ctx: var Ctx, typ: PType): PSym =
  220. result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
  221. inc ctx.tempVarId
  222. proc hasYields(n: PNode): bool =
  223. # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
  224. case n.kind
  225. of nkYieldStmt:
  226. result = true
  227. of nkSkip:
  228. discard
  229. else:
  230. for c in n:
  231. if c.hasYields:
  232. result = true
  233. break
  234. proc transformBreaksAndContinuesInWhile(ctx: var Ctx, n: PNode, before, after: PNode): PNode =
  235. result = n
  236. case n.kind
  237. of nkSkip:
  238. discard
  239. of nkWhileStmt: discard # Do not recurse into nested whiles
  240. of nkContinueStmt:
  241. result = before
  242. of nkBlockStmt:
  243. inc ctx.blockLevel
  244. result[1] = ctx.transformBreaksAndContinuesInWhile(result[1], before, after)
  245. dec ctx.blockLevel
  246. of nkBreakStmt:
  247. if ctx.blockLevel == 0:
  248. result = after
  249. else:
  250. for i in 0..<n.len:
  251. n[i] = ctx.transformBreaksAndContinuesInWhile(n[i], before, after)
  252. proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode =
  253. result = n
  254. case n.kind
  255. of nkSkip:
  256. discard
  257. of nkBlockStmt, nkWhileStmt:
  258. inc ctx.blockLevel
  259. result[1] = ctx.transformBreaksInBlock(result[1], label, after)
  260. dec ctx.blockLevel
  261. of nkBreakStmt:
  262. if n[0].kind == nkEmpty:
  263. if ctx.blockLevel == 0:
  264. result = after
  265. else:
  266. if label.kind == nkSym and n[0].sym == label.sym:
  267. result = after
  268. else:
  269. for i in 0..<n.len:
  270. n[i] = ctx.transformBreaksInBlock(n[i], label, after)
  271. proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
  272. # :curEcx = nil
  273. let curExc = ctx.newCurExcAccess()
  274. curExc.info = info
  275. let nilnode = newNode(nkNilLit)
  276. nilnode.typ = curExc.typ
  277. result = newTree(nkAsgn, curExc, nilnode)
  278. proc newOr(g: ModuleGraph, a, b: PNode): PNode {.inline.} =
  279. result = newTree(nkCall, newSymNode(g.getSysMagic(a.info, "or", mOr)), a, b)
  280. result.typ = g.getSysType(a.info, tyBool)
  281. result.info = a.info
  282. proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
  283. var ifStmt = newNodeI(nkIfStmt, n.info)
  284. let g = ctx.g
  285. for c in n:
  286. if c.kind == nkExceptBranch:
  287. var ifBranch: PNode
  288. if c.len > 1:
  289. var cond: PNode
  290. for i in 0..<c.len - 1:
  291. assert(c[i].kind == nkType)
  292. let nextCond = newTree(nkCall,
  293. newSymNode(g.getSysMagic(c.info, "of", mOf)),
  294. g.callCodegenProc("getCurrentException"),
  295. c[i])
  296. nextCond.typ = ctx.g.getSysType(c.info, tyBool)
  297. nextCond.info = c.info
  298. if cond.isNil:
  299. cond = nextCond
  300. else:
  301. cond = g.newOr(cond, nextCond)
  302. ifBranch = newNodeI(nkElifBranch, c.info)
  303. ifBranch.add(cond)
  304. else:
  305. if ifStmt.len == 0:
  306. ifStmt = newNodeI(nkStmtList, c.info)
  307. ifBranch = newNodeI(nkStmtList, c.info)
  308. else:
  309. ifBranch = newNodeI(nkElse, c.info)
  310. ifBranch.add(c[^1])
  311. ifStmt.add(ifBranch)
  312. if ifStmt.len != 0:
  313. result = newTree(nkStmtList, ctx.newNullifyCurExc(n.info), ifStmt)
  314. else:
  315. result = ctx.g.emptyNode
  316. proc addElseToExcept(ctx: var Ctx, n: PNode) =
  317. if n.kind == nkStmtList and n[1].kind == nkIfStmt and n[1][^1].kind != nkElse:
  318. # Not all cases are covered
  319. let branchBody = newNodeI(nkStmtList, n.info)
  320. block: # :unrollFinally = true
  321. branchBody.add(newTree(nkAsgn,
  322. ctx.newUnrollFinallyAccess(n.info),
  323. newIntTypeNode(1, ctx.g.getSysType(n.info, tyBool))))
  324. block: # :curExc = getCurrentException()
  325. branchBody.add(newTree(nkAsgn,
  326. ctx.newCurExcAccess(),
  327. ctx.g.callCodegenProc("getCurrentException")))
  328. block: # goto nearestFinally
  329. branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally)))
  330. let elseBranch = newTree(nkElse, branchBody)
  331. n[1].add(elseBranch)
  332. proc getFinallyNode(ctx: var Ctx, n: PNode): PNode =
  333. result = n[^1]
  334. if result.kind == nkFinally:
  335. result = result[0]
  336. else:
  337. result = ctx.g.emptyNode
  338. proc hasYieldsInExpressions(n: PNode): bool =
  339. case n.kind
  340. of nkSkip:
  341. discard
  342. of nkStmtListExpr:
  343. if isEmptyType(n.typ):
  344. for c in n:
  345. if c.hasYieldsInExpressions:
  346. return true
  347. else:
  348. result = n.hasYields
  349. of nkCast:
  350. for i in 1..<n.len:
  351. if n[i].hasYieldsInExpressions:
  352. return true
  353. else:
  354. for c in n:
  355. if c.hasYieldsInExpressions:
  356. return true
  357. proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
  358. assert(n.kind == nkStmtListExpr)
  359. result.s = newNodeI(nkStmtList, n.info)
  360. result.s.sons = @[]
  361. var n = n
  362. while n.kind == nkStmtListExpr:
  363. result.s.sons.add(n.sons)
  364. result.s.sons.setLen(result.s.len - 1) # delete last son
  365. n = n[^1]
  366. result.res = n
  367. proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
  368. result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
  369. result.info = v.info
  370. proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
  371. if input.kind == nkStmtListExpr:
  372. let (st, res) = exprToStmtList(input)
  373. output.add(st)
  374. output.add(ctx.newEnvVarAsgn(sym, res))
  375. else:
  376. output.add(ctx.newEnvVarAsgn(sym, input))
  377. proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
  378. result = newNodeI(nkStmtList, exprBody.info)
  379. ctx.addExprAssgn(result, exprBody, res)
  380. proc newNotCall(g: ModuleGraph; e: PNode): PNode =
  381. result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
  382. result.typ = g.getSysType(e.info, tyBool)
  383. proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
  384. result = n
  385. case n.kind
  386. of nkSkip:
  387. discard
  388. of nkYieldStmt:
  389. var ns = false
  390. for i in 0..<n.len:
  391. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  392. if ns:
  393. result = newNodeI(nkStmtList, n.info)
  394. let (st, ex) = exprToStmtList(n[0])
  395. result.add(st)
  396. n[0] = ex
  397. result.add(n)
  398. needsSplit = true
  399. of nkPar, nkObjConstr, nkTupleConstr, nkBracket:
  400. var ns = false
  401. for i in 0..<n.len:
  402. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  403. if ns:
  404. needsSplit = true
  405. result = newNodeI(nkStmtListExpr, n.info)
  406. if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil")
  407. result.typ = n.typ
  408. for i in 0..<n.len:
  409. case n[i].kind
  410. of nkExprColonExpr:
  411. if n[i][1].kind == nkStmtListExpr:
  412. let (st, ex) = exprToStmtList(n[i][1])
  413. result.add(st)
  414. n[i][1] = ex
  415. of nkStmtListExpr:
  416. let (st, ex) = exprToStmtList(n[i])
  417. result.add(st)
  418. n[i] = ex
  419. else: discard
  420. result.add(n)
  421. of nkIfStmt, nkIfExpr:
  422. var ns = false
  423. for i in 0..<n.len:
  424. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  425. if ns:
  426. needsSplit = true
  427. var tmp: PSym
  428. var s: PNode
  429. let isExpr = not isEmptyType(n.typ)
  430. if isExpr:
  431. tmp = ctx.newTempVar(n.typ)
  432. result = newNodeI(nkStmtListExpr, n.info)
  433. result.typ = n.typ
  434. else:
  435. result = newNodeI(nkStmtList, n.info)
  436. var curS = result
  437. for branch in n:
  438. case branch.kind
  439. of nkElseExpr, nkElse:
  440. if isExpr:
  441. let branchBody = newNodeI(nkStmtList, branch.info)
  442. ctx.addExprAssgn(branchBody, branch[0], tmp)
  443. let newBranch = newTree(nkElse, branchBody)
  444. curS.add(newBranch)
  445. else:
  446. curS.add(branch)
  447. of nkElifExpr, nkElifBranch:
  448. var newBranch: PNode
  449. if branch[0].kind == nkStmtListExpr:
  450. let (st, res) = exprToStmtList(branch[0])
  451. let elseBody = newTree(nkStmtList, st)
  452. newBranch = newTree(nkElifBranch, res, branch[1])
  453. let newIf = newTree(nkIfStmt, newBranch)
  454. elseBody.add(newIf)
  455. if curS.kind == nkIfStmt:
  456. let newElse = newNodeI(nkElse, branch.info)
  457. newElse.add(elseBody)
  458. curS.add(newElse)
  459. else:
  460. curS.add(elseBody)
  461. curS = newIf
  462. else:
  463. newBranch = branch
  464. if curS.kind == nkIfStmt:
  465. curS.add(newBranch)
  466. else:
  467. let newIf = newTree(nkIfStmt, newBranch)
  468. curS.add(newIf)
  469. curS = newIf
  470. if isExpr:
  471. let branchBody = newNodeI(nkStmtList, branch[1].info)
  472. ctx.addExprAssgn(branchBody, branch[1], tmp)
  473. newBranch[1] = branchBody
  474. else:
  475. internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind)
  476. if isExpr: result.add(ctx.newEnvVarAccess(tmp))
  477. of nkTryStmt, nkHiddenTryStmt:
  478. var ns = false
  479. for i in 0..<n.len:
  480. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  481. if ns:
  482. needsSplit = true
  483. let isExpr = not isEmptyType(n.typ)
  484. if isExpr:
  485. result = newNodeI(nkStmtListExpr, n.info)
  486. result.typ = n.typ
  487. let tmp = ctx.newTempVar(n.typ)
  488. n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
  489. for i in 1..<n.len:
  490. let branch = n[i]
  491. case branch.kind
  492. of nkExceptBranch:
  493. if branch[0].kind == nkType:
  494. branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp)
  495. else:
  496. branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp)
  497. of nkFinally:
  498. discard
  499. else:
  500. internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind)
  501. result.add(n)
  502. result.add(ctx.newEnvVarAccess(tmp))
  503. of nkCaseStmt:
  504. var ns = false
  505. for i in 0..<n.len:
  506. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  507. if ns:
  508. needsSplit = true
  509. let isExpr = not isEmptyType(n.typ)
  510. if isExpr:
  511. let tmp = ctx.newTempVar(n.typ)
  512. result = newNodeI(nkStmtListExpr, n.info)
  513. result.typ = n.typ
  514. if n[0].kind == nkStmtListExpr:
  515. let (st, ex) = exprToStmtList(n[0])
  516. result.add(st)
  517. n[0] = ex
  518. for i in 1..<n.len:
  519. let branch = n[i]
  520. case branch.kind
  521. of nkOfBranch:
  522. branch[^1] = ctx.convertExprBodyToAsgn(branch[^1], tmp)
  523. of nkElse:
  524. branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp)
  525. else:
  526. internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
  527. result.add(n)
  528. result.add(ctx.newEnvVarAccess(tmp))
  529. of nkCallKinds, nkChckRange, nkChckRangeF, nkChckRange64:
  530. var ns = false
  531. for i in 0..<n.len:
  532. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  533. if ns:
  534. needsSplit = true
  535. let isExpr = not isEmptyType(n.typ)
  536. if isExpr:
  537. result = newNodeI(nkStmtListExpr, n.info)
  538. result.typ = n.typ
  539. else:
  540. result = newNodeI(nkStmtList, n.info)
  541. if n[0].kind == nkSym and n[0].sym.magic in {mAnd, mOr}: # `and`/`or` short cirquiting
  542. var cond = n[1]
  543. if cond.kind == nkStmtListExpr:
  544. let (st, ex) = exprToStmtList(cond)
  545. result.add(st)
  546. cond = ex
  547. let tmp = ctx.newTempVar(cond.typ)
  548. result.add(ctx.newEnvVarAsgn(tmp, cond))
  549. var check = ctx.newEnvVarAccess(tmp)
  550. if n[0].sym.magic == mOr:
  551. check = ctx.g.newNotCall(check)
  552. cond = n[2]
  553. let ifBody = newNodeI(nkStmtList, cond.info)
  554. if cond.kind == nkStmtListExpr:
  555. let (st, ex) = exprToStmtList(cond)
  556. ifBody.add(st)
  557. cond = ex
  558. ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
  559. let ifBranch = newTree(nkElifBranch, check, ifBody)
  560. let ifNode = newTree(nkIfStmt, ifBranch)
  561. result.add(ifNode)
  562. result.add(ctx.newEnvVarAccess(tmp))
  563. else:
  564. for i in 0..<n.len:
  565. if n[i].kind == nkStmtListExpr:
  566. let (st, ex) = exprToStmtList(n[i])
  567. result.add(st)
  568. n[i] = ex
  569. if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking
  570. let tmp = ctx.newTempVar(n[i].typ)
  571. result.add(ctx.newEnvVarAsgn(tmp, n[i]))
  572. n[i] = ctx.newEnvVarAccess(tmp)
  573. result.add(n)
  574. of nkVarSection, nkLetSection:
  575. result = newNodeI(nkStmtList, n.info)
  576. for c in n:
  577. let varSect = newNodeI(n.kind, n.info)
  578. varSect.add(c)
  579. var ns = false
  580. c[^1] = ctx.lowerStmtListExprs(c[^1], ns)
  581. if ns:
  582. needsSplit = true
  583. let (st, ex) = exprToStmtList(c[^1])
  584. result.add(st)
  585. c[^1] = ex
  586. result.add(varSect)
  587. of nkDiscardStmt, nkReturnStmt, nkRaiseStmt:
  588. var ns = false
  589. for i in 0..<n.len:
  590. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  591. if ns:
  592. needsSplit = true
  593. result = newNodeI(nkStmtList, n.info)
  594. let (st, ex) = exprToStmtList(n[0])
  595. result.add(st)
  596. n[0] = ex
  597. result.add(n)
  598. of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv,
  599. nkDerefExpr, nkHiddenDeref:
  600. var ns = false
  601. for i in ord(n.kind == nkCast)..<n.len:
  602. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  603. if ns:
  604. needsSplit = true
  605. result = newNodeI(nkStmtListExpr, n.info)
  606. result.typ = n.typ
  607. let (st, ex) = exprToStmtList(n[^1])
  608. result.add(st)
  609. n[^1] = ex
  610. result.add(n)
  611. of nkAsgn, nkFastAsgn:
  612. var ns = false
  613. for i in 0..<n.len:
  614. n[i] = ctx.lowerStmtListExprs(n[i], ns)
  615. if ns:
  616. needsSplit = true
  617. result = newNodeI(nkStmtList, n.info)
  618. if n[0].kind == nkStmtListExpr:
  619. let (st, ex) = exprToStmtList(n[0])
  620. result.add(st)
  621. n[0] = ex
  622. if n[1].kind == nkStmtListExpr:
  623. let (st, ex) = exprToStmtList(n[1])
  624. result.add(st)
  625. n[1] = ex
  626. result.add(n)
  627. of nkBracketExpr:
  628. var lhsNeedsSplit = false
  629. var rhsNeedsSplit = false
  630. n[0] = ctx.lowerStmtListExprs(n[0], lhsNeedsSplit)
  631. n[1] = ctx.lowerStmtListExprs(n[1], rhsNeedsSplit)
  632. if lhsNeedsSplit or rhsNeedsSplit:
  633. needsSplit = true
  634. result = newNodeI(nkStmtListExpr, n.info)
  635. if lhsNeedsSplit:
  636. let (st, ex) = exprToStmtList(n[0])
  637. result.add(st)
  638. n[0] = ex
  639. if rhsNeedsSplit:
  640. let (st, ex) = exprToStmtList(n[1])
  641. result.add(st)
  642. n[1] = ex
  643. result.add(n)
  644. of nkWhileStmt:
  645. var ns = false
  646. var condNeedsSplit = false
  647. n[0] = ctx.lowerStmtListExprs(n[0], condNeedsSplit)
  648. var bodyNeedsSplit = false
  649. n[1] = ctx.lowerStmtListExprs(n[1], bodyNeedsSplit)
  650. if condNeedsSplit or bodyNeedsSplit:
  651. needsSplit = true
  652. if condNeedsSplit:
  653. let (st, ex) = exprToStmtList(n[0])
  654. let brk = newTree(nkBreakStmt, ctx.g.emptyNode)
  655. let branch = newTree(nkElifBranch, ctx.g.newNotCall(ex), brk)
  656. let check = newTree(nkIfStmt, branch)
  657. let newBody = newTree(nkStmtList, st, check, n[1])
  658. n[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true"))
  659. n[1] = newBody
  660. of nkDotExpr, nkCheckedFieldExpr:
  661. var ns = false
  662. n[0] = ctx.lowerStmtListExprs(n[0], ns)
  663. if ns:
  664. needsSplit = true
  665. result = newNodeI(nkStmtListExpr, n.info)
  666. result.typ = n.typ
  667. let (st, ex) = exprToStmtList(n[0])
  668. result.add(st)
  669. n[0] = ex
  670. result.add(n)
  671. of nkBlockExpr:
  672. var ns = false
  673. n[1] = ctx.lowerStmtListExprs(n[1], ns)
  674. if ns:
  675. needsSplit = true
  676. result = newNodeI(nkStmtListExpr, n.info)
  677. result.typ = n.typ
  678. let (st, ex) = exprToStmtList(n[1])
  679. n.transitionSonsKind(nkBlockStmt)
  680. n.typ = nil
  681. n[1] = st
  682. result.add(n)
  683. result.add(ex)
  684. else:
  685. for i in 0..<n.len:
  686. n[i] = ctx.lowerStmtListExprs(n[i], needsSplit)
  687. proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
  688. # Generate the following code:
  689. # if :unrollFinally:
  690. # if :curExc.isNil:
  691. # return :tmpResult
  692. # else:
  693. # raise
  694. let curExc = ctx.newCurExcAccess()
  695. let nilnode = newNode(nkNilLit)
  696. nilnode.typ = curExc.typ
  697. let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
  698. cmp.typ = ctx.g.getSysType(info, tyBool)
  699. let asgn = newTree(nkFastAsgn,
  700. newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
  701. ctx.newTmpResultAccess())
  702. let retStmt = newTree(nkReturnStmt, asgn)
  703. let branch = newTree(nkElifBranch, cmp, retStmt)
  704. let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
  705. nullifyExc.info = info
  706. let raiseStmt = newTree(nkRaiseStmt, curExc)
  707. raiseStmt.info = info
  708. let elseBranch = newTree(nkElse, newTree(nkStmtList, nullifyExc, raiseStmt))
  709. let ifBody = newTree(nkIfStmt, branch, elseBranch)
  710. let elifBranch = newTree(nkElifBranch, ctx.newUnrollFinallyAccess(info), ifBody)
  711. elifBranch.info = info
  712. result = newTree(nkIfStmt, elifBranch)
  713. proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
  714. result = n
  715. # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
  716. case n.kind
  717. of nkReturnStmt:
  718. # We're somewhere in try, transform to finally unrolling
  719. assert(ctx.nearestFinally != 0)
  720. result = newNodeI(nkStmtList, n.info)
  721. block: # :unrollFinally = true
  722. let asgn = newNodeI(nkAsgn, n.info)
  723. asgn.add(ctx.newUnrollFinallyAccess(n.info))
  724. asgn.add(newIntTypeNode(1, ctx.g.getSysType(n.info, tyBool)))
  725. result.add(asgn)
  726. if n[0].kind != nkEmpty:
  727. let asgnTmpResult = newNodeI(nkAsgn, n.info)
  728. asgnTmpResult.add(ctx.newTmpResultAccess())
  729. let x = if n[0].kind in {nkAsgn, nkFastAsgn}: n[0][1] else: n[0]
  730. asgnTmpResult.add(x)
  731. result.add(asgnTmpResult)
  732. result.add(ctx.newNullifyCurExc(n.info))
  733. let goto = newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally))
  734. result.add(goto)
  735. of nkSkip:
  736. discard
  737. else:
  738. for i in 0..<n.len:
  739. n[i] = ctx.transformReturnsInTry(n[i])
  740. proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode =
  741. result = n
  742. case n.kind
  743. of nkSkip: discard
  744. of nkStmtList, nkStmtListExpr:
  745. result = addGotoOut(result, gotoOut)
  746. for i in 0..<n.len:
  747. if n[i].hasYields:
  748. # Create a new split
  749. let go = newNodeI(nkGotoState, n[i].info)
  750. n[i] = ctx.transformClosureIteratorBody(n[i], go)
  751. let s = newNodeI(nkStmtList, n[i + 1].info)
  752. for j in i + 1..<n.len:
  753. s.add(n[j])
  754. n.sons.setLen(i + 1)
  755. discard ctx.newState(s, go)
  756. if ctx.transformClosureIteratorBody(s, gotoOut) != s:
  757. internalError(ctx.g.config, "transformClosureIteratorBody != s")
  758. break
  759. of nkYieldStmt:
  760. result = newNodeI(nkStmtList, n.info)
  761. result.add(n)
  762. result.add(gotoOut)
  763. of nkElse, nkElseExpr:
  764. result[0] = addGotoOut(result[0], gotoOut)
  765. result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut)
  766. of nkElifBranch, nkElifExpr, nkOfBranch:
  767. result[^1] = addGotoOut(result[^1], gotoOut)
  768. result[^1] = ctx.transformClosureIteratorBody(result[^1], gotoOut)
  769. of nkIfStmt, nkCaseStmt:
  770. for i in 0..<n.len:
  771. n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
  772. if n[^1].kind != nkElse:
  773. # We don't have an else branch, but every possible branch has to end with
  774. # gotoOut, so add else here.
  775. let elseBranch = newTree(nkElse, gotoOut)
  776. n.add(elseBranch)
  777. of nkWhileStmt:
  778. # while e:
  779. # s
  780. # ->
  781. # BEGIN_STATE:
  782. # if e:
  783. # s
  784. # goto BEGIN_STATE
  785. # else:
  786. # goto OUT
  787. result = newNodeI(nkGotoState, n.info)
  788. let s = newNodeI(nkStmtList, n.info)
  789. discard ctx.newState(s, result)
  790. let ifNode = newNodeI(nkIfStmt, n.info)
  791. let elifBranch = newNodeI(nkElifBranch, n.info)
  792. elifBranch.add(n[0])
  793. var body = addGotoOut(n[1], result)
  794. body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
  795. body = ctx.transformClosureIteratorBody(body, result)
  796. elifBranch.add(body)
  797. ifNode.add(elifBranch)
  798. let elseBranch = newTree(nkElse, gotoOut)
  799. ifNode.add(elseBranch)
  800. s.add(ifNode)
  801. of nkBlockStmt:
  802. result[1] = addGotoOut(result[1], gotoOut)
  803. result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
  804. result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
  805. of nkTryStmt, nkHiddenTryStmt:
  806. # See explanation above about how this works
  807. ctx.hasExceptions = true
  808. result = newNodeI(nkGotoState, n.info)
  809. var tryBody = toStmtList(n[0])
  810. var exceptBody = ctx.collectExceptState(n)
  811. var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
  812. finallyBody = ctx.transformReturnsInTry(finallyBody)
  813. finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
  814. # The following index calculation is based on the knowledge how state
  815. # indexes are assigned
  816. let tryIdx = ctx.states.len
  817. var exceptIdx, finallyIdx: int
  818. if exceptBody.kind != nkEmpty:
  819. exceptIdx = -(tryIdx + 1)
  820. finallyIdx = tryIdx + 2
  821. else:
  822. exceptIdx = tryIdx + 1
  823. finallyIdx = tryIdx + 1
  824. let outToFinally = newNodeI(nkGotoState, finallyBody.info)
  825. block: # Create initial states.
  826. let oldExcHandlingState = ctx.curExcHandlingState
  827. ctx.curExcHandlingState = exceptIdx
  828. let realTryIdx = ctx.newState(tryBody, result)
  829. assert(realTryIdx == tryIdx)
  830. if exceptBody.kind != nkEmpty:
  831. ctx.curExcHandlingState = finallyIdx
  832. let realExceptIdx = ctx.newState(exceptBody, nil)
  833. assert(realExceptIdx == -exceptIdx)
  834. ctx.curExcHandlingState = oldExcHandlingState
  835. let realFinallyIdx = ctx.newState(finallyBody, outToFinally)
  836. assert(realFinallyIdx == finallyIdx)
  837. block: # Subdivide the states
  838. let oldNearestFinally = ctx.nearestFinally
  839. ctx.nearestFinally = finallyIdx
  840. let oldExcHandlingState = ctx.curExcHandlingState
  841. ctx.curExcHandlingState = exceptIdx
  842. if ctx.transformReturnsInTry(tryBody) != tryBody:
  843. internalError(ctx.g.config, "transformReturnsInTry != tryBody")
  844. if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody:
  845. internalError(ctx.g.config, "transformClosureIteratorBody != tryBody")
  846. ctx.curExcHandlingState = finallyIdx
  847. ctx.addElseToExcept(exceptBody)
  848. if ctx.transformReturnsInTry(exceptBody) != exceptBody:
  849. internalError(ctx.g.config, "transformReturnsInTry != exceptBody")
  850. if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody:
  851. internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody")
  852. ctx.curExcHandlingState = oldExcHandlingState
  853. ctx.nearestFinally = oldNearestFinally
  854. if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody:
  855. internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody")
  856. of nkGotoState, nkForStmt:
  857. internalError(ctx.g.config, "closure iter " & $n.kind)
  858. else:
  859. for i in 0..<n.len:
  860. n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
  861. proc stateFromGotoState(n: PNode): int =
  862. assert(n.kind == nkGotoState)
  863. result = n[0].intVal.int
  864. proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode =
  865. # This transforms 3 patterns:
  866. ########################## 1
  867. # yield e
  868. # goto STATE
  869. # ->
  870. # :state = STATE
  871. # return e
  872. ########################## 2
  873. # goto STATE
  874. # ->
  875. # :state = STATE
  876. # break :stateLoop
  877. ########################## 3
  878. # return e
  879. # ->
  880. # :state = -1
  881. # return e
  882. #
  883. result = n
  884. case n.kind
  885. of nkStmtList, nkStmtListExpr:
  886. if n.len != 0 and n[0].kind == nkYieldStmt:
  887. assert(n.len == 2)
  888. assert(n[1].kind == nkGotoState)
  889. result = newNodeI(nkStmtList, n.info)
  890. result.add(ctx.newStateAssgn(stateFromGotoState(n[1])))
  891. var retStmt = newNodeI(nkReturnStmt, n.info)
  892. if n[0][0].kind != nkEmpty:
  893. var a = newNodeI(nkAsgn, n[0][0].info)
  894. var retVal = n[0][0] #liftCapturedVars(n[0], owner, d, c)
  895. a.add newSymNode(getClosureIterResult(ctx.g, ctx.fn))
  896. a.add retVal
  897. retStmt.add(a)
  898. else:
  899. retStmt.add(ctx.g.emptyNode)
  900. result.add(retStmt)
  901. else:
  902. for i in 0..<n.len:
  903. n[i] = ctx.transformStateAssignments(n[i])
  904. of nkSkip:
  905. discard
  906. of nkReturnStmt:
  907. result = newNodeI(nkStmtList, n.info)
  908. result.add(ctx.newStateAssgn(-1))
  909. result.add(n)
  910. of nkGotoState:
  911. result = newNodeI(nkStmtList, n.info)
  912. result.add(ctx.newStateAssgn(stateFromGotoState(n)))
  913. let breakState = newNodeI(nkBreakStmt, n.info)
  914. breakState.add(newSymNode(ctx.stateLoopLabel))
  915. result.add(breakState)
  916. else:
  917. for i in 0..<n.len:
  918. n[i] = ctx.transformStateAssignments(n[i])
  919. proc skipStmtList(ctx: Ctx; n: PNode): PNode =
  920. result = n
  921. while result.kind in {nkStmtList}:
  922. if result.len == 0: return ctx.g.emptyNode
  923. result = result[0]
  924. proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
  925. # Returns first non-empty state idx for `stateIdx`. Returns `stateIdx` if
  926. # it is not empty
  927. var maxJumps = ctx.states.len # maxJumps used only for debugging purposes.
  928. var stateIdx = stateIdx
  929. while true:
  930. let label = stateIdx
  931. if label == ctx.exitStateIdx: break
  932. var newLabel = label
  933. if label == -1:
  934. newLabel = ctx.exitStateIdx
  935. else:
  936. let fs = skipStmtList(ctx, ctx.states[label][1])
  937. if fs.kind == nkGotoState:
  938. newLabel = fs[0].intVal.int
  939. if label == newLabel: break
  940. stateIdx = newLabel
  941. dec maxJumps
  942. if maxJumps == 0:
  943. assert(false, "Internal error")
  944. result = ctx.states[stateIdx][0].intVal.int
  945. proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode =
  946. result = n
  947. case n.kind
  948. of nkSkip:
  949. discard
  950. of nkGotoState:
  951. result = copyTree(n)
  952. result[0].intVal = ctx.skipEmptyStates(result[0].intVal.int)
  953. else:
  954. for i in 0..<n.len:
  955. n[i] = ctx.skipThroughEmptyStates(n[i])
  956. proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType =
  957. result = newType(tyArray, owner)
  958. let rng = newType(tyRange, owner)
  959. rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
  960. rng.rawAddSon(t)
  961. result.rawAddSon(rng)
  962. result.rawAddSon(t)
  963. proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
  964. result = newNodeI(nkBracket, ctx.fn.info)
  965. result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.fn)
  966. for i in ctx.exceptionTable:
  967. let elem = newIntNode(nkIntLit, i)
  968. elem.typ = ctx.g.getSysType(ctx.fn.info, tyInt16)
  969. result.add(elem)
  970. proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
  971. # Generates the code:
  972. # :state = exceptionTable[:state]
  973. # if :state == 0: raise
  974. # :unrollFinally = :state > 0
  975. # if :state < 0:
  976. # :state = -:state
  977. # :curExc = getCurrentException()
  978. result = newNodeI(nkStmtList, info)
  979. let intTyp = ctx.g.getSysType(info, tyInt)
  980. let boolTyp = ctx.g.getSysType(info, tyBool)
  981. # :state = exceptionTable[:state]
  982. block:
  983. # exceptionTable[:state]
  984. let getNextState = newTree(nkBracketExpr,
  985. ctx.createExceptionTable(),
  986. ctx.newStateAccess())
  987. getNextState.typ = intTyp
  988. # :state = exceptionTable[:state]
  989. result.add(ctx.newStateAssgn(getNextState))
  990. # if :state == 0: raise
  991. block:
  992. let cond = newTree(nkCall,
  993. ctx.g.getSysMagic(info, "==", mEqI).newSymNode(),
  994. ctx.newStateAccess(),
  995. newIntTypeNode(0, intTyp))
  996. cond.typ = boolTyp
  997. let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
  998. let ifBranch = newTree(nkElifBranch, cond, raiseStmt)
  999. let ifStmt = newTree(nkIfStmt, ifBranch)
  1000. result.add(ifStmt)
  1001. # :unrollFinally = :state > 0
  1002. block:
  1003. let cond = newTree(nkCall,
  1004. ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
  1005. newIntTypeNode(0, intTyp),
  1006. ctx.newStateAccess())
  1007. cond.typ = boolTyp
  1008. let asgn = newTree(nkAsgn, ctx.newUnrollFinallyAccess(info), cond)
  1009. result.add(asgn)
  1010. # if :state < 0: :state = -:state
  1011. block:
  1012. let cond = newTree(nkCall,
  1013. ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
  1014. ctx.newStateAccess(),
  1015. newIntTypeNode(0, intTyp))
  1016. cond.typ = boolTyp
  1017. let negateState = newTree(nkCall,
  1018. ctx.g.getSysMagic(info, "-", mUnaryMinusI).newSymNode,
  1019. ctx.newStateAccess())
  1020. negateState.typ = intTyp
  1021. let ifBranch = newTree(nkElifBranch, cond, ctx.newStateAssgn(negateState))
  1022. let ifStmt = newTree(nkIfStmt, ifBranch)
  1023. result.add(ifStmt)
  1024. # :curExc = getCurrentException()
  1025. block:
  1026. result.add(newTree(nkAsgn,
  1027. ctx.newCurExcAccess(),
  1028. ctx.g.callCodegenProc("getCurrentException")))
  1029. proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
  1030. let setupExc = newTree(nkCall,
  1031. newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")),
  1032. ctx.newCurExcAccess())
  1033. let tryBody = newTree(nkStmtList, setupExc, n)
  1034. let exceptBranch = newTree(nkExceptBranch, ctx.newCatchBody(ctx.fn.info))
  1035. result = newTree(nkTryStmt, tryBody, exceptBranch)
  1036. proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
  1037. # while true:
  1038. # block :stateLoop:
  1039. # gotoState :state
  1040. # local vars decl (if needed)
  1041. # body # Might get wrapped in try-except
  1042. let loopBody = newNodeI(nkStmtList, n.info)
  1043. result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody)
  1044. result.info = n.info
  1045. let localVars = newNodeI(nkStmtList, n.info)
  1046. if not ctx.stateVarSym.isNil:
  1047. let varSect = newNodeI(nkVarSection, n.info)
  1048. addVar(varSect, newSymNode(ctx.stateVarSym))
  1049. localVars.add(varSect)
  1050. if not ctx.tempVars.isNil:
  1051. localVars.add(ctx.tempVars)
  1052. let blockStmt = newNodeI(nkBlockStmt, n.info)
  1053. blockStmt.add(newSymNode(ctx.stateLoopLabel))
  1054. let gs = newNodeI(nkGotoState, n.info)
  1055. gs.add(ctx.newStateAccess())
  1056. gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1))
  1057. var blockBody = newTree(nkStmtList, gs, localVars, n)
  1058. if ctx.hasExceptions:
  1059. blockBody = ctx.wrapIntoTryExcept(blockBody)
  1060. blockStmt.add(blockBody)
  1061. loopBody.add(blockStmt)
  1062. proc deleteEmptyStates(ctx: var Ctx) =
  1063. let goOut = newTree(nkGotoState, ctx.g.newIntLit(TLineInfo(), -1))
  1064. ctx.exitStateIdx = ctx.newState(goOut, nil)
  1065. # Apply new state indexes and mark unused states with -1
  1066. var iValid = 0
  1067. for i, s in ctx.states:
  1068. let body = skipStmtList(ctx, s[1])
  1069. if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0:
  1070. # This is an empty state. Mark with -1.
  1071. s[0].intVal = -1
  1072. else:
  1073. s[0].intVal = iValid
  1074. inc iValid
  1075. for i, s in ctx.states:
  1076. let body = skipStmtList(ctx, s[1])
  1077. if body.kind != nkGotoState or i == 0:
  1078. discard ctx.skipThroughEmptyStates(s)
  1079. let excHandlState = ctx.exceptionTable[i]
  1080. if excHandlState < 0:
  1081. ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState)
  1082. elif excHandlState != 0:
  1083. ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState)
  1084. var i = 0
  1085. while i < ctx.states.len - 1:
  1086. let fs = skipStmtList(ctx, ctx.states[i][1])
  1087. if fs.kind == nkGotoState and i != 0:
  1088. ctx.states.delete(i)
  1089. ctx.exceptionTable.delete(i)
  1090. else:
  1091. inc i
  1092. proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
  1093. var ctx: Ctx
  1094. ctx.g = g
  1095. ctx.fn = fn
  1096. if getEnvParam(fn).isNil:
  1097. # Lambda lifting was not done yet. Use temporary :state sym, which will
  1098. # be handled specially by lambda lifting. Local temp vars (if needed)
  1099. # should follow the same logic.
  1100. ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
  1101. ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
  1102. ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
  1103. var n = n.toStmtList
  1104. discard ctx.newState(n, nil)
  1105. let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
  1106. var ns = false
  1107. n = ctx.lowerStmtListExprs(n, ns)
  1108. if n.hasYieldsInExpressions():
  1109. internalError(ctx.g.config, "yield in expr not lowered")
  1110. # Splitting transformation
  1111. discard ctx.transformClosureIteratorBody(n, gotoOut)
  1112. # Optimize empty states away
  1113. ctx.deleteEmptyStates()
  1114. # Make new body by concatenating the list of states
  1115. result = newNodeI(nkStmtList, n.info)
  1116. for s in ctx.states:
  1117. assert(s.len == 2)
  1118. let body = s[1]
  1119. s.sons.del(1)
  1120. result.add(s)
  1121. result.add(body)
  1122. result = ctx.transformStateAssignments(result)
  1123. result = ctx.wrapIntoStateLoop(result)
  1124. # echo "TRANSFORM TO STATES: "
  1125. # echo renderTree(result)
  1126. # echo "exception table:"
  1127. # for i, e in ctx.exceptionTable:
  1128. # echo i, " -> ", e