injectdestructors.nim 48 KB

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