injectdestructors.nim 50 KB

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