closureiters.nim 45 KB

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