closureiters.nim 40 KB

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