closureiters.nim 45 KB

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