closureiters.nim 47 KB

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