lambdalifting.nim 34 KB

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