lambdalifting.nim 36 KB

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