injectdestructors.nim 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2017 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Injects destructor calls into Nim code as well as
  10. ## an optimizer that optimizes copies to moves. This is implemented as an
  11. ## AST to AST transformation so that every backend benefits from it.
  12. ## See doc/destructors.rst for a spec of the implemented rewrite rules
  13. import
  14. intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
  15. strutils, options, lowerings, tables, modulegraphs,
  16. lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
  17. varpartitions, aliasanalysis, dfa, wordrecg
  18. when defined(nimPreviewSlimSystem):
  19. import std/assertions
  20. from trees import exprStructuralEquivalent, getRoot, whichPragma
  21. type
  22. Con = object
  23. owner: PSym
  24. when true:
  25. g: ControlFlowGraph
  26. graph: ModuleGraph
  27. inLoop, inSpawn, inLoopCond: int
  28. uninit: IntSet # set of uninit'ed vars
  29. idgen: IdGenerator
  30. body: PNode
  31. otherUsage: TLineInfo
  32. inUncheckedAssignSection: int
  33. Scope = object # we do scope-based memory management.
  34. # a scope is comparable to an nkStmtListExpr like
  35. # (try: statements; dest = y(); finally: destructors(); dest)
  36. vars: seq[PSym]
  37. wasMoved: seq[PNode]
  38. final: seq[PNode] # finally section
  39. locals: seq[PSym]
  40. body: PNode
  41. needsTry: bool
  42. parent: ptr Scope
  43. ProcessMode = enum
  44. normal
  45. consumed
  46. sinkArg
  47. const toDebug {.strdefine.} = ""
  48. when toDebug.len > 0:
  49. var shouldDebug = false
  50. template dbg(body) =
  51. when toDebug.len > 0:
  52. if shouldDebug:
  53. body
  54. proc hasDestructor(c: Con; t: PType): bool {.inline.} =
  55. result = ast.hasDestructor(t)
  56. when toDebug.len > 0:
  57. # for more effective debugging
  58. if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
  59. assert(not containsGarbageCollectedRef(t))
  60. proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
  61. let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info)
  62. sym.typ = typ
  63. s.vars.add(sym)
  64. result = newSymNode(sym)
  65. proc nestedScope(parent: var Scope; body: PNode): Scope =
  66. Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
  67. proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
  68. type
  69. MoveOrCopyFlag = enum
  70. IsDecl, IsExplicitSink
  71. proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
  72. when false:
  73. var
  74. perfCounters: array[InstrKind, int]
  75. proc showCounters*() =
  76. for i in low(InstrKind)..high(InstrKind):
  77. echo "INSTR ", i, " ", perfCounters[i]
  78. proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
  79. let root = parampatterns.exprRoot(n, allowCalls=false)
  80. if root == nil: return false
  81. var s = addr(scope)
  82. while s != nil:
  83. if s.locals.contains(root): break
  84. s = s.parent
  85. c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root)
  86. dbg:
  87. echo "\n### ", c.owner.name.s, ":\nCFG:"
  88. echoCfg(c.g)
  89. #echo c.body
  90. var j = 0
  91. while j < c.g.len:
  92. if c.g[j].kind == use and c.g[j].n == n: break
  93. inc j
  94. c.otherUsage = unknownLineInfo
  95. if j < c.g.len:
  96. var pcs = @[j+1]
  97. var marked = initIntSet()
  98. result = true
  99. while pcs.len > 0:
  100. var pc = pcs.pop()
  101. if not marked.contains(pc):
  102. let oldPc = pc
  103. while pc < c.g.len:
  104. dbg:
  105. echo "EXEC ", c.g[pc].kind, " ", pc, " ", n
  106. when false:
  107. inc perfCounters[c.g[pc].kind]
  108. case c.g[pc].kind
  109. of loop:
  110. let back = pc + c.g[pc].dest
  111. if not marked.containsOrIncl(back):
  112. pc = back
  113. else:
  114. break
  115. of goto:
  116. pc = pc + c.g[pc].dest
  117. of fork:
  118. if not marked.contains(pc+1):
  119. pcs.add pc + 1
  120. pc = pc + c.g[pc].dest
  121. of use:
  122. if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no:
  123. c.otherUsage = c.g[pc].n.info
  124. return false
  125. inc pc
  126. of def:
  127. if c.g[pc].n.aliases(n) == yes:
  128. # the path leads to a redefinition of 's' --> sink 's'.
  129. break
  130. elif n.aliases(c.g[pc].n) != no:
  131. # only partially writes to 's' --> can't sink 's', so this def reads 's'
  132. # or maybe writes to 's' --> can't sink 's'
  133. c.otherUsage = c.g[pc].n.info
  134. return false
  135. inc pc
  136. marked.incl oldPc
  137. else:
  138. result = false
  139. proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
  140. if not hasDestructor(c, n.typ): return true
  141. let m = skipConvDfa(n)
  142. result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
  143. isLastReadImpl(n, c, s)
  144. proc isFirstWrite(n: PNode; c: var Con): bool =
  145. let m = skipConvDfa(n)
  146. result = nfFirstWrite in m.flags
  147. proc isCursor(n: PNode): bool =
  148. case n.kind
  149. of nkSym:
  150. sfCursor in n.sym.flags
  151. of nkDotExpr:
  152. isCursor(n[1])
  153. of nkCheckedFieldExpr:
  154. isCursor(n[0])
  155. else:
  156. false
  157. template isUnpackedTuple(n: PNode): bool =
  158. ## we move out all elements of unpacked tuples,
  159. ## hence unpacked tuples themselves don't need to be destroyed
  160. (n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
  161. proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
  162. var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
  163. if (opname == "=" or opname == "=copy") and ri != nil:
  164. m.add "; requires a copy because it's not the last read of '"
  165. m.add renderTree(ri)
  166. m.add '\''
  167. if c.otherUsage != unknownLineInfo:
  168. # ri.comment.startsWith('\n'):
  169. m.add "; another read is done here: "
  170. m.add c.graph.config $ c.otherUsage
  171. #m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
  172. elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
  173. m.add "; try to make "
  174. m.add renderTree(ri)
  175. m.add " a 'sink' parameter"
  176. m.add "; routine: "
  177. m.add c.owner.name.s
  178. localError(c.graph.config, ri.info, errGenerated, m)
  179. proc makePtrType(c: var Con, baseType: PType): PType =
  180. result = newType(tyPtr, nextTypeId c.idgen, c.owner)
  181. addSonSkipIntLit(result, baseType, c.idgen)
  182. proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
  183. let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
  184. addrExp.add(dest)
  185. result = newTree(nkCall, newSymNode(op), addrExp)
  186. proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
  187. var op = getAttachedOp(c.graph, t, kind)
  188. if op == nil or op.ast.isGenericRoutine:
  189. # give up and find the canonical type instead:
  190. let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct})
  191. let canon = c.graph.canonTypes.getOrDefault(h)
  192. if canon != nil:
  193. op = getAttachedOp(c.graph, canon, kind)
  194. if op == nil:
  195. #echo dest.typ.id
  196. globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
  197. "' operator not found for type " & typeToString(t))
  198. elif op.ast.isGenericRoutine:
  199. globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
  200. "' operator is generic")
  201. dbg:
  202. if kind == attachedDestructor:
  203. echo "destructor is ", op.id, " ", op.ast
  204. if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
  205. c.genOp(op, dest)
  206. proc genDestroy(c: var Con; dest: PNode): PNode =
  207. let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
  208. result = c.genOp(t, attachedDestructor, dest, nil)
  209. proc canBeMoved(c: Con; t: PType): bool {.inline.} =
  210. let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
  211. if optOwnedRefs in c.graph.config.globalOptions:
  212. result = t.kind != tyRef and getAttachedOp(c.graph, t, attachedSink) != nil
  213. else:
  214. result = getAttachedOp(c.graph, t, attachedSink) != nil
  215. proc isNoInit(dest: PNode): bool {.inline.} =
  216. result = dest.kind == nkSym and sfNoInit in dest.sym.flags
  217. proc deepAliases(dest, ri: PNode): bool =
  218. case ri.kind
  219. of nkCallKinds, nkStmtListExpr, nkBracket, nkTupleConstr, nkObjConstr,
  220. nkCast, nkConv, nkObjUpConv, nkObjDownConv:
  221. for r in ri:
  222. if deepAliases(dest, r): return true
  223. return false
  224. else:
  225. return aliases(dest, ri) != no
  226. proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
  227. if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
  228. (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
  229. isNoInit(dest):
  230. # optimize sink call into a bitwise memcopy
  231. result = newTree(nkFastAsgn, dest, ri)
  232. else:
  233. let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
  234. if getAttachedOp(c.graph, t, attachedSink) != nil:
  235. result = c.genOp(t, attachedSink, dest, ri)
  236. result.add ri
  237. else:
  238. # the default is to use combination of `=destroy(dest)` and
  239. # and copyMem(dest, source). This is efficient.
  240. if deepAliases(dest, ri):
  241. # consider: x = x + y, it is wrong to destroy the destination first!
  242. # tmp to support self assignments
  243. let tmp = c.getTemp(s, dest.typ, dest.info)
  244. result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
  245. c.genDestroy(tmp))
  246. else:
  247. result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
  248. proc isCriticalLink(dest: PNode): bool {.inline.} =
  249. #[
  250. Lins's idea that only "critical" links can introduce a cycle. This is
  251. critical for the performance gurantees that we strive for: If you
  252. traverse a data structure, no tracing will be performed at all.
  253. ORC is about this promise: The GC only touches the memory that the
  254. mutator touches too.
  255. These constructs cannot possibly create cycles::
  256. local = ...
  257. new(x)
  258. dest = ObjectConstructor(field: noalias(dest))
  259. But since 'ObjectConstructor' is already moved into 'dest' all we really have
  260. to look for is assignments to local variables.
  261. ]#
  262. result = dest.kind != nkSym
  263. proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
  264. if c.graph.config.selectedGC == gcOrc:
  265. let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
  266. if cyclicType(t):
  267. result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
  268. proc genMarkCyclic(c: var Con; result, dest: PNode) =
  269. if c.graph.config.selectedGC == gcOrc:
  270. let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
  271. if cyclicType(t):
  272. if t.kind == tyRef:
  273. result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
  274. else:
  275. let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
  276. xenv.typ = getSysType(c.graph, dest.info, tyPointer)
  277. result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
  278. proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
  279. let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
  280. result = c.genOp(t, a, dest, ri)
  281. assert ri.typ != nil
  282. proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
  283. let t = dest.typ
  284. if tfHasOwned in t.flags and ri.kind != nkNilLit:
  285. # try to improve the error message here:
  286. if IsExplicitSink in flags:
  287. c.checkForErrorPragma(t, ri, "=sink")
  288. else:
  289. c.checkForErrorPragma(t, ri, "=copy")
  290. let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn
  291. result = c.genCopyNoCheck(dest, ri, a)
  292. assert ri.typ != nil
  293. proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
  294. # discriminator is ordinal value that doesn't need sink destroy
  295. # but fields within active case branch might need destruction
  296. # tmp to support self assignments
  297. let tmp = c.getTemp(s, n[1].typ, n.info)
  298. result = newTree(nkStmtList)
  299. result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
  300. result.add p(n[0], c, s, normal)
  301. let le = p(n[0], c, s, normal)
  302. let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le
  303. let objType = leDotExpr[0].typ
  304. if hasDestructor(c, objType):
  305. if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
  306. sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
  307. localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
  308. It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
  309. result.add newTree(nkFastAsgn, le, tmp)
  310. return
  311. # generate: if le != tmp: `=destroy`(le)
  312. if c.inUncheckedAssignSection != 0:
  313. let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
  314. let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
  315. cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
  316. cond.add le
  317. cond.add tmp
  318. let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
  319. notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
  320. notExpr.add cond
  321. result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
  322. result.add newTree(nkFastAsgn, le, tmp)
  323. proc genWasMoved(c: var Con, n: PNode): PNode =
  324. let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
  325. let op = getAttachedOp(c.graph, n.typ, attachedWasMoved)
  326. if op != nil:
  327. if sfError in op.flags:
  328. c.checkForErrorPragma(n.typ, n, "=wasMoved")
  329. result = genOp(c, op, n)
  330. else:
  331. result = newNodeI(nkCall, n.info)
  332. result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
  333. result.add copyTree(n) #mWasMoved does not take the address
  334. #if n.kind != nkSym:
  335. # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
  336. proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
  337. result = newNodeI(nkCall, info)
  338. result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
  339. result.typ = t
  340. proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
  341. # generate: (let tmp = v; reset(v); tmp)
  342. if not hasDestructor(c, n.typ):
  343. assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
  344. result = copyTree(n)
  345. else:
  346. result = newNodeIT(nkStmtListExpr, n.info, n.typ)
  347. var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info)
  348. temp.typ = n.typ
  349. var v = newNodeI(nkLetSection, n.info)
  350. let tempAsNode = newSymNode(temp)
  351. var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
  352. vpart[0] = tempAsNode
  353. vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
  354. vpart[2] = n
  355. v.add(vpart)
  356. result.add v
  357. let nn = skipConv(n)
  358. c.genMarkCyclic(result, nn)
  359. let wasMovedCall = c.genWasMoved(nn)
  360. result.add wasMovedCall
  361. result.add tempAsNode
  362. proc isCapturedVar(n: PNode): bool =
  363. let root = getRoot(n)
  364. if root != nil: result = root.name.s[0] == ':'
  365. proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
  366. result = newNodeIT(nkStmtListExpr, n.info, n.typ)
  367. let tmp = c.getTemp(s, n.typ, n.info)
  368. if hasDestructor(c, n.typ):
  369. result.add c.genWasMoved(tmp)
  370. var m = c.genCopy(tmp, n, {})
  371. m.add p(n, c, s, normal)
  372. c.finishCopy(m, n, isFromSink = true)
  373. result.add m
  374. if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
  375. message(c.graph.config, n.info, hintPerformance,
  376. ("passing '$1' to a sink parameter introduces an implicit copy; " &
  377. "if possible, rearrange your program's control flow to prevent it") % $n)
  378. else:
  379. if c.graph.config.selectedGC in {gcArc, gcOrc}:
  380. assert(not containsManagedMemory(n.typ))
  381. if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
  382. localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
  383. result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
  384. # Since we know somebody will take over the produced copy, there is
  385. # no need to destroy it.
  386. result.add tmp
  387. proc isDangerousSeq(t: PType): bool {.inline.} =
  388. let t = t.skipTypes(abstractInst)
  389. result = t.kind == tySequence and tfHasOwned notin t[0].flags
  390. proc containsConstSeq(n: PNode): bool =
  391. if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
  392. return true
  393. result = false
  394. case n.kind
  395. of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
  396. result = containsConstSeq(n[1])
  397. of nkObjConstr, nkClosure:
  398. for i in 1..<n.len:
  399. if containsConstSeq(n[i]): return true
  400. of nkCurly, nkBracket, nkPar, nkTupleConstr:
  401. for son in n:
  402. if containsConstSeq(son): return true
  403. else: discard
  404. proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
  405. # it can happen that we need to destroy expression contructors
  406. # like [], (), closures explicitly in order to not leak them.
  407. if arg.typ != nil and hasDestructor(c, arg.typ):
  408. # produce temp creation for (fn, env). But we need to move 'env'?
  409. # This was already done in the sink parameter handling logic.
  410. result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
  411. let tmp = c.getTemp(s, arg.typ, arg.info)
  412. result.add c.genSink(s, tmp, arg, {IsDecl})
  413. result.add tmp
  414. s.final.add c.genDestroy(tmp)
  415. else:
  416. result = arg
  417. proc cycleCheck(n: PNode; c: var Con) =
  418. if c.graph.config.selectedGC != gcArc: return
  419. var value = n[1]
  420. if value.kind == nkClosure:
  421. value = value[1]
  422. if value.kind == nkNilLit: return
  423. let destTyp = n[0].typ.skipTypes(abstractInst)
  424. if destTyp.kind != tyRef and not (destTyp.kind == tyProc and destTyp.callConv == ccClosure):
  425. return
  426. var x = n[0]
  427. var field: PNode = nil
  428. while true:
  429. if x.kind == nkDotExpr:
  430. field = x[1]
  431. if field.kind == nkSym and sfCursor in field.sym.flags: return
  432. x = x[0]
  433. elif x.kind in {nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref}:
  434. x = x[0]
  435. else:
  436. break
  437. if exprStructuralEquivalent(x, value, strictSymEquality = true):
  438. let msg =
  439. if field != nil:
  440. "'$#' creates an uncollectable ref cycle; annotate '$#' with .cursor" % [$n, $field]
  441. else:
  442. "'$#' creates an uncollectable ref cycle" % [$n]
  443. message(c.graph.config, n.info, warnCycleCreated, msg)
  444. break
  445. proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
  446. # move the variable declaration to the top of the frame:
  447. s.vars.add v.sym
  448. if isUnpackedTuple(v):
  449. if c.inLoop > 0:
  450. # unpacked tuple needs reset at every loop iteration
  451. res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
  452. elif sfThread notin v.sym.flags and sfCursor notin v.sym.flags:
  453. # do not destroy thread vars for now at all for consistency.
  454. if {sfGlobal, sfPure} <= v.sym.flags or sfGlobal in v.sym.flags and s.parent == nil:
  455. c.graph.globalDestructors.add c.genDestroy(v)
  456. else:
  457. s.final.add c.genDestroy(v)
  458. proc processScope(c: var Con; s: var Scope; ret: PNode): PNode =
  459. result = newNodeI(nkStmtList, ret.info)
  460. if s.vars.len > 0:
  461. let varSection = newNodeI(nkVarSection, ret.info)
  462. for tmp in s.vars:
  463. varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info),
  464. newNodeI(nkEmpty, ret.info))
  465. result.add varSection
  466. if s.wasMoved.len > 0 or s.final.len > 0:
  467. let finSection = newNodeI(nkStmtList, ret.info)
  468. for m in s.wasMoved: finSection.add m
  469. for i in countdown(s.final.high, 0): finSection.add s.final[i]
  470. if s.needsTry:
  471. result.add newTryFinally(ret, finSection)
  472. else:
  473. result.add ret
  474. result.add finSection
  475. else:
  476. result.add ret
  477. if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
  478. template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped, tmpFlags: TSymFlags): PNode =
  479. assert not ret.typ.isEmptyType
  480. var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ)
  481. # There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
  482. # later and use it to eliminate the temporary when theres no need for it, but its
  483. # tricky because you would have to intercept moveOrCopy at a certain point
  484. let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
  485. tmp.sym.flags = tmpFlags
  486. let cpy = if hasDestructor(c, ret.typ):
  487. s.parent[].final.add c.genDestroy(tmp)
  488. moveOrCopy(tmp, ret, c, s, {IsDecl})
  489. else:
  490. newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
  491. if s.vars.len > 0:
  492. let varSection = newNodeI(nkVarSection, ret.info)
  493. for tmp in s.vars:
  494. varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info),
  495. newNodeI(nkEmpty, ret.info))
  496. result.add varSection
  497. let finSection = newNodeI(nkStmtList, ret.info)
  498. for m in s.wasMoved: finSection.add m
  499. for i in countdown(s.final.high, 0): finSection.add s.final[i]
  500. if s.needsTry:
  501. result.add newTryFinally(newTree(nkStmtListExpr, cpy, processCall(tmp, s.parent[])), finSection)
  502. else:
  503. result.add cpy
  504. result.add finSection
  505. result.add processCall(tmp, s.parent[])
  506. if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
  507. result
  508. template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
  509. tmpFlags = {sfSingleUsedTemp}) =
  510. template maybeVoid(child, s): untyped =
  511. if isEmptyType(child.typ): p(child, c, s, normal)
  512. else: processCall(child, s)
  513. case n.kind
  514. of nkStmtList, nkStmtListExpr:
  515. # a statement list does not open a new scope
  516. if n.len == 0: return n
  517. result = copyNode(n)
  518. for i in 0..<n.len-1:
  519. result.add p(n[i], c, s, normal)
  520. result.add maybeVoid(n[^1], s)
  521. of nkCaseStmt:
  522. result = copyNode(n)
  523. result.add p(n[0], c, s, normal)
  524. for i in 1..<n.len:
  525. let it = n[i]
  526. assert it.kind in {nkOfBranch, nkElse}
  527. var branch = shallowCopy(it)
  528. for j in 0 ..< it.len-1:
  529. branch[j] = copyTree(it[j])
  530. var ofScope = nestedScope(s, it.lastSon)
  531. branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
  532. processScope(c, ofScope, maybeVoid(it[^1], ofScope))
  533. else:
  534. processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
  535. result.add branch
  536. of nkWhileStmt:
  537. inc c.inLoop
  538. inc c.inLoopCond
  539. result = copyNode(n)
  540. result.add p(n[0], c, s, normal)
  541. dec c.inLoopCond
  542. var bodyScope = nestedScope(s, n[1])
  543. let bodyResult = p(n[1], c, bodyScope, normal)
  544. result.add processScope(c, bodyScope, bodyResult)
  545. dec c.inLoop
  546. of nkParForStmt:
  547. inc c.inLoop
  548. result = shallowCopy(n)
  549. let last = n.len-1
  550. for i in 0..<last-1:
  551. result[i] = n[i]
  552. result[last-1] = p(n[last-1], c, s, normal)
  553. var bodyScope = nestedScope(s, n[1])
  554. let bodyResult = p(n[last], c, bodyScope, normal)
  555. result[last] = processScope(c, bodyScope, bodyResult)
  556. dec c.inLoop
  557. of nkBlockStmt, nkBlockExpr:
  558. result = copyNode(n)
  559. result.add n[0]
  560. var bodyScope = nestedScope(s, n[1])
  561. result.add if n[1].typ.isEmptyType or willProduceStmt:
  562. processScope(c, bodyScope, processCall(n[1], bodyScope))
  563. else:
  564. processScopeExpr(c, bodyScope, n[1], processCall, tmpFlags)
  565. of nkIfStmt, nkIfExpr:
  566. result = copyNode(n)
  567. for i in 0..<n.len:
  568. let it = n[i]
  569. var branch = shallowCopy(it)
  570. var branchScope = nestedScope(s, it.lastSon)
  571. if it.kind in {nkElifBranch, nkElifExpr}:
  572. #Condition needs to be destroyed outside of the condition/branch scope
  573. branch[0] = p(it[0], c, s, normal)
  574. branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
  575. processScope(c, branchScope, maybeVoid(it[^1], branchScope))
  576. else:
  577. processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
  578. result.add branch
  579. of nkTryStmt:
  580. result = copyNode(n)
  581. var tryScope = nestedScope(s, n[0])
  582. result.add if n[0].typ.isEmptyType or willProduceStmt:
  583. processScope(c, tryScope, maybeVoid(n[0], tryScope))
  584. else:
  585. processScopeExpr(c, tryScope, n[0], maybeVoid, tmpFlags)
  586. for i in 1..<n.len:
  587. let it = n[i]
  588. var branch = copyTree(it)
  589. var branchScope = nestedScope(s, it[^1])
  590. branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
  591. processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
  592. else: maybeVoid(it[^1], branchScope))
  593. else:
  594. processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
  595. result.add branch
  596. of nkWhen: # This should be a "when nimvm" node.
  597. result = copyTree(n)
  598. result[1][0] = processCall(n[1][0], s)
  599. else: assert(false)
  600. proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
  601. if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
  602. if n[0].kind in nkCallKinds:
  603. let call = p(n[0], c, s, normal)
  604. result = copyNode(n)
  605. result.add call
  606. else:
  607. let tmp = c.getTemp(s, n[0].typ, n.info)
  608. var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
  609. m.add p(n[0], c, s, normal)
  610. c.finishCopy(m, n[0], isFromSink = false)
  611. result = newTree(nkStmtList, c.genWasMoved(tmp), m)
  612. var toDisarm = n[0]
  613. if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
  614. if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
  615. result.add c.genWasMoved(toDisarm)
  616. result.add newTree(nkRaiseStmt, tmp)
  617. else:
  618. result = copyNode(n)
  619. if n[0].kind != nkEmpty:
  620. result.add p(n[0], c, s, sinkArg)
  621. else:
  622. result.add copyNode(n[0])
  623. s.needsTry = true
  624. proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode =
  625. if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
  626. nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt}:
  627. template process(child, s): untyped = p(child, c, s, mode)
  628. handleNestedTempl(n, process, tmpFlags = tmpFlags)
  629. elif mode == sinkArg:
  630. if n.containsConstSeq:
  631. # const sequences are not mutable and so we need to pass a copy to the
  632. # sink parameter (bug #11524). Note that the string implementation is
  633. # different and can deal with 'const string sunk into var'.
  634. result = passCopyToSink(n, c, s)
  635. elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
  636. nkCallKinds + nkLiterals:
  637. result = p(n, c, s, consumed)
  638. elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
  639. isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
  640. # Sinked params can be consumed only once. We need to reset the memory
  641. # to disable the destructor which we have not elided
  642. result = destructiveMoveVar(n, c, s)
  643. elif n.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}:
  644. result = copyTree(n)
  645. if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and
  646. n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
  647. # allow conversions from owned to unowned via this little hack:
  648. let nTyp = n[1].typ
  649. n[1].typ = n.typ
  650. result[1] = p(n[1], c, s, sinkArg)
  651. result[1].typ = nTyp
  652. else:
  653. result[1] = p(n[1], c, s, sinkArg)
  654. elif n.kind in {nkObjDownConv, nkObjUpConv}:
  655. result = copyTree(n)
  656. result[0] = p(n[0], c, s, sinkArg)
  657. elif n.typ == nil:
  658. # 'raise X' can be part of a 'case' expression. Deal with it here:
  659. result = p(n, c, s, normal)
  660. else:
  661. # copy objects that are not temporary but passed to a 'sink' parameter
  662. result = passCopyToSink(n, c, s)
  663. else:
  664. case n.kind
  665. of nkBracket, nkTupleConstr, nkClosure, nkCurly:
  666. # Let C(x) be the construction, 'x' the vector of arguments.
  667. # C(x) either owns 'x' or it doesn't.
  668. # If C(x) owns its data, we must consume C(x).
  669. # If it doesn't own the data, it's harmful to destroy it (double frees etc).
  670. # We have the freedom to choose whether it owns it or not so we are smart about it
  671. # and we say, "if passed to a sink we demand C(x) to own its data"
  672. # otherwise we say "C(x) is just some temporary storage, it doesn't own anything,
  673. # don't destroy it"
  674. # but if C(x) is a ref it MUST own its data since we must destroy it
  675. # so then we have no choice but to use 'sinkArg'.
  676. let m = if mode == normal: normal
  677. else: sinkArg
  678. result = copyTree(n)
  679. for i in ord(n.kind == nkClosure)..<n.len:
  680. if n[i].kind == nkExprColonExpr:
  681. result[i][1] = p(n[i][1], c, s, m)
  682. elif n[i].kind == nkRange:
  683. result[i][0] = p(n[i][0], c, s, m)
  684. result[i][1] = p(n[i][1], c, s, m)
  685. else:
  686. result[i] = p(n[i], c, s, m)
  687. of nkObjConstr:
  688. # see also the remark about `nkTupleConstr`.
  689. let t = n.typ.skipTypes(abstractInst)
  690. let isRefConstr = t.kind == tyRef
  691. let m = if isRefConstr: sinkArg
  692. elif mode == normal: normal
  693. else: sinkArg
  694. result = copyTree(n)
  695. for i in 1..<n.len:
  696. if n[i].kind == nkExprColonExpr:
  697. let field = lookupFieldAgain(t, n[i][0].sym)
  698. if field != nil and sfCursor in field.flags:
  699. result[i][1] = p(n[i][1], c, s, normal)
  700. else:
  701. result[i][1] = p(n[i][1], c, s, m)
  702. else:
  703. result[i] = p(n[i], c, s, m)
  704. if mode == normal and isRefConstr:
  705. result = ensureDestruction(result, n, c, s)
  706. of nkCallKinds:
  707. let inSpawn = c.inSpawn
  708. if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
  709. c.inSpawn.inc
  710. elif c.inSpawn > 0:
  711. c.inSpawn.dec
  712. let parameters = n[0].typ
  713. let L = if parameters != nil: parameters.len else: 0
  714. when false:
  715. var isDangerous = false
  716. if n[0].kind == nkSym and n[0].sym.magic in {mOr, mAnd}:
  717. inc c.inDangerousBranch
  718. isDangerous = true
  719. result = shallowCopy(n)
  720. for i in 1..<n.len:
  721. if i < L and isCompileTimeOnly(parameters[i]):
  722. result[i] = n[i]
  723. elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
  724. result[i] = p(n[i], c, s, sinkArg)
  725. else:
  726. result[i] = p(n[i], c, s, normal)
  727. when false:
  728. if isDangerous:
  729. dec c.inDangerousBranch
  730. if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
  731. result[0] = copyTree(n[0])
  732. if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
  733. let destroyOld = c.genDestroy(result[1])
  734. result = newTree(nkStmtList, destroyOld, result)
  735. else:
  736. result[0] = p(n[0], c, s, normal)
  737. if canRaise(n[0]): s.needsTry = true
  738. if mode == normal:
  739. result = ensureDestruction(result, n, c, s)
  740. of nkDiscardStmt: # Small optimization
  741. result = shallowCopy(n)
  742. if n[0].kind != nkEmpty:
  743. result[0] = p(n[0], c, s, normal)
  744. else:
  745. result[0] = copyNode(n[0])
  746. of nkVarSection, nkLetSection:
  747. # transform; var x = y to var x; x op y where op is a move or copy
  748. result = newNodeI(nkStmtList, n.info)
  749. for it in n:
  750. var ri = it[^1]
  751. if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
  752. for i in 0..<it.len-2:
  753. if it[i].kind == nkSym: s.locals.add it[i].sym
  754. let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
  755. result.add p(x, c, s, consumed)
  756. elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ):
  757. for j in 0..<it.len-2:
  758. let v = skipPragmaExpr(it[j])
  759. if v.kind == nkSym:
  760. if sfCompileTime in v.sym.flags: continue
  761. s.locals.add v.sym
  762. pVarTopLevel(v, c, s, result)
  763. if ri.kind != nkEmpty:
  764. result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
  765. elif ri.kind == nkEmpty and c.inLoop > 0:
  766. let skipInit = v.kind == nkDotExpr and # Closure var
  767. sfNoInit in v[1].sym.flags
  768. if not skipInit:
  769. result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {})
  770. else: # keep the var but transform 'ri':
  771. var v = copyNode(n)
  772. var itCopy = copyNode(it)
  773. for j in 0..<it.len-1:
  774. itCopy.add it[j]
  775. var flags = {sfSingleUsedTemp}
  776. if it.kind == nkIdentDefs and it.len == 3 and it[0].kind == nkSym and
  777. sfGlobal in it[0].sym.flags:
  778. flags.incl sfGlobal
  779. itCopy.add p(it[^1], c, s, normal, tmpFlags = flags)
  780. v.add itCopy
  781. result.add v
  782. of nkAsgn, nkFastAsgn, nkSinkAsgn:
  783. if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
  784. if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
  785. cycleCheck(n, c)
  786. assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
  787. let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
  788. result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
  789. elif isDiscriminantField(n[0]):
  790. result = c.genDiscriminantAsgn(s, n)
  791. else:
  792. result = copyNode(n)
  793. result.add p(n[0], c, s, mode)
  794. result.add p(n[1], c, s, consumed)
  795. of nkRaiseStmt:
  796. result = pRaiseStmt(n, c, s)
  797. of nkWhileStmt:
  798. internalError(c.graph.config, n.info, "nkWhileStmt should have been handled earlier")
  799. result = n
  800. of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
  801. nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
  802. nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
  803. nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
  804. nkTypeOfExpr, nkMixinStmt, nkBindStmt:
  805. result = n
  806. of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange:
  807. result = shallowCopy(n)
  808. for i in 0 ..< n.len:
  809. result[i] = p(n[i], c, s, normal)
  810. if n.typ != nil and hasDestructor(c, n.typ):
  811. if mode == normal:
  812. result = ensureDestruction(result, n, c, s)
  813. of nkPragmaBlock:
  814. var inUncheckedAssignSection = 0
  815. let pragmaList = n[0]
  816. for pi in pragmaList:
  817. if whichPragma(pi) == wCast:
  818. case whichPragma(pi[1])
  819. of wUncheckedAssign:
  820. inUncheckedAssignSection = 1
  821. else:
  822. discard
  823. result = shallowCopy(n)
  824. inc c.inUncheckedAssignSection, inUncheckedAssignSection
  825. for i in 0 ..< n.len:
  826. result[i] = p(n[i], c, s, normal)
  827. dec c.inUncheckedAssignSection, inUncheckedAssignSection
  828. if n.typ != nil and hasDestructor(c, n.typ):
  829. if mode == normal:
  830. result = ensureDestruction(result, n, c, s)
  831. of nkHiddenSubConv, nkHiddenStdConv, nkConv:
  832. # we have an "ownership invariance" for all constructors C(x).
  833. # See the comment for nkBracket construction. If the caller wants
  834. # to own 'C(x)', it really wants to own 'x' too. If it doesn't,
  835. # we need to destroy 'x' but the function call handling ensures that
  836. # already.
  837. result = copyTree(n)
  838. if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and
  839. n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
  840. # allow conversions from owned to unowned via this little hack:
  841. let nTyp = n[1].typ
  842. n[1].typ = n.typ
  843. result[1] = p(n[1], c, s, mode)
  844. result[1].typ = nTyp
  845. else:
  846. result[1] = p(n[1], c, s, mode)
  847. of nkObjDownConv, nkObjUpConv:
  848. result = copyTree(n)
  849. result[0] = p(n[0], c, s, mode)
  850. of nkDotExpr:
  851. result = shallowCopy(n)
  852. result[0] = p(n[0], c, s, normal)
  853. for i in 1 ..< n.len:
  854. result[i] = n[i]
  855. if mode == sinkArg and hasDestructor(c, n.typ):
  856. if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
  857. s.wasMoved.add c.genWasMoved(n)
  858. else:
  859. result = passCopyToSink(result, c, s)
  860. of nkBracketExpr, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref:
  861. result = shallowCopy(n)
  862. for i in 0 ..< n.len:
  863. result[i] = p(n[i], c, s, normal)
  864. if mode == sinkArg and hasDestructor(c, n.typ):
  865. if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
  866. # consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
  867. # without the junk, hence 'c.genWasMoved(n)'
  868. # and not 'c.genWasMoved(result)':
  869. s.wasMoved.add c.genWasMoved(n)
  870. else:
  871. result = passCopyToSink(result, c, s)
  872. of nkDefer, nkRange:
  873. result = shallowCopy(n)
  874. for i in 0 ..< n.len:
  875. result[i] = p(n[i], c, s, normal)
  876. of nkBreakStmt:
  877. s.needsTry = true
  878. result = n
  879. of nkReturnStmt:
  880. result = shallowCopy(n)
  881. for i in 0..<n.len:
  882. result[i] = p(n[i], c, s, mode)
  883. s.needsTry = true
  884. of nkCast:
  885. result = shallowCopy(n)
  886. result[0] = n[0]
  887. result[1] = p(n[1], c, s, mode)
  888. of nkCheckedFieldExpr:
  889. result = shallowCopy(n)
  890. result[0] = p(n[0], c, s, mode)
  891. for i in 1..<n.len:
  892. result[i] = n[i]
  893. of nkGotoState, nkState, nkAsmStmt:
  894. result = n
  895. else:
  896. internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind)
  897. proc sameLocation*(a, b: PNode): bool =
  898. proc sameConstant(a, b: PNode): bool =
  899. a.kind in nkLiterals and a.intVal == b.intVal
  900. const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
  901. if a.kind in nkEndPoint and b.kind in nkEndPoint:
  902. if a.kind == b.kind:
  903. case a.kind
  904. of nkSym: a.sym == b.sym
  905. of nkDotExpr, nkCheckedFieldExpr: sameLocation(a[0], b[0]) and sameLocation(a[1], b[1])
  906. of nkBracketExpr: sameLocation(a[0], b[0]) and sameConstant(a[1], b[1])
  907. else: false
  908. else: false
  909. else:
  910. case a.kind
  911. of nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
  912. # Reached an endpoint, flip to recurse the other side.
  913. sameLocation(b, a)
  914. of nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref:
  915. # We don't need to check addr/deref levels or differentiate between the two,
  916. # since pointers don't have hooks :) (e.g: var p: ptr pointer; p[] = addr p)
  917. sameLocation(a[0], b)
  918. of nkObjDownConv, nkObjUpConv: sameLocation(a[0], b)
  919. of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
  920. else: false
  921. proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
  922. # with side effects
  923. var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
  924. temp.typ = ri[1].typ
  925. var v = newNodeI(nkLetSection, ri[1].info)
  926. let tempAsNode = newSymNode(temp)
  927. var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
  928. vpart[0] = tempAsNode
  929. vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
  930. vpart[2] = ri[1]
  931. v.add(vpart)
  932. var newAccess = copyNode(ri)
  933. newAccess.add ri[0]
  934. newAccess.add tempAsNode
  935. var snk = c.genSink(s, dest, newAccess, flags)
  936. result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
  937. proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
  938. if sameLocation(dest, ri):
  939. # rule (self-assignment-removal):
  940. result = newNodeI(nkEmpty, dest.info)
  941. elif isCursor(dest):
  942. case ri.kind:
  943. of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
  944. template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
  945. # We know the result will be a stmt so we use that fact to optimize
  946. handleNestedTempl(ri, process, willProduceStmt = true)
  947. else:
  948. result = newTree(nkFastAsgn, dest, p(ri, c, s, normal))
  949. else:
  950. case ri.kind
  951. of nkCallKinds:
  952. result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
  953. of nkBracketExpr:
  954. if isUnpackedTuple(ri[0]):
  955. # unpacking of tuple: take over the elements
  956. result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
  957. elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
  958. if aliases(dest, ri) == no:
  959. # Rule 3: `=sink`(x, z); wasMoved(z)
  960. if isAtom(ri[1]):
  961. var snk = c.genSink(s, dest, ri, flags)
  962. result = newTree(nkStmtList, snk, c.genWasMoved(ri))
  963. else:
  964. result = genFieldAccessSideEffects(c, s, dest, ri, flags)
  965. else:
  966. result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags)
  967. else:
  968. result = c.genCopy(dest, ri, flags)
  969. result.add p(ri, c, s, consumed)
  970. c.finishCopy(result, dest, isFromSink = false)
  971. of nkBracket:
  972. # array constructor
  973. if ri.len > 0 and isDangerousSeq(ri.typ):
  974. result = c.genCopy(dest, ri, flags)
  975. result.add p(ri, c, s, consumed)
  976. c.finishCopy(result, dest, isFromSink = false)
  977. else:
  978. result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
  979. of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
  980. result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
  981. of nkSym:
  982. if isSinkParam(ri.sym) and isLastRead(ri, c, s):
  983. # Rule 3: `=sink`(x, z); wasMoved(z)
  984. let snk = c.genSink(s, dest, ri, flags)
  985. result = newTree(nkStmtList, snk, c.genWasMoved(ri))
  986. elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
  987. isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
  988. # Rule 3: `=sink`(x, z); wasMoved(z)
  989. let snk = c.genSink(s, dest, ri, flags)
  990. result = newTree(nkStmtList, snk, c.genWasMoved(ri))
  991. else:
  992. result = c.genCopy(dest, ri, flags)
  993. result.add p(ri, c, s, consumed)
  994. c.finishCopy(result, dest, isFromSink = false)
  995. of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
  996. result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
  997. of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
  998. template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
  999. # We know the result will be a stmt so we use that fact to optimize
  1000. handleNestedTempl(ri, process, willProduceStmt = true)
  1001. of nkRaiseStmt:
  1002. result = pRaiseStmt(ri, c, s)
  1003. else:
  1004. if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
  1005. canBeMoved(c, dest.typ):
  1006. # Rule 3: `=sink`(x, z); wasMoved(z)
  1007. let snk = c.genSink(s, dest, ri, flags)
  1008. result = newTree(nkStmtList, snk, c.genWasMoved(ri))
  1009. else:
  1010. result = c.genCopy(dest, ri, flags)
  1011. result.add p(ri, c, s, consumed)
  1012. c.finishCopy(result, dest, isFromSink = false)
  1013. when false:
  1014. proc computeUninit(c: var Con) =
  1015. if not c.uninitComputed:
  1016. c.uninitComputed = true
  1017. c.uninit = initIntSet()
  1018. var init = initIntSet()
  1019. discard initialized(c.g, pc = 0, init, c.uninit, int.high)
  1020. proc injectDefaultCalls(n: PNode, c: var Con) =
  1021. case n.kind
  1022. of nkVarSection, nkLetSection:
  1023. for it in n:
  1024. if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
  1025. computeUninit(c)
  1026. for j in 0..<it.len-2:
  1027. let v = skipPragmaExpr(it[j])
  1028. doAssert v.kind == nkSym
  1029. if c.uninit.contains(v.sym.id):
  1030. it[^1] = genDefaultCall(v.sym.typ, c, v.info)
  1031. break
  1032. of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
  1033. nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
  1034. discard
  1035. else:
  1036. for i in 0..<n.safeLen:
  1037. injectDefaultCalls(n[i], c)
  1038. proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
  1039. when toDebug.len > 0:
  1040. shouldDebug = toDebug == owner.name.s or toDebug == "always"
  1041. if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
  1042. return n
  1043. var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo)
  1044. if optCursorInference in g.config.options:
  1045. computeCursors(owner, n, g)
  1046. var scope = Scope(body: n)
  1047. let body = p(n, c, scope, normal)
  1048. if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
  1049. let params = owner.typ.n
  1050. for i in 1..<params.len:
  1051. let t = params[i].sym.typ
  1052. if isSinkTypeForParam(t) and hasDestructor(c, t.skipTypes({tySink})):
  1053. scope.final.add c.genDestroy(params[i])
  1054. #if optNimV2 in c.graph.config.globalOptions:
  1055. # injectDefaultCalls(n, c)
  1056. result = optimize processScope(c, scope, body)
  1057. dbg:
  1058. echo ">---------transformed-to--------->"
  1059. echo renderTree(result, {renderIds})
  1060. if g.config.arcToExpand.hasKey(owner.name.s):
  1061. echo "--expandArc: ", owner.name.s
  1062. echo renderTree(result, {renderIr, renderNoComments})
  1063. echo "-- end of expandArc ------------------------"