liftdestructors.nim 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements lifting for type-bound operations
  10. ## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
  11. import modulegraphs, lineinfos, idents, ast, renderer, semdata,
  12. sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
  13. from trees import isCaseObj
  14. type
  15. TLiftCtx = object
  16. g: ModuleGraph
  17. info: TLineInfo # for construction
  18. kind: TTypeAttachedOp
  19. fn: PSym
  20. asgnForType: PType
  21. recurse: bool
  22. addMemReset: bool # add wasMoved() call after destructor call
  23. canRaise: bool
  24. filterDiscriminator: PSym # we generating destructor for case branch
  25. c: PContext # c can be nil, then we are called from lambdalifting!
  26. idgen: IdGenerator
  27. template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
  28. template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
  29. template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
  30. proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
  31. proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
  32. info: TLineInfo; idgen: IdGenerator): PSym
  33. proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
  34. idgen: IdGenerator)
  35. proc at(a, i: PNode, elemType: PType): PNode =
  36. result = newNodeI(nkBracketExpr, a.info, 2)
  37. result[0] = a
  38. result[1] = i
  39. result.typ = elemType
  40. proc destructorOverriden(g: ModuleGraph; t: PType): bool =
  41. let op = getAttachedOp(g, t, attachedDestructor)
  42. op != nil and sfOverriden in op.flags
  43. proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  44. for i in 0..<t.len:
  45. let lit = lowerings.newIntLit(c.g, x.info, i)
  46. let b = if c.kind == attachedTrace: y else: y.at(lit, t[i])
  47. fillBody(c, t[i], body, x.at(lit, t[i]), b)
  48. proc dotField(x: PNode, f: PSym): PNode =
  49. result = newNodeI(nkDotExpr, x.info, 2)
  50. if x.typ.skipTypes(abstractInst).kind == tyVar:
  51. result[0] = x.newDeref
  52. else:
  53. result[0] = x
  54. result[1] = newSymNode(f, x.info)
  55. result.typ = f.typ
  56. proc newAsgnStmt(le, ri: PNode): PNode =
  57. result = newNodeI(nkAsgn, le.info, 2)
  58. result[0] = le
  59. result[1] = ri
  60. proc genBuiltin*(g: ModuleGraph; idgen: IdGenerator; magic: TMagic; name: string; i: PNode): PNode =
  61. result = newNodeI(nkCall, i.info)
  62. result.add createMagic(g, idgen, name, magic).newSymNode
  63. result.add i
  64. proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode =
  65. result = genBuiltin(c.g, c.idgen, magic, name, i)
  66. proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  67. if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
  68. body.add newAsgnStmt(x, y)
  69. elif c.kind == attachedDestructor and c.addMemReset:
  70. let call = genBuiltin(c, mDefault, "default", x)
  71. call.typ = t
  72. body.add newAsgnStmt(x, call)
  73. proc genAddr(c: var TLiftCtx; x: PNode): PNode =
  74. if x.kind == nkHiddenDeref:
  75. checkSonsLen(x, 1, c.g.config)
  76. result = x[0]
  77. else:
  78. result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ, c.idgen))
  79. result.add x
  80. proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
  81. result = newNodeI(nkWhileStmt, c.info, 2)
  82. let cmp = genBuiltin(c, mLtI, "<", i)
  83. cmp.add genLen(c.g, dest)
  84. cmp.typ = getSysType(c.g, c.info, tyBool)
  85. result[0] = cmp
  86. result[1] = newNodeI(nkStmtList, c.info)
  87. proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
  88. result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
  89. proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
  90. # generate: cast[ptr ObjType](cast[int](addr(x)) - offsetOf(objType.field))
  91. let intType = getSysType(c.g, unknownLineInfo, tyInt)
  92. let addrOf = newNodeIT(nkAddr, c.info, makePtrType(x.owner, x.typ, c.idgen))
  93. addrOf.add newDeref(newSymNode(x))
  94. let castExpr1 = newNodeIT(nkCast, c.info, intType)
  95. castExpr1.add newNodeIT(nkType, c.info, intType)
  96. castExpr1.add addrOf
  97. let dotExpr = newNodeIT(nkDotExpr, c.info, x.typ)
  98. dotExpr.add newNodeIT(nkType, c.info, objType)
  99. dotExpr.add newSymNode(field)
  100. let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
  101. offsetOf.typ = intType
  102. let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
  103. minusExpr.typ = intType
  104. minusExpr.add offsetOf
  105. let objPtr = makePtrType(objType.owner, objType, c.idgen)
  106. result = newNodeIT(nkCast, c.info, objPtr)
  107. result.add newNodeIT(nkType, c.info, objPtr)
  108. result.add minusExpr
  109. proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
  110. var destroy = newNodeIT(nkCall, x.info, op.typ[0])
  111. destroy.add(newSymNode(op))
  112. destroy.add genAddr(c, x)
  113. if sfNeverRaises notin op.flags:
  114. c.canRaise = true
  115. if c.addMemReset:
  116. result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
  117. else:
  118. result = destroy
  119. proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
  120. case n.kind
  121. of nkSym:
  122. if c.filterDiscriminator != nil: return
  123. let f = n.sym
  124. let b = if c.kind == attachedTrace: y else: y.dotField(f)
  125. if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
  126. c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}) or
  127. enforceDefaultOp:
  128. defaultOp(c, f.typ, body, x.dotField(f), b)
  129. else:
  130. fillBody(c, f.typ, body, x.dotField(f), b)
  131. of nkNilLit: discard
  132. of nkRecCase:
  133. # XXX This is only correct for 'attachedSink'!
  134. var localEnforceDefaultOp = enforceDefaultOp
  135. if c.kind == attachedSink:
  136. # the value needs to be destroyed before we assign the selector
  137. # or the value is lost
  138. let prevKind = c.kind
  139. let prevAddMemReset = c.addMemReset
  140. c.kind = attachedDestructor
  141. c.addMemReset = true
  142. fillBodyObj(c, n, body, x, y, enforceDefaultOp = false)
  143. c.kind = prevKind
  144. c.addMemReset = prevAddMemReset
  145. localEnforceDefaultOp = true
  146. if c.kind != attachedDestructor:
  147. # copy the selector before case stmt, but destroy after case stmt
  148. fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
  149. let oldfilterDiscriminator = c.filterDiscriminator
  150. if c.filterDiscriminator == n[0].sym:
  151. c.filterDiscriminator = nil # we have found the case part, proceed as normal
  152. # we need to generate a case statement:
  153. var caseStmt = newNodeI(nkCaseStmt, c.info)
  154. # XXX generate 'if' that checks same branches
  155. # generate selector:
  156. var access = dotField(x, n[0].sym)
  157. caseStmt.add(access)
  158. var emptyBranches = 0
  159. # copy the branches over, but replace the fields with the for loop body:
  160. for i in 1..<n.len:
  161. var branch = copyTree(n[i])
  162. branch[^1] = newNodeI(nkStmtList, c.info)
  163. fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
  164. enforceDefaultOp = localEnforceDefaultOp)
  165. if branch[^1].len == 0: inc emptyBranches
  166. caseStmt.add(branch)
  167. if emptyBranches != n.len-1:
  168. body.add(caseStmt)
  169. if c.kind == attachedDestructor:
  170. # destructor for selector is done after case stmt
  171. fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
  172. c.filterDiscriminator = oldfilterDiscriminator
  173. of nkRecList:
  174. for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
  175. else:
  176. illFormedAstLocal(n, c.g.config)
  177. proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
  178. if t.len > 0 and t[0] != nil:
  179. fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
  180. fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
  181. proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
  182. var hasCase = isCaseObj(t.n)
  183. var obj = t
  184. while obj.len > 0 and obj[0] != nil:
  185. obj = skipTypes(obj[0], abstractPtrs)
  186. hasCase = hasCase or isCaseObj(obj.n)
  187. if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
  188. # assignment for case objects is complex, we do:
  189. # =destroy(dest)
  190. # wasMoved(dest)
  191. # for every field:
  192. # `=` dest.field, src.field
  193. # ^ this is what we used to do, but for 'result = result.sons[0]' it
  194. # destroys 'result' too early.
  195. # So this is what we really need to do:
  196. # let blob {.cursor.} = dest # remembers the old dest.kind
  197. # wasMoved(dest)
  198. # dest.kind = src.kind
  199. # for every field (dependent on dest.kind):
  200. # `=` dest.field, src.field
  201. # =destroy(blob)
  202. var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
  203. dummy.typ = y.typ
  204. if ccgIntroducedPtr(c.g.config, dummy, y.typ):
  205. # Because of potential aliasing when the src param is passed by ref, we need to check for equality here,
  206. # because the wasMoved(dest) call would zero out src, if dest aliases src.
  207. var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
  208. newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
  209. cond.typ = getSysType(c.g, x.info, tyBool)
  210. body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
  211. var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
  212. temp.typ = x.typ
  213. incl(temp.flags, sfFromGeneric)
  214. var v = newNodeI(nkVarSection, c.info)
  215. let blob = newSymNode(temp)
  216. v.addVar(blob, x)
  217. body.add v
  218. #body.add newAsgnStmt(blob, x)
  219. var wasMovedCall = newNodeI(nkCall, c.info)
  220. wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
  221. wasMovedCall.add x # mWasMoved does not take the address
  222. body.add wasMovedCall
  223. fillBodyObjTImpl(c, t, body, x, y)
  224. when false:
  225. # does not work yet due to phase-ordering problems:
  226. assert t.destructor != nil
  227. body.add destructorCall(c.g, t.destructor, blob)
  228. let prevKind = c.kind
  229. c.kind = attachedDestructor
  230. fillBodyObjTImpl(c, t, body, blob, y)
  231. c.kind = prevKind
  232. else:
  233. fillBodyObjTImpl(c, t, body, x, y)
  234. proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
  235. result = newIntLit(g, info, ord value)
  236. result.typ = getSysType(g, info, tyBool)
  237. proc getCycleParam(c: TLiftCtx): PNode =
  238. assert c.kind == attachedAsgn
  239. if c.fn.typ.len == 4:
  240. result = c.fn.typ.n.lastSon
  241. assert result.kind == nkSym
  242. assert result.sym.name.s == "cyclic"
  243. else:
  244. result = boolLit(c.g, c.info, true)
  245. proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
  246. #if sfError in op.flags:
  247. # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
  248. result = newNodeI(nkCall, x.info)
  249. result.add newSymNode(op)
  250. if sfNeverRaises notin op.flags:
  251. c.canRaise = true
  252. if op.typ.sons[1].kind == tyVar:
  253. result.add genAddr(c, x)
  254. else:
  255. result.add x
  256. if y != nil:
  257. result.add y
  258. if op.typ.len == 4:
  259. assert y != nil
  260. if c.fn.typ.len == 4:
  261. result.add getCycleParam(c)
  262. else:
  263. # assume the worst: A cycle is created:
  264. result.add boolLit(c.g, y.info, true)
  265. proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
  266. result = newNodeIT(nkCall, x.info, op.typ[0])
  267. result.add(newSymNode(op))
  268. result.add x
  269. if sfNeverRaises notin op.flags:
  270. c.canRaise = true
  271. proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
  272. result = newAsgnStmt(x, newOpCall(c, op, y))
  273. proc usesBuiltinArc(t: PType): bool =
  274. proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t)
  275. result = types.searchTypeFor(t, wrap)
  276. proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
  277. result = optSeqDestructors in c.g.config.globalOptions and
  278. ({tfHasGCedMem, tfHasOwned} * t.flags != {} or usesBuiltinArc(t))
  279. proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
  280. result = optSeqDestructors in c.g.config.globalOptions and
  281. containsGarbageCollectedRef(t)
  282. proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
  283. if c.c != nil and typeInst != nil:
  284. result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
  285. else:
  286. localError(c.g.config, c.info,
  287. "cannot generate destructor for generic type: " & typeToString(t))
  288. result = nil
  289. proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
  290. field: var PSym): bool =
  291. if optSeqDestructors in c.g.config.globalOptions:
  292. var op = field
  293. let destructorOverriden = destructorOverriden(c.g, t)
  294. if op != nil and op != c.fn and
  295. (sfOverriden in op.flags or destructorOverriden):
  296. if sfError in op.flags:
  297. incl c.fn.flags, sfError
  298. #else:
  299. # markUsed(c.g.config, c.info, op, c.g.usageSym)
  300. onUse(c.info, op)
  301. body.add newHookCall(c, op, x, y)
  302. result = true
  303. elif op == nil and destructorOverriden:
  304. op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
  305. body.add newHookCall(c, op, x, y)
  306. result = true
  307. elif tfHasAsgn in t.flags:
  308. var op: PSym
  309. if sameType(t, c.asgnForType):
  310. # generate recursive call:
  311. if c.recurse:
  312. op = c.fn
  313. else:
  314. c.recurse = true
  315. return false
  316. else:
  317. op = field
  318. if op == nil:
  319. op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
  320. if sfError in op.flags:
  321. incl c.fn.flags, sfError
  322. #else:
  323. # markUsed(c.g.config, c.info, op, c.g.usageSym)
  324. onUse(c.info, op)
  325. # We also now do generic instantiations in the destructor lifting pass:
  326. if op.ast.isGenericRoutine:
  327. op = instantiateGeneric(c, op, t, t.typeInst)
  328. field = op
  329. #echo "trying to use ", op.ast
  330. #echo "for ", op.name.s, " "
  331. #debug(t)
  332. #return false
  333. assert op.ast[genericParamsPos].kind == nkEmpty
  334. body.add newHookCall(c, op, x, y)
  335. result = true
  336. proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
  337. let t = orig.skipTypes(abstractInst - {tyDistinct})
  338. var op = t.destructor
  339. if op != nil and sfOverriden in op.flags:
  340. if op.ast.isGenericRoutine:
  341. # patch generic destructor:
  342. op = instantiateGeneric(c, op, t, t.typeInst)
  343. setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
  344. if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
  345. op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
  346. doAssert op != nil
  347. doAssert op == t.destructor
  348. if op != nil:
  349. #markUsed(c.g.config, c.info, op, c.g.usageSym)
  350. onUse(c.info, op)
  351. body.add destructorCall(c, op, x)
  352. elif useNoGc(c, t):
  353. internalError(c.g.config, c.info,
  354. "type-bound operator could not be resolved")
  355. proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
  356. case c.kind
  357. of attachedDestructor:
  358. var op = t.destructor
  359. if op != nil and sfOverriden in op.flags:
  360. if op.ast.isGenericRoutine:
  361. # patch generic destructor:
  362. op = instantiateGeneric(c, op, t, t.typeInst)
  363. setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
  364. #markUsed(c.g.config, c.info, op, c.g.usageSym)
  365. onUse(c.info, op)
  366. body.add destructorCall(c, op, x)
  367. result = true
  368. #result = addDestructorCall(c, t, body, x)
  369. of attachedAsgn, attachedSink, attachedTrace:
  370. var op = getAttachedOp(c.g, t, c.kind)
  371. if op != nil and sfOverriden in op.flags:
  372. if op.ast.isGenericRoutine:
  373. # patch generic =trace:
  374. op = instantiateGeneric(c, op, t, t.typeInst)
  375. setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
  376. result = considerAsgnOrSink(c, t, body, x, y, op)
  377. if op != nil:
  378. setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
  379. of attachedDeepCopy:
  380. let op = getAttachedOp(c.g, t, attachedDeepCopy)
  381. if op != nil:
  382. #markUsed(c.g.config, c.info, op, c.g.usageSym)
  383. onUse(c.info, op)
  384. body.add newDeepCopyCall(c, op, x, y)
  385. result = true
  386. proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
  387. var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
  388. temp.typ = getSysType(c.g, body.info, tyInt)
  389. incl(temp.flags, sfFromGeneric)
  390. var v = newNodeI(nkVarSection, c.info)
  391. result = newSymNode(temp)
  392. v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
  393. body.add v
  394. proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
  395. var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
  396. temp.typ = value.typ
  397. incl(temp.flags, sfFromGeneric)
  398. var v = newNodeI(nkVarSection, c.info)
  399. result = newSymNode(temp)
  400. v.addVar(result, value)
  401. body.add v
  402. proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
  403. let incCall = genBuiltin(c, mInc, "inc", i)
  404. incCall.add lowerings.newIntLit(c.g, c.info, 1)
  405. body.add incCall
  406. proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
  407. # don't call genAddr(c, x) here:
  408. result = genBuiltin(c, mNewSeq, "newSeq", x)
  409. let lenCall = genBuiltin(c, mLengthSeq, "len", y)
  410. lenCall.typ = getSysType(c.g, x.info, tyInt)
  411. result.add lenCall
  412. proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
  413. let lenCall = genBuiltin(c, mLengthStr, "len", y)
  414. lenCall.typ = getSysType(c.g, x.info, tyInt)
  415. result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
  416. result.add lenCall
  417. proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
  418. let lenCall = genBuiltin(c, mLengthSeq, "len", y)
  419. lenCall.typ = getSysType(c.g, x.info, tyInt)
  420. var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
  421. op = instantiateGeneric(c, op, t, t)
  422. result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
  423. proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  424. let counterIdx = body.len
  425. let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
  426. let whileLoop = genWhileLoop(c, i, x)
  427. let elemType = t.lastSon
  428. let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
  429. fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
  430. if whileLoop[1].len > 0:
  431. addIncStmt(c, whileLoop[1], i)
  432. body.add whileLoop
  433. else:
  434. body.sons.setLen counterIdx
  435. proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  436. case c.kind
  437. of attachedAsgn, attachedDeepCopy:
  438. # we generate:
  439. # setLen(dest, y.len)
  440. # var i = 0
  441. # while i < y.len: dest[i] = y[i]; inc(i)
  442. # This is usually more efficient than a destroy/create pair.
  443. body.add setLenSeqCall(c, t, x, y)
  444. forallElements(c, t, body, x, y)
  445. of attachedSink:
  446. let moveCall = genBuiltin(c, mMove, "move", x)
  447. moveCall.add y
  448. doAssert t.destructor != nil
  449. moveCall.add destructorCall(c, t.destructor, x)
  450. body.add moveCall
  451. of attachedDestructor:
  452. # destroy all elements:
  453. forallElements(c, t, body, x, y)
  454. body.add genBuiltin(c, mDestroy, "destroy", x)
  455. of attachedTrace:
  456. if canFormAcycle(c.g, t.elemType):
  457. # follow all elements:
  458. forallElements(c, t, body, x, y)
  459. proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  460. createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
  461. # recursions are tricky, so we might need to forward the generated
  462. # operation here:
  463. var t = t
  464. if t.assignment == nil or t.destructor == nil:
  465. let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct})
  466. let canon = c.g.canonTypes.getOrDefault(h)
  467. if canon != nil: t = canon
  468. case c.kind
  469. of attachedAsgn, attachedDeepCopy:
  470. # XXX: replace these with assertions.
  471. if t.assignment == nil:
  472. return # protect from recursion
  473. body.add newHookCall(c, t.assignment, x, y)
  474. of attachedSink:
  475. # we always inline the move for better performance:
  476. let moveCall = genBuiltin(c, mMove, "move", x)
  477. moveCall.add y
  478. doAssert t.destructor != nil
  479. moveCall.add destructorCall(c, t.destructor, x)
  480. body.add moveCall
  481. # alternatively we could do this:
  482. when false:
  483. doAssert t.asink != nil
  484. body.add newHookCall(c, t.asink, x, y)
  485. of attachedDestructor:
  486. doAssert t.destructor != nil
  487. body.add destructorCall(c, t.destructor, x)
  488. of attachedTrace:
  489. if t.kind != tyString and canFormAcycle(c.g, t.elemType):
  490. let op = getAttachedOp(c.g, t, c.kind)
  491. if op == nil:
  492. return # protect from recursion
  493. body.add newHookCall(c, op, x, y)
  494. proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  495. case c.kind
  496. of attachedAsgn, attachedDeepCopy:
  497. body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
  498. of attachedSink:
  499. let moveCall = genBuiltin(c, mMove, "move", x)
  500. moveCall.add y
  501. doAssert t.destructor != nil
  502. moveCall.add destructorCall(c, t.destructor, x)
  503. body.add moveCall
  504. of attachedDestructor:
  505. body.add genBuiltin(c, mDestroy, "destroy", x)
  506. of attachedTrace:
  507. discard "strings are atomic and have no inner elements that are to trace"
  508. proc cyclicType*(g: ModuleGraph, t: PType): bool =
  509. case t.kind
  510. of tyRef: result = types.canFormAcycle(g, t.lastSon)
  511. of tyProc: result = t.callConv == ccClosure
  512. else: result = false
  513. proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  514. #[ bug #15753 is really subtle. Usually the classical write barrier for reference
  515. counting looks like this::
  516. incRef source # increment first; this takes care of self-assignments1
  517. decRef dest
  518. dest[] = source
  519. However, 'decRef dest' might trigger a cycle collection and then the collector
  520. traverses the graph. It is crucial that when it follows the pointers the assignment
  521. 'dest[] = source' already happened so that we don't do trial deletion on a wrong
  522. graph -- this causes premature freeing of objects! The correct barrier looks like
  523. this::
  524. let tmp = dest
  525. incRef source
  526. dest[] = source
  527. decRef tmp
  528. ]#
  529. var actions = newNodeI(nkStmtList, c.info)
  530. let elemType = t.lastSon
  531. createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
  532. let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
  533. let tmp =
  534. if isCyclic and c.kind in {attachedAsgn, attachedSink}:
  535. declareTempOf(c, body, x)
  536. else:
  537. x
  538. if isFinal(elemType):
  539. addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
  540. var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
  541. alignOf.typ = getSysType(c.g, c.info, tyInt)
  542. actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
  543. else:
  544. addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
  545. actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
  546. var cond: PNode
  547. if isCyclic:
  548. if isFinal(elemType):
  549. let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
  550. typInfo.typ = getSysType(c.g, c.info, tyPointer)
  551. cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
  552. else:
  553. cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
  554. else:
  555. cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
  556. cond.typ = getSysType(c.g, x.info, tyBool)
  557. case c.kind
  558. of attachedSink:
  559. if isCyclic:
  560. body.add newAsgnStmt(x, y)
  561. body.add genIf(c, cond, actions)
  562. else:
  563. body.add genIf(c, cond, actions)
  564. body.add newAsgnStmt(x, y)
  565. of attachedAsgn:
  566. if isCyclic:
  567. body.add genIf(c, y, callCodegenProc(c.g,
  568. "nimIncRefCyclic", c.info, y, getCycleParam(c)))
  569. body.add newAsgnStmt(x, y)
  570. body.add genIf(c, cond, actions)
  571. else:
  572. body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
  573. body.add genIf(c, cond, actions)
  574. body.add newAsgnStmt(x, y)
  575. of attachedDestructor:
  576. body.add genIf(c, cond, actions)
  577. of attachedDeepCopy: assert(false, "cannot happen")
  578. of attachedTrace:
  579. if isCyclic:
  580. if isFinal(elemType):
  581. let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
  582. typInfo.typ = getSysType(c.g, c.info, tyPointer)
  583. body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
  584. else:
  585. # If the ref is polymorphic we have to account for this
  586. body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
  587. #echo "can follow ", elemType, " static ", isFinal(elemType)
  588. proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  589. ## Closures are really like refs except they always use a virtual destructor
  590. ## and we need to do the refcounting only on the ref field which we call 'xenv':
  591. let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
  592. xenv.typ = getSysType(c.g, c.info, tyPointer)
  593. let isCyclic = c.g.config.selectedGC == gcOrc
  594. let tmp =
  595. if isCyclic and c.kind in {attachedAsgn, attachedSink}:
  596. declareTempOf(c, body, xenv)
  597. else:
  598. xenv
  599. var actions = newNodeI(nkStmtList, c.info)
  600. actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
  601. let decRefProc =
  602. if isCyclic: "nimDecRefIsLastCyclicDyn"
  603. else: "nimDecRefIsLast"
  604. let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
  605. cond.typ = getSysType(c.g, x.info, tyBool)
  606. case c.kind
  607. of attachedSink:
  608. if isCyclic:
  609. body.add newAsgnStmt(x, y)
  610. body.add genIf(c, cond, actions)
  611. else:
  612. body.add genIf(c, cond, actions)
  613. body.add newAsgnStmt(x, y)
  614. of attachedAsgn:
  615. let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
  616. yenv.typ = getSysType(c.g, c.info, tyPointer)
  617. if isCyclic:
  618. body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
  619. body.add newAsgnStmt(x, y)
  620. body.add genIf(c, cond, actions)
  621. else:
  622. body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
  623. body.add genIf(c, cond, actions)
  624. body.add newAsgnStmt(x, y)
  625. of attachedDestructor:
  626. body.add genIf(c, cond, actions)
  627. of attachedDeepCopy: assert(false, "cannot happen")
  628. of attachedTrace:
  629. body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
  630. proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  631. case c.kind
  632. of attachedSink:
  633. # we 'nil' y out afterwards so we *need* to take over its reference
  634. # count value:
  635. body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
  636. body.add newAsgnStmt(x, y)
  637. of attachedAsgn:
  638. body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
  639. body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
  640. body.add newAsgnStmt(x, y)
  641. of attachedDestructor:
  642. # it's better to prepend the destruction of weak refs in order to
  643. # prevent wrong "dangling refs exist" problems:
  644. var actions = newNodeI(nkStmtList, c.info)
  645. actions.add callCodegenProc(c.g, "nimDecWeakRef", c.info, x)
  646. let des = genIf(c, x, actions)
  647. if body.len == 0:
  648. body.add des
  649. else:
  650. body.sons.insert(des, 0)
  651. of attachedDeepCopy: assert(false, "cannot happen")
  652. of attachedTrace: discard
  653. proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  654. var actions = newNodeI(nkStmtList, c.info)
  655. let elemType = t.lastSon
  656. #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
  657. #var disposeCall = genBuiltin(c, mDispose, "dispose", x)
  658. if isFinal(elemType):
  659. addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
  660. var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
  661. alignOf.typ = getSysType(c.g, c.info, tyInt)
  662. actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
  663. else:
  664. addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
  665. actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
  666. case c.kind
  667. of attachedSink, attachedAsgn:
  668. body.add genIf(c, x, actions)
  669. body.add newAsgnStmt(x, y)
  670. of attachedDestructor:
  671. body.add genIf(c, x, actions)
  672. of attachedDeepCopy: assert(false, "cannot happen")
  673. of attachedTrace: discard
  674. proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  675. if c.kind == attachedDeepCopy:
  676. # a big problem is that we don't know the environment's type here, so we
  677. # have to go through some indirection; we delegate this to the codegen:
  678. let call = newNodeI(nkCall, c.info, 2)
  679. call.typ = t
  680. call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
  681. call[1] = y
  682. body.add newAsgnStmt(x, call)
  683. elif (optOwnedRefs in c.g.config.globalOptions and
  684. optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
  685. let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
  686. xx.typ = getSysType(c.g, c.info, tyPointer)
  687. case c.kind
  688. of attachedSink:
  689. # we 'nil' y out afterwards so we *need* to take over its reference
  690. # count value:
  691. body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
  692. body.add newAsgnStmt(x, y)
  693. of attachedAsgn:
  694. let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
  695. yy.typ = getSysType(c.g, c.info, tyPointer)
  696. body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
  697. body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
  698. body.add newAsgnStmt(x, y)
  699. of attachedDestructor:
  700. let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
  701. if body.len == 0:
  702. body.add des
  703. else:
  704. body.sons.insert(des, 0)
  705. of attachedDeepCopy: assert(false, "cannot happen")
  706. of attachedTrace: discard
  707. proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  708. let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
  709. xx.typ = getSysType(c.g, c.info, tyPointer)
  710. var actions = newNodeI(nkStmtList, c.info)
  711. #discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
  712. actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
  713. case c.kind
  714. of attachedSink, attachedAsgn:
  715. body.add genIf(c, xx, actions)
  716. body.add newAsgnStmt(x, y)
  717. of attachedDestructor:
  718. body.add genIf(c, xx, actions)
  719. of attachedDeepCopy: assert(false, "cannot happen")
  720. of attachedTrace: discard
  721. proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  722. case t.kind
  723. of tyNone, tyEmpty, tyVoid: discard
  724. of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring,
  725. tyPtr, tyUncheckedArray, tyVar, tyLent:
  726. defaultOp(c, t, body, x, y)
  727. of tyRef:
  728. if c.g.config.selectedGC in {gcArc, gcOrc}:
  729. atomicRefOp(c, t, body, x, y)
  730. elif (optOwnedRefs in c.g.config.globalOptions and
  731. optRefCheck in c.g.config.options):
  732. weakrefOp(c, t, body, x, y)
  733. else:
  734. defaultOp(c, t, body, x, y)
  735. of tyProc:
  736. if t.callConv == ccClosure:
  737. if c.g.config.selectedGC in {gcArc, gcOrc}:
  738. atomicClosureOp(c, t, body, x, y)
  739. else:
  740. closureOp(c, t, body, x, y)
  741. else:
  742. defaultOp(c, t, body, x, y)
  743. of tyOwned:
  744. let base = t.skipTypes(abstractInstOwned)
  745. if optOwnedRefs in c.g.config.globalOptions:
  746. case base.kind
  747. of tyRef:
  748. ownedRefOp(c, base, body, x, y)
  749. return
  750. of tyProc:
  751. if base.callConv == ccClosure:
  752. ownedClosureOp(c, base, body, x, y)
  753. return
  754. else: discard
  755. defaultOp(c, base, body, x, y)
  756. of tyArray:
  757. if tfHasAsgn in t.flags or useNoGc(c, t):
  758. forallElements(c, t, body, x, y)
  759. else:
  760. defaultOp(c, t, body, x, y)
  761. of tySequence:
  762. if useNoGc(c, t):
  763. useSeqOrStrOp(c, t, body, x, y)
  764. elif optSeqDestructors in c.g.config.globalOptions:
  765. # note that tfHasAsgn is propagated so we need the check on
  766. # 'selectedGC' here to determine if we have the new runtime.
  767. discard considerUserDefinedOp(c, t, body, x, y)
  768. elif tfHasAsgn in t.flags:
  769. if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
  770. body.add newSeqCall(c, x, y)
  771. forallElements(c, t, body, x, y)
  772. else:
  773. defaultOp(c, t, body, x, y)
  774. of tyString:
  775. if useNoGc(c, t):
  776. useSeqOrStrOp(c, t, body, x, y)
  777. elif tfHasAsgn in t.flags:
  778. discard considerUserDefinedOp(c, t, body, x, y)
  779. else:
  780. defaultOp(c, t, body, x, y)
  781. of tyObject:
  782. if not considerUserDefinedOp(c, t, body, x, y):
  783. if c.kind in {attachedAsgn, attachedSink} and t.sym != nil and sfImportc in t.sym.flags:
  784. body.add newAsgnStmt(x, y)
  785. else:
  786. fillBodyObjT(c, t, body, x, y)
  787. of tyDistinct:
  788. if not considerUserDefinedOp(c, t, body, x, y):
  789. fillBody(c, t[0], body, x, y)
  790. of tyTuple:
  791. fillBodyTup(c, t, body, x, y)
  792. of tyVarargs, tyOpenArray:
  793. if c.kind == attachedDestructor and (tfHasAsgn in t.flags or useNoGc(c, t)):
  794. forallElements(c, t, body, x, y)
  795. else:
  796. discard "cannot copy openArray"
  797. of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
  798. tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
  799. tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
  800. tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
  801. #internalError(c.g.config, c.info, "assignment requested for type: " & typeToString(t))
  802. discard
  803. of tyOrdinal, tyRange, tyInferred,
  804. tyGenericInst, tyAlias, tySink:
  805. fillBody(c, lastSon(t), body, x, y)
  806. of tyConcept, tyIterable: doAssert false
  807. proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
  808. kind: TTypeAttachedOp; info: TLineInfo;
  809. idgen: IdGenerator): PSym =
  810. assert typ.kind == tyDistinct
  811. let baseType = typ[0]
  812. if getAttachedOp(g, baseType, kind) == nil:
  813. discard produceSym(g, c, baseType, kind, info, idgen)
  814. result = getAttachedOp(g, baseType, kind)
  815. setAttachedOp(g, idgen.module, typ, kind, result)
  816. proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
  817. info: TLineInfo; idgen: IdGenerator): PSym =
  818. let procname = getIdent(g.cache, AttachedOpToStr[kind])
  819. result = newSym(skProc, procname, nextSymId(idgen), owner, info)
  820. let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
  821. let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
  822. nextSymId(idgen), result, info)
  823. dest.typ = makeVarType(typ.owner, typ, idgen)
  824. if kind == attachedTrace:
  825. src.typ = getSysType(g, info, tyPointer)
  826. else:
  827. src.typ = typ
  828. result.typ = newProcType(info, nextTypeId(idgen), owner)
  829. result.typ.addParam dest
  830. if kind != attachedDestructor:
  831. result.typ.addParam src
  832. if kind == attachedAsgn and g.config.selectedGC == gcOrc and
  833. cyclicType(g, typ.skipTypes(abstractInst)):
  834. let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
  835. nextSymId(idgen), result, info)
  836. cycleParam.typ = getSysType(g, info, tyBool)
  837. result.typ.addParam cycleParam
  838. var n = newNodeI(nkProcDef, info, bodyPos+1)
  839. for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
  840. n[namePos] = newSymNode(result)
  841. n[paramsPos] = result.typ.n
  842. n[bodyPos] = newNodeI(nkStmtList, info)
  843. result.ast = n
  844. incl result.flags, sfFromGeneric
  845. incl result.flags, sfGeneratedOp
  846. proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  847. let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
  848. let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
  849. xx.typ = getSysType(c.g, c.info, tyPointer)
  850. yy.typ = xx.typ
  851. body.add newAsgnStmt(xx, yy)
  852. proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
  853. info: TLineInfo; idgen: IdGenerator): PSym =
  854. if typ.kind == tyDistinct:
  855. return produceSymDistinctType(g, c, typ, kind, info, idgen)
  856. result = getAttachedOp(g, typ, kind)
  857. if result == nil:
  858. result = symPrototype(g, typ, typ.owner, kind, info, idgen)
  859. var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
  860. fn: result)
  861. let dest = result.typ.n[1].sym
  862. let d = newDeref(newSymNode(dest))
  863. let src = if kind == attachedDestructor: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
  864. else: newSymNode(result.typ.n[2].sym)
  865. # register this operation already:
  866. setAttachedOpPartial(g, idgen.module, typ, kind, result)
  867. if kind == attachedSink and destructorOverriden(g, typ):
  868. ## compiler can use a combination of `=destroy` and memCopy for sink op
  869. dest.flags.incl sfCursor
  870. result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
  871. result.ast[bodyPos].add newAsgnStmt(d, src)
  872. else:
  873. var tk: TTypeKind
  874. if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
  875. tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
  876. else:
  877. tk = tyNone # no special casing for strings and seqs
  878. case tk
  879. of tySequence:
  880. fillSeqOp(a, typ, result.ast[bodyPos], d, src)
  881. of tyString:
  882. fillStrOp(a, typ, result.ast[bodyPos], d, src)
  883. else:
  884. fillBody(a, typ, result.ast[bodyPos], d, src)
  885. if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy} and not lacksMTypeField(typ):
  886. # bug #19205: Do not forget to also copy the hidden type field:
  887. genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
  888. if not a.canRaise: incl result.flags, sfNeverRaises
  889. completePartialOp(g, idgen.module, typ, kind, result)
  890. proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
  891. info: TLineInfo; idgen: IdGenerator): PSym =
  892. assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
  893. result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
  894. var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
  895. fn: result)
  896. a.asgnForType = typ
  897. a.filterDiscriminator = field
  898. a.addMemReset = true
  899. let discrimantDest = result.typ.n[1].sym
  900. let dst = newSym(skVar, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
  901. dst.typ = makePtrType(typ.owner, typ, idgen)
  902. let dstSym = newSymNode(dst)
  903. let d = newDeref(dstSym)
  904. let v = newNodeI(nkVarSection, info)
  905. v.addVar(dstSym, genContainerOf(a, typ, field, discrimantDest))
  906. result.ast[bodyPos].add v
  907. let placeHolder = newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
  908. fillBody(a, typ, result.ast[bodyPos], d, placeHolder)
  909. if not a.canRaise: incl result.flags, sfNeverRaises
  910. template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
  911. discard "now a nop"
  912. proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: IdGenerator) =
  913. if n.kind in nkCallKinds:
  914. if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
  915. let t = n[1].typ.skipTypes(abstractVar)
  916. if getAttachedOp(g, t, attachedDestructor) == nil:
  917. discard produceSym(g, c, t, attachedDestructor, info, idgen)
  918. let op = getAttachedOp(g, t, attachedDestructor)
  919. if op != nil:
  920. if op.ast.isGenericRoutine:
  921. internalError(g.config, info, "resolved destructor is generic")
  922. if op.magic == mDestroy:
  923. internalError(g.config, info, "patching mDestroy with mDestroy?")
  924. n[0] = newSymNode(op)
  925. for x in n: patchBody(g, c, x, info, idgen)
  926. proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator;
  927. info: TLineInfo) =
  928. let op = getAttachedOp(g, t, kind)
  929. if op != nil and op.ast != nil and op.ast.isGenericRoutine:
  930. if t.typeInst != nil:
  931. var a: TLiftCtx
  932. a.info = info
  933. a.g = g
  934. a.kind = kind
  935. a.c = c
  936. a.idgen = idgen
  937. let opInst = instantiateGeneric(a, op, t, t.typeInst)
  938. if opInst.ast != nil:
  939. patchBody(g, c, opInst.ast, info, a.idgen)
  940. setAttachedOp(g, idgen.module, t, kind, opInst)
  941. else:
  942. localError(g.config, info, "unresolved generic parameter")
  943. proc isTrival(s: PSym): bool {.inline.} =
  944. s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
  945. proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
  946. idgen: IdGenerator) =
  947. ## In the semantic pass this is called in strategic places
  948. ## to ensure we lift assignment, destructors and moves properly.
  949. ## The later 'injectdestructors' pass depends on it.
  950. if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
  951. incl orig.flags, tfCheckedForDestructor
  952. let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
  953. if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
  954. let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct})
  955. var canon = g.canonTypes.getOrDefault(h)
  956. if canon == nil:
  957. g.canonTypes[h] = skipped
  958. canon = skipped
  959. # multiple cases are to distinguish here:
  960. # 1. we don't know yet if 'typ' has a nontrival destructor.
  961. # 2. we have a nop destructor. --> mDestroy
  962. # 3. we have a lifted destructor.
  963. # 4. We have a custom destructor.
  964. # 5. We have a (custom) generic destructor.
  965. # we do not generate '=trace' procs if we
  966. # have the cycle detection disabled, saves code size.
  967. let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace
  968. else: attachedSink
  969. # bug #15122: We need to produce all prototypes before entering the
  970. # mind boggling recursion. Hacks like these imply we should rewrite
  971. # this module.
  972. var generics: array[attachedDestructor..attachedTrace, bool]
  973. for k in attachedDestructor..lastAttached:
  974. generics[k] = getAttachedOp(g, canon, k) != nil
  975. if not generics[k]:
  976. setAttachedOp(g, idgen.module, canon, k,
  977. symPrototype(g, canon, canon.owner, k, info, idgen))
  978. # we generate the destructor first so that other operators can depend on it:
  979. for k in attachedDestructor..lastAttached:
  980. if not generics[k]:
  981. discard produceSym(g, c, canon, k, info, idgen)
  982. else:
  983. inst(g, c, canon, k, idgen, info)
  984. if canon != orig:
  985. setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
  986. if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
  987. #or not isTrival(orig.assignment) or
  988. # not isTrival(orig.sink):
  989. orig.flags.incl tfHasAsgn
  990. # ^ XXX Breaks IC!