lambdalifting.nim 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This include file implements lambda lifting for the transformator.
  10. import
  11. intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
  12. idents, renderer, types, magicsys, rodread, lowerings, tables
  13. discard """
  14. The basic approach is that captured vars need to be put on the heap and
  15. that the calling chain needs to be explicitly modelled. Things to consider:
  16. proc a =
  17. var v = 0
  18. proc b =
  19. var w = 2
  20. for x in 0..3:
  21. proc c = capture v, w, x
  22. c()
  23. b()
  24. for x in 0..4:
  25. proc d = capture x
  26. d()
  27. Needs to be translated into:
  28. proc a =
  29. var cl: *
  30. new cl
  31. cl.v = 0
  32. proc b(cl) =
  33. var bcl: *
  34. new bcl
  35. bcl.w = 2
  36. bcl.up = cl
  37. for x in 0..3:
  38. var bcl2: *
  39. new bcl2
  40. bcl2.up = bcl
  41. bcl2.up2 = cl
  42. bcl2.x = x
  43. proc c(cl) = capture cl.up2.v, cl.up.w, cl.x
  44. c(bcl2)
  45. c(bcl)
  46. b(cl)
  47. for x in 0..4:
  48. var acl2: *
  49. new acl2
  50. acl2.x = x
  51. proc d(cl) = capture cl.x
  52. d(acl2)
  53. Closures as interfaces:
  54. proc outer: T =
  55. var captureMe: TObject # value type required for efficiency
  56. proc getter(): int = result = captureMe.x
  57. proc setter(x: int) = captureMe.x = x
  58. result = (getter, setter)
  59. Is translated to:
  60. proc outer: T =
  61. var cl: *
  62. new cl
  63. proc getter(cl): int = result = cl.captureMe.x
  64. proc setter(cl: *, x: int) = cl.captureMe.x = x
  65. result = ((cl, getter), (cl, setter))
  66. For 'byref' capture, the outer proc needs to access the captured var through
  67. the indirection too. For 'bycopy' capture, the outer proc accesses the var
  68. not through the indirection.
  69. Possible optimizations:
  70. 1) If the closure contains a single 'ref' and this
  71. reference is not re-assigned (check ``sfAddrTaken`` flag) make this the
  72. closure. This is an important optimization if closures are used as
  73. interfaces.
  74. 2) If the closure does not escape, put it onto the stack, not on the heap.
  75. 3) Dataflow analysis would help to eliminate the 'up' indirections.
  76. 4) If the captured var is not actually used in the outer proc (common?),
  77. put it into an inner proc.
  78. """
  79. # Important things to keep in mind:
  80. # * Don't base the analysis on nkProcDef et al. This doesn't work for
  81. # instantiated (formerly generic) procs. The analysis has to look at nkSym.
  82. # This also means we need to prevent the same proc is processed multiple
  83. # times via the 'processed' set.
  84. # * Keep in mind that the owner of some temporaries used to be unreliable.
  85. # * For closure iterators we merge the "real" potential closure with the
  86. # local storage requirements for efficiency. This means closure iterators
  87. # have slightly different semantics from ordinary closures.
  88. # ---------------- essential helpers -------------------------------------
  89. const
  90. upName* = ":up" # field name for the 'up' reference
  91. paramName* = ":envP"
  92. envName* = ":env"
  93. proc newCall(a: PSym, b: PNode): PNode =
  94. result = newNodeI(nkCall, a.info)
  95. result.add newSymNode(a)
  96. result.add b
  97. proc createStateType(iter: PSym): PType =
  98. var n = newNodeI(nkRange, iter.info)
  99. addSon(n, newIntNode(nkIntLit, -1))
  100. addSon(n, newIntNode(nkIntLit, 0))
  101. result = newType(tyRange, iter)
  102. result.n = n
  103. var intType = nilOrSysInt()
  104. if intType.isNil: intType = newType(tyInt, iter)
  105. rawAddSon(result, intType)
  106. proc createStateField(iter: PSym): PSym =
  107. result = newSym(skField, getIdent(":state"), iter, iter.info)
  108. result.typ = createStateType(iter)
  109. proc createEnvObj(owner: PSym; info: TLineInfo): PType =
  110. # YYY meh, just add the state field for every closure for now, it's too
  111. # hard to figure out if it comes from a closure iterator:
  112. result = createObj(owner, info, final=false)
  113. rawAddField(result, createStateField(owner))
  114. proc getIterResult(iter: PSym): PSym =
  115. if resultPos < iter.ast.len:
  116. result = iter.ast.sons[resultPos].sym
  117. else:
  118. # XXX a bit hacky:
  119. result = newSym(skResult, getIdent":result", iter, iter.info)
  120. result.typ = iter.typ.sons[0]
  121. incl(result.flags, sfUsed)
  122. iter.ast.add newSymNode(result)
  123. proc addHiddenParam(routine: PSym, param: PSym) =
  124. assert param.kind == skParam
  125. var params = routine.ast.sons[paramsPos]
  126. # -1 is correct here as param.position is 0 based but we have at position 0
  127. # some nkEffect node:
  128. param.position = routine.typ.n.len-1
  129. addSon(params, newSymNode(param))
  130. #incl(routine.typ.flags, tfCapturesEnv)
  131. assert sfFromGeneric in param.flags
  132. #echo "produced environment: ", param.id, " for ", routine.id
  133. proc getHiddenParam(routine: PSym): PSym =
  134. let params = routine.ast.sons[paramsPos]
  135. let hidden = lastSon(params)
  136. if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
  137. result = hidden.sym
  138. assert sfFromGeneric in result.flags
  139. else:
  140. # writeStackTrace()
  141. localError(routine.info, "internal error: could not find env param for " & routine.name.s)
  142. result = routine
  143. proc getEnvParam*(routine: PSym): PSym =
  144. let params = routine.ast.sons[paramsPos]
  145. let hidden = lastSon(params)
  146. if hidden.kind == nkSym and hidden.sym.name.s == paramName:
  147. result = hidden.sym
  148. assert sfFromGeneric in result.flags
  149. proc interestingVar(s: PSym): bool {.inline.} =
  150. result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
  151. sfGlobal notin s.flags
  152. proc illegalCapture(s: PSym): bool {.inline.} =
  153. result = skipTypes(s.typ, abstractInst).kind in
  154. {tyVar, tyOpenArray, tyVarargs} or
  155. s.kind == skResult
  156. proc isInnerProc(s: PSym): bool =
  157. if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
  158. result = s.skipGenericOwner.kind in routineKinds
  159. proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
  160. # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
  161. # mean to be able to capture string literals which have no GC header.
  162. # However this can only happen if the capture happens through a parameter,
  163. # which is however the only case when we generate an assignment in the first
  164. # place.
  165. result = newNodeI(nkAsgn, info, 2)
  166. result.sons[0] = le
  167. result.sons[1] = ri
  168. proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode =
  169. result = newNodeIT(nkClosure, info, prc.typ)
  170. result.add(newSymNode(prc))
  171. if env == nil:
  172. result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
  173. else:
  174. if env.skipConv.kind == nkClosure:
  175. localError(info, "internal error: taking closure of closure")
  176. result.add(env)
  177. proc interestingIterVar(s: PSym): bool {.inline.} =
  178. # XXX optimization: Only lift the variable if it lives across
  179. # yield/return boundaries! This can potentially speed up
  180. # closure iterators quite a bit.
  181. result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
  182. template isIterator*(owner: PSym): bool =
  183. owner.kind == skIterator and owner.typ.callConv == ccClosure
  184. proc liftingHarmful(owner: PSym): bool {.inline.} =
  185. ## lambda lifting can be harmful for JS-like code generators.
  186. let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
  187. result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime
  188. proc liftIterSym*(n: PNode; owner: PSym): PNode =
  189. # transforms (iter) to (let env = newClosure[iter](); (iter, env))
  190. if liftingHarmful(owner): return n
  191. let iter = n.sym
  192. assert iter.isIterator
  193. result = newNodeIT(nkStmtListExpr, n.info, n.typ)
  194. let hp = getHiddenParam(iter)
  195. var env: PNode
  196. if owner.isIterator:
  197. let it = getHiddenParam(owner)
  198. addUniqueField(it.typ.sons[0], hp)
  199. env = indirectAccess(newSymNode(it), hp, hp.info)
  200. else:
  201. let e = newSym(skLet, iter.name, owner, n.info)
  202. e.typ = hp.typ
  203. e.flags = hp.flags
  204. env = newSymNode(e)
  205. var v = newNodeI(nkVarSection, n.info)
  206. addVar(v, env)
  207. result.add(v)
  208. # add 'new' statement:
  209. result.add newCall(getSysSym"internalNew", env)
  210. result.add makeClosure(iter, env, n.info)
  211. proc freshVarForClosureIter*(s, owner: PSym): PNode =
  212. let envParam = getHiddenParam(owner)
  213. let obj = envParam.typ.lastSon
  214. addField(obj, s)
  215. var access = newSymNode(envParam)
  216. assert obj.kind == tyObject
  217. let field = getFieldFromObj(obj, s)
  218. if field != nil:
  219. result = rawIndirectAccess(access, field, s.info)
  220. else:
  221. localError(s.info, "internal error: cannot generate fresh variable")
  222. result = access
  223. # ------------------ new stuff -------------------------------------------
  224. proc markAsClosure(owner: PSym; n: PNode) =
  225. let s = n.sym
  226. if illegalCapture(s) or owner.typ.callConv notin {ccClosure, ccDefault}:
  227. localError(n.info, errIllegalCaptureX, s.name.s)
  228. incl(owner.typ.flags, tfCapturesEnv)
  229. owner.typ.callConv = ccClosure
  230. type
  231. DetectionPass = object
  232. processed, capturedVars: IntSet
  233. ownerToType: Table[int, PType]
  234. somethingToDo: bool
  235. proc initDetectionPass(fn: PSym): DetectionPass =
  236. result.processed = initIntSet()
  237. result.capturedVars = initIntSet()
  238. result.ownerToType = initTable[int, PType]()
  239. result.processed.incl(fn.id)
  240. discard """
  241. proc outer =
  242. var a, b: int
  243. proc innerA = use(a)
  244. proc innerB = use(b); innerA()
  245. # --> innerA and innerB need to *share* the closure type!
  246. This is why need to store the 'ownerToType' table and use it
  247. during .closure'fication.
  248. """
  249. proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
  250. info: TLineInfo): PType =
  251. result = c.ownerToType.getOrDefault(owner.id)
  252. if result.isNil:
  253. result = newType(tyRef, owner)
  254. let obj = createEnvObj(owner, info)
  255. rawAddSon(result, obj)
  256. c.ownerToType[owner.id] = result
  257. proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
  258. let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ
  259. let obj = refObj.lastSon
  260. let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ
  261. if refObj == fieldType:
  262. localError(dep.info, "internal error: invalid up reference computed")
  263. let upIdent = getIdent(upName)
  264. let upField = lookupInRecord(obj.n, upIdent)
  265. if upField != nil:
  266. if upField.typ != fieldType:
  267. localError(dep.info, "internal error: up references do not agree")
  268. else:
  269. let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
  270. result.typ = fieldType
  271. rawAddField(obj, result)
  272. discard """
  273. There are a couple of possibilities of how to implement closure
  274. iterators that capture outer variables in a traditional sense
  275. (aka closure closure iterators).
  276. 1. Transform iter() to iter(state, capturedEnv). So use 2 hidden
  277. parameters.
  278. 2. Add the captured vars directly to 'state'.
  279. 3. Make capturedEnv an up-reference of 'state'.
  280. We do (3) here because (2) is obviously wrong and (1) is wrong too.
  281. Consider:
  282. proc outer =
  283. var xx = 9
  284. iterator foo() =
  285. var someState = 3
  286. proc bar = echo someState
  287. proc baz = someState = 0
  288. baz()
  289. bar()
  290. """
  291. proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
  292. var cp = getEnvParam(fn)
  293. let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
  294. let t = c.getEnvTypeForOwner(owner, info)
  295. if cp == nil:
  296. cp = newSym(skParam, getIdent(paramName), fn, fn.info)
  297. incl(cp.flags, sfFromGeneric)
  298. cp.typ = t
  299. addHiddenParam(fn, cp)
  300. elif cp.typ != t and fn.kind != skIterator:
  301. localError(fn.info, "internal error: inconsistent environment type")
  302. #echo "adding closure to ", fn.name.s
  303. proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
  304. case n.kind
  305. of nkSym:
  306. let s = n.sym
  307. if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and
  308. s.typ != nil and s.typ.callConv == ccClosure:
  309. # this handles the case that the inner proc was declared as
  310. # .closure but does not actually capture anything:
  311. addClosureParam(c, s, n.info)
  312. c.somethingToDo = true
  313. let innerProc = isInnerProc(s)
  314. if innerProc:
  315. if s.isIterator: c.somethingToDo = true
  316. if not c.processed.containsOrIncl(s.id):
  317. detectCapturedVars(s.getBody, s, c)
  318. let ow = s.skipGenericOwner
  319. if ow == owner:
  320. if owner.isIterator:
  321. c.somethingToDo = true
  322. addClosureParam(c, owner, n.info)
  323. if interestingIterVar(s):
  324. if not c.capturedVars.containsOrIncl(s.id):
  325. let obj = getHiddenParam(owner).typ.lastSon
  326. #let obj = c.getEnvTypeForOwner(s.owner).lastSon
  327. addField(obj, s)
  328. # but always return because the rest of the proc is only relevant when
  329. # ow != owner:
  330. return
  331. # direct or indirect dependency:
  332. if (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
  333. discard """
  334. proc outer() =
  335. var x: int
  336. proc inner() =
  337. proc innerInner() =
  338. echo x
  339. innerInner()
  340. inner()
  341. # inner() takes a closure too!
  342. """
  343. # mark 'owner' as taking a closure:
  344. c.somethingToDo = true
  345. markAsClosure(owner, n)
  346. addClosureParam(c, owner, n.info)
  347. #echo "capturing ", n.info
  348. # variable 's' is actually captured:
  349. if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
  350. let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
  351. #getHiddenParam(owner).typ.lastSon
  352. addField(obj, s)
  353. # create required upFields:
  354. var w = owner.skipGenericOwner
  355. if isInnerProc(w) or owner.isIterator:
  356. if owner.isIterator: w = owner
  357. let last = if ow.isIterator: ow.skipGenericOwner else: ow
  358. while w != nil and w.kind != skModule and last != w:
  359. discard """
  360. proc outer =
  361. var a, b: int
  362. proc outerB =
  363. proc innerA = use(a)
  364. proc innerB = use(b); innerA()
  365. # --> make outerB of calling convention .closure and
  366. # give it the same env type that outer's env var gets:
  367. """
  368. let up = w.skipGenericOwner
  369. #echo "up for ", w.name.s, " up ", up.name.s
  370. markAsClosure(w, n)
  371. addClosureParam(c, w, n.info) # , ow
  372. createUpField(c, w, up, n.info)
  373. w = up
  374. of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
  375. nkTemplateDef, nkTypeSection:
  376. discard
  377. of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
  378. discard
  379. of nkLambdaKinds, nkIteratorDef, nkFuncDef:
  380. if n.typ != nil:
  381. detectCapturedVars(n[namePos], owner, c)
  382. else:
  383. for i in 0..<n.len:
  384. detectCapturedVars(n[i], owner, c)
  385. type
  386. LiftingPass = object
  387. processed: IntSet
  388. envVars: Table[int, PNode]
  389. proc initLiftingPass(fn: PSym): LiftingPass =
  390. result.processed = initIntSet()
  391. result.processed.incl(fn.id)
  392. result.envVars = initTable[int, PNode]()
  393. proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
  394. let s = n.sym
  395. # Type based expression construction for simplicity:
  396. let envParam = getHiddenParam(owner)
  397. if not envParam.isNil:
  398. var access = newSymNode(envParam)
  399. while true:
  400. let obj = access.typ.sons[0]
  401. assert obj.kind == tyObject
  402. let field = getFieldFromObj(obj, s)
  403. if field != nil:
  404. return rawIndirectAccess(access, field, n.info)
  405. let upField = lookupInRecord(obj.n, getIdent(upName))
  406. if upField == nil: break
  407. access = rawIndirectAccess(access, upField, n.info)
  408. localError(n.info, "internal error: environment misses: " & s.name.s)
  409. result = n
  410. proc newEnvVar(owner: PSym; typ: PType): PNode =
  411. var v = newSym(skVar, getIdent(envName), owner, owner.info)
  412. incl(v.flags, sfShadowed)
  413. v.typ = typ
  414. result = newSymNode(v)
  415. when false:
  416. if owner.kind == skIterator and owner.typ.callConv == ccClosure:
  417. let it = getHiddenParam(owner)
  418. addUniqueField(it.typ.sons[0], v)
  419. result = indirectAccess(newSymNode(it), v, v.info)
  420. else:
  421. result = newSymNode(v)
  422. proc setupEnvVar(owner: PSym; d: DetectionPass;
  423. c: var LiftingPass): PNode =
  424. if owner.isIterator:
  425. return getHiddenParam(owner).newSymNode
  426. result = c.envvars.getOrDefault(owner.id)
  427. if result.isNil:
  428. let envVarType = d.ownerToType.getOrDefault(owner.id)
  429. if envVarType.isNil:
  430. localError owner.info, "internal error: could not determine closure type"
  431. result = newEnvVar(owner, envVarType)
  432. c.envVars[owner.id] = result
  433. proc getUpViaParam(owner: PSym): PNode =
  434. let p = getHiddenParam(owner)
  435. result = p.newSymNode
  436. if owner.isIterator:
  437. let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName))
  438. if upField == nil:
  439. localError(owner.info, "could not find up reference for closure iter")
  440. else:
  441. result = rawIndirectAccess(result, upField, p.info)
  442. proc rawClosureCreation(owner: PSym;
  443. d: DetectionPass; c: var LiftingPass): PNode =
  444. result = newNodeI(nkStmtList, owner.info)
  445. var env: PNode
  446. if owner.isIterator:
  447. env = getHiddenParam(owner).newSymNode
  448. else:
  449. env = setupEnvVar(owner, d, c)
  450. if env.kind == nkSym:
  451. var v = newNodeI(nkVarSection, env.info)
  452. addVar(v, env)
  453. result.add(v)
  454. # add 'new' statement:
  455. result.add(newCall(getSysSym"internalNew", env))
  456. # add assignment statements for captured parameters:
  457. for i in 1..<owner.typ.n.len:
  458. let local = owner.typ.n[i].sym
  459. if local.id in d.capturedVars:
  460. let fieldAccess = indirectAccess(env, local, env.info)
  461. # add ``env.param = param``
  462. result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
  463. let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
  464. if upField != nil:
  465. let up = getUpViaParam(owner)
  466. if up != nil and upField.typ == up.typ:
  467. result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
  468. up, env.info))
  469. #elif oldenv != nil and oldenv.typ == upField.typ:
  470. # result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
  471. # oldenv, env.info))
  472. else:
  473. localError(env.info, "internal error: cannot create up reference")
  474. proc closureCreationForIter(iter: PNode;
  475. d: DetectionPass; c: var LiftingPass): PNode =
  476. result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
  477. let owner = iter.sym.skipGenericOwner
  478. var v = newSym(skVar, getIdent(envName), owner, iter.info)
  479. incl(v.flags, sfShadowed)
  480. v.typ = getHiddenParam(iter.sym).typ
  481. var vnode: PNode
  482. if owner.isIterator:
  483. let it = getHiddenParam(owner)
  484. addUniqueField(it.typ.sons[0], v)
  485. vnode = indirectAccess(newSymNode(it), v, v.info)
  486. else:
  487. vnode = v.newSymNode
  488. var vs = newNodeI(nkVarSection, iter.info)
  489. addVar(vs, vnode)
  490. result.add(vs)
  491. result.add(newCall(getSysSym"internalNew", vnode))
  492. let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
  493. if upField != nil:
  494. let u = setupEnvVar(owner, d, c)
  495. if u.typ == upField.typ:
  496. result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
  497. u, iter.info))
  498. else:
  499. localError(iter.info, "internal error: cannot create up reference for iter")
  500. result.add makeClosure(iter.sym, vnode, iter.info)
  501. proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
  502. c: var LiftingPass): PNode =
  503. let access = setupEnvVar(owner, d, c)
  504. let obj = access.typ.sons[0]
  505. let field = getFieldFromObj(obj, n.sym)
  506. if field != nil:
  507. result = rawIndirectAccess(access, field, n.info)
  508. else:
  509. localError(n.info, "internal error: not part of closure object type")
  510. result = n
  511. proc getStateField(owner: PSym): PSym =
  512. getHiddenParam(owner).typ.sons[0].n.sons[0].sym
  513. proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
  514. c: var LiftingPass): PNode
  515. proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
  516. c: var LiftingPass): PNode =
  517. let state = getStateField(owner)
  518. assert state != nil
  519. assert state.typ != nil
  520. assert state.typ.n != nil
  521. inc state.typ.n.sons[1].intVal
  522. let stateNo = state.typ.n.sons[1].intVal
  523. var stateAsgnStmt = newNodeI(nkAsgn, n.info)
  524. stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
  525. state, n.info))
  526. stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
  527. var retStmt = newNodeI(nkReturnStmt, n.info)
  528. if n.sons[0].kind != nkEmpty:
  529. var a = newNodeI(nkAsgn, n.sons[0].info)
  530. var retVal = liftCapturedVars(n.sons[0], owner, d, c)
  531. addSon(a, newSymNode(getIterResult(owner)))
  532. addSon(a, retVal)
  533. retStmt.add(a)
  534. else:
  535. retStmt.add(emptyNode)
  536. var stateLabelStmt = newNodeI(nkState, n.info)
  537. stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
  538. result = newNodeI(nkStmtList, n.info)
  539. result.add(stateAsgnStmt)
  540. result.add(retStmt)
  541. result.add(stateLabelStmt)
  542. proc transformReturn(n: PNode; owner: PSym; d: DetectionPass;
  543. c: var LiftingPass): PNode =
  544. let state = getStateField(owner)
  545. result = newNodeI(nkStmtList, n.info)
  546. var stateAsgnStmt = newNodeI(nkAsgn, n.info)
  547. stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
  548. state, n.info))
  549. stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
  550. result.add(stateAsgnStmt)
  551. result.add(n)
  552. proc wrapIterBody(n: PNode; owner: PSym): PNode =
  553. if not owner.isIterator: return n
  554. when false:
  555. # unfortunately control flow is still convoluted and we can end up
  556. # multiple times here for the very same iterator. We shield against this
  557. # with some rather primitive check for now:
  558. if n.kind == nkStmtList and n.len > 0:
  559. if n.sons[0].kind == nkGotoState: return n
  560. if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and
  561. n[1][0].kind == nkGotoState:
  562. return n
  563. let info = n.info
  564. result = newNodeI(nkStmtList, info)
  565. var gs = newNodeI(nkGotoState, info)
  566. gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), info))
  567. result.add(gs)
  568. var state0 = newNodeI(nkState, info)
  569. state0.add(newIntNode(nkIntLit, 0))
  570. result.add(state0)
  571. result.add(n)
  572. var stateAsgnStmt = newNodeI(nkAsgn, info)
  573. stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam),
  574. getStateField(owner), info))
  575. stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
  576. result.add(stateAsgnStmt)
  577. proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
  578. c: var LiftingPass): PNode =
  579. let s = n.sym
  580. if s == owner:
  581. # recursive calls go through (lambda, hiddenParam):
  582. let available = getHiddenParam(owner)
  583. result = makeClosure(s, available.newSymNode, n.info)
  584. elif s.isIterator:
  585. result = closureCreationForIter(n, d, c)
  586. elif s.skipGenericOwner == owner:
  587. # direct dependency, so use the outer's env variable:
  588. result = makeClosure(s, setupEnvVar(owner, d, c), n.info)
  589. else:
  590. let available = getHiddenParam(owner)
  591. let wanted = getHiddenParam(s).typ
  592. # ugh: call through some other inner proc;
  593. var access = newSymNode(available)
  594. while true:
  595. if access.typ == wanted:
  596. return makeClosure(s, access, n.info)
  597. let obj = access.typ.sons[0]
  598. let upField = lookupInRecord(obj.n, getIdent(upName))
  599. if upField == nil:
  600. localError(n.info, "internal error: no environment found")
  601. return n
  602. access = rawIndirectAccess(access, upField, n.info)
  603. proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
  604. c: var LiftingPass): PNode =
  605. result = n
  606. case n.kind
  607. of nkSym:
  608. let s = n.sym
  609. if isInnerProc(s):
  610. if not c.processed.containsOrIncl(s.id):
  611. #if s.name.s == "temp":
  612. # echo renderTree(s.getBody, {renderIds})
  613. let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
  614. if c.envvars.getOrDefault(s.id).isNil:
  615. s.ast.sons[bodyPos] = body
  616. else:
  617. s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
  618. if s.typ.callConv == ccClosure:
  619. result = symToClosure(n, owner, d, c)
  620. elif s.id in d.capturedVars:
  621. if s.owner != owner:
  622. result = accessViaEnvParam(n, owner)
  623. elif owner.isIterator and interestingIterVar(s):
  624. result = accessViaEnvParam(n, owner)
  625. else:
  626. result = accessViaEnvVar(n, owner, d, c)
  627. of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
  628. nkTemplateDef, nkTypeSection:
  629. discard
  630. of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
  631. discard
  632. of nkClosure:
  633. if n[1].kind == nkNilLit:
  634. n.sons[0] = liftCapturedVars(n[0], owner, d, c)
  635. let x = n.sons[0].skipConv
  636. if x.kind == nkClosure:
  637. #localError(n.info, "internal error: closure to closure created")
  638. # now we know better, so patch it:
  639. n.sons[0] = x.sons[0]
  640. n.sons[1] = x.sons[1]
  641. of nkLambdaKinds, nkIteratorDef, nkFuncDef:
  642. if n.typ != nil and n[namePos].kind == nkSym:
  643. let m = newSymNode(n[namePos].sym)
  644. m.typ = n.typ
  645. result = liftCapturedVars(m, owner, d, c)
  646. of nkHiddenStdConv:
  647. if n.len == 2:
  648. n.sons[1] = liftCapturedVars(n[1], owner, d, c)
  649. if n[1].kind == nkClosure: result = n[1]
  650. else:
  651. if owner.isIterator:
  652. if n.kind == nkYieldStmt:
  653. return transformYield(n, owner, d, c)
  654. elif n.kind == nkReturnStmt:
  655. return transformReturn(n, owner, d, c)
  656. elif nfLL in n.flags:
  657. # special case 'when nimVm' due to bug #3636:
  658. n.sons[1] = liftCapturedVars(n[1], owner, d, c)
  659. return
  660. for i in 0..<n.len:
  661. n.sons[i] = liftCapturedVars(n[i], owner, d, c)
  662. # ------------------ old stuff -------------------------------------------
  663. proc semCaptureSym*(s, owner: PSym) =
  664. if interestingVar(s) and s.kind != skResult:
  665. if owner.typ != nil and not isGenericRoutine(owner):
  666. # XXX: is this really safe?
  667. # if we capture a var from another generic routine,
  668. # it won't be consider captured.
  669. var o = owner.skipGenericOwner
  670. while o.kind != skModule and o != nil:
  671. if s.owner == o:
  672. owner.typ.callConv = ccClosure
  673. #echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s
  674. o = o.skipGenericOwner
  675. # since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
  676. # here
  677. proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
  678. var d = initDetectionPass(fn)
  679. var c = initLiftingPass(fn)
  680. # pretend 'fn' is a closure iterator for the analysis:
  681. let oldKind = fn.kind
  682. let oldCC = fn.typ.callConv
  683. fn.kind = skIterator
  684. fn.typ.callConv = ccClosure
  685. d.ownerToType[fn.id] = ptrType
  686. detectCapturedVars(body, fn, d)
  687. result = wrapIterBody(liftCapturedVars(body, fn, d, c), fn)
  688. fn.kind = oldKind
  689. fn.typ.callConv = oldCC
  690. proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
  691. # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
  692. # the transformation even when compiling to JS ...
  693. # However we can do lifting for the stuff which is *only* compiletime.
  694. let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
  695. if body.kind == nkEmpty or (
  696. gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or
  697. fn.skipGenericOwner.kind != skModule:
  698. # ignore forward declaration:
  699. result = body
  700. tooEarly = true
  701. else:
  702. var d = initDetectionPass(fn)
  703. detectCapturedVars(body, fn, d)
  704. if not d.somethingToDo and fn.isIterator:
  705. addClosureParam(d, fn, body.info)
  706. d.somethingToDo = true
  707. if d.somethingToDo:
  708. var c = initLiftingPass(fn)
  709. var newBody = liftCapturedVars(body, fn, d, c)
  710. if c.envvars.getOrDefault(fn.id) != nil:
  711. newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
  712. result = wrapIterBody(newBody, fn)
  713. else:
  714. result = body
  715. #if fn.name.s == "get2":
  716. # echo "had something to do ", d.somethingToDo
  717. # echo renderTree(result, {renderIds})
  718. proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
  719. if body.kind == nkEmpty or gCmd == cmdCompileToJS:
  720. result = body
  721. else:
  722. # XXX implement it properly
  723. result = body
  724. # ------------------- iterator transformation --------------------------------
  725. proc liftForLoop*(body: PNode; owner: PSym): PNode =
  726. # problem ahead: the iterator could be invoked indirectly, but then
  727. # we don't know what environment to create here:
  728. #
  729. # iterator count(): int =
  730. # yield 0
  731. #
  732. # iterator count2(): int =
  733. # var x = 3
  734. # yield x
  735. # inc x
  736. # yield x
  737. #
  738. # proc invoke(iter: iterator(): int) =
  739. # for x in iter(): echo x
  740. #
  741. # --> When to create the closure? --> for the (count) occurrence!
  742. discard """
  743. for i in foo(): ...
  744. Is transformed to:
  745. cl = createClosure()
  746. while true:
  747. let i = foo(cl)
  748. nkBreakState(cl.state)
  749. ...
  750. """
  751. if liftingHarmful(owner): return body
  752. var L = body.len
  753. if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
  754. localError(body.info, "ignored invalid for loop")
  755. return body
  756. var call = body[L-2]
  757. result = newNodeI(nkStmtList, body.info)
  758. # static binding?
  759. var env: PSym
  760. let op = call[0]
  761. if op.kind == nkSym and op.sym.isIterator:
  762. # createClosure()
  763. let iter = op.sym
  764. let hp = getHiddenParam(iter)
  765. env = newSym(skLet, iter.name, owner, body.info)
  766. env.typ = hp.typ
  767. env.flags = hp.flags
  768. var v = newNodeI(nkVarSection, body.info)
  769. addVar(v, newSymNode(env))
  770. result.add(v)
  771. # add 'new' statement:
  772. result.add(newCall(getSysSym"internalNew", env.newSymNode))
  773. elif op.kind == nkStmtListExpr:
  774. let closure = op.lastSon
  775. if closure.kind == nkClosure:
  776. call.sons[0] = closure
  777. for i in 0 .. op.len-2:
  778. result.add op[i]
  779. var loopBody = newNodeI(nkStmtList, body.info, 3)
  780. var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
  781. whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
  782. whileLoop.sons[1] = loopBody
  783. result.add whileLoop
  784. # setup loopBody:
  785. # gather vars in a tuple:
  786. var v2 = newNodeI(nkLetSection, body.info)
  787. var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, body.info)
  788. for i in 0 .. L-3:
  789. if body[i].kind == nkSym:
  790. body[i].sym.kind = skLet
  791. addSon(vpart, body[i])
  792. addSon(vpart, ast.emptyNode) # no explicit type
  793. if not env.isNil:
  794. call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info)
  795. addSon(vpart, call)
  796. addSon(v2, vpart)
  797. loopBody.sons[0] = v2
  798. var bs = newNodeI(nkBreakState, body.info)
  799. bs.addSon(call.sons[0])
  800. loopBody.sons[1] = bs
  801. loopBody.sons[2] = body[L-1]