closureiters.nim 45 KB

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