lambdalifting.nim 37 KB

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