liftdestructors.nim 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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. # Todo:
  12. # - use openArray instead of array to avoid over-specializations
  13. import modulegraphs, lineinfos, idents, ast, renderer, semdata,
  14. sighashes, lowerings, options, types, msgs, magicsys, tables
  15. type
  16. TLiftCtx = object
  17. g: ModuleGraph
  18. info: TLineInfo # for construction
  19. kind: TTypeAttachedOp
  20. fn: PSym
  21. asgnForType: PType
  22. recurse: bool
  23. c: PContext # c can be nil, then we are called from lambdalifting!
  24. proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
  25. proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
  26. info: TLineInfo): PSym
  27. proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo)
  28. proc at(a, i: PNode, elemType: PType): PNode =
  29. result = newNodeI(nkBracketExpr, a.info, 2)
  30. result.sons[0] = a
  31. result.sons[1] = i
  32. result.typ = elemType
  33. proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  34. for i in 0 ..< t.len:
  35. let lit = lowerings.newIntLit(c.g, x.info, i)
  36. fillBody(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
  37. proc dotField(x: PNode, f: PSym): PNode =
  38. result = newNodeI(nkDotExpr, x.info, 2)
  39. result.sons[0] = x
  40. result.sons[1] = newSymNode(f, x.info)
  41. result.typ = f.typ
  42. proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
  43. case n.kind
  44. of nkSym:
  45. let f = n.sym
  46. fillBody(c, f.typ, body, x.dotField(f), y.dotField(f))
  47. of nkNilLit: discard
  48. of nkRecCase:
  49. if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
  50. ## the value needs to be destroyed before we assign the selector
  51. ## or the value is lost
  52. let prevKind = c.kind
  53. c.kind = attachedDestructor
  54. fillBodyObj(c, n, body, x, y)
  55. c.kind = prevKind
  56. # copy the selector:
  57. fillBodyObj(c, n[0], body, x, y)
  58. # we need to generate a case statement:
  59. var caseStmt = newNodeI(nkCaseStmt, c.info)
  60. # XXX generate 'if' that checks same branches
  61. # generate selector:
  62. var access = dotField(x, n[0].sym)
  63. caseStmt.add(access)
  64. var emptyBranches = 0
  65. # copy the branches over, but replace the fields with the for loop body:
  66. for i in 1 ..< n.len:
  67. var branch = copyTree(n[i])
  68. let L = branch.len
  69. branch.sons[L-1] = newNodeI(nkStmtList, c.info)
  70. fillBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
  71. if branch.sons[L-1].len == 0: inc emptyBranches
  72. caseStmt.add(branch)
  73. if emptyBranches != n.len-1:
  74. body.add(caseStmt)
  75. of nkRecList:
  76. for t in items(n): fillBodyObj(c, t, body, x, y)
  77. else:
  78. illFormedAstLocal(n, c.g.config)
  79. proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
  80. if t.len > 0 and t.sons[0] != nil:
  81. fillBodyObjT(c, skipTypes(t.sons[0], abstractPtrs), body, x, y)
  82. fillBodyObj(c, t.n, body, x, y)
  83. proc genAddr(g: ModuleGraph; x: PNode): PNode =
  84. if x.kind == nkHiddenDeref:
  85. checkSonsLen(x, 1, g.config)
  86. result = x.sons[0]
  87. else:
  88. result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
  89. addSon(result, x)
  90. proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
  91. #if sfError in op.flags:
  92. # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
  93. result = newNodeI(nkCall, x.info)
  94. result.add newSymNode(op)
  95. result.add genAddr(g, x)
  96. result.add y
  97. proc newAsgnStmt(le, ri: PNode): PNode =
  98. result = newNodeI(nkAsgn, le.info, 2)
  99. result.sons[0] = le
  100. result.sons[1] = ri
  101. proc newOpCall(op: PSym; x: PNode): PNode =
  102. result = newNodeIT(nkCall, x.info, op.typ.sons[0])
  103. result.add(newSymNode(op))
  104. result.add x
  105. proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
  106. result = newNodeIT(nkCall, x.info, op.typ.sons[0])
  107. result.add(newSymNode(op))
  108. result.add genAddr(g, x)
  109. proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
  110. result = newAsgnStmt(x, newOpCall(op, y))
  111. proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
  112. result = c.g.config.selectedGC == gcDestructors and
  113. ({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
  114. proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
  115. if c.c != nil and typeInst != nil:
  116. result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
  117. else:
  118. localError(c.g.config, c.info,
  119. "cannot generate destructor for generic type: " & typeToString(t))
  120. result = nil
  121. proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
  122. field: var PSym): bool =
  123. if c.g.config.selectedGC == gcDestructors:
  124. let op = field
  125. if field != nil and sfOverriden in field.flags:
  126. if sfError in op.flags:
  127. incl c.fn.flags, sfError
  128. #else:
  129. # markUsed(c.g.config, c.info, op, c.g.usageSym)
  130. onUse(c.info, op)
  131. body.add newAsgnCall(c.g, op, x, y)
  132. result = true
  133. elif tfHasAsgn in t.flags:
  134. var op: PSym
  135. if sameType(t, c.asgnForType):
  136. # generate recursive call:
  137. if c.recurse:
  138. op = c.fn
  139. else:
  140. c.recurse = true
  141. return false
  142. else:
  143. op = field
  144. if op == nil:
  145. op = produceSym(c.g, c.c, t, c.kind, c.info)
  146. if sfError in op.flags:
  147. incl c.fn.flags, sfError
  148. #else:
  149. # markUsed(c.g.config, c.info, op, c.g.usageSym)
  150. onUse(c.info, op)
  151. # We also now do generic instantiations in the destructor lifting pass:
  152. if op.ast[genericParamsPos].kind != nkEmpty:
  153. op = instantiateGeneric(c, op, t, t.typeInst)
  154. field = op
  155. #echo "trying to use ", op.ast
  156. #echo "for ", op.name.s, " "
  157. #debug(t)
  158. #return false
  159. assert op.ast[genericParamsPos].kind == nkEmpty
  160. body.add newAsgnCall(c.g, op, x, y)
  161. result = true
  162. proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
  163. let t = orig.skipTypes(abstractInst)
  164. var op = t.destructor
  165. if op != nil and sfOverriden in op.flags:
  166. if op.ast[genericParamsPos].kind != nkEmpty:
  167. # patch generic destructor:
  168. op = instantiateGeneric(c, op, t, t.typeInst)
  169. t.attachedOps[attachedDestructor] = op
  170. if op == nil and useNoGc(c, t):
  171. op = produceSym(c.g, c.c, t, attachedDestructor, c.info)
  172. doAssert op != nil
  173. doAssert op == t.destructor
  174. if op != nil:
  175. #markUsed(c.g.config, c.info, op, c.g.usageSym)
  176. onUse(c.info, op)
  177. body.add destructorCall(c.g, op, x)
  178. elif useNoGc(c, t):
  179. internalError(c.g.config, c.info,
  180. "type-bound operator could not be resolved")
  181. proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
  182. case c.kind
  183. of attachedDestructor:
  184. var op = t.destructor
  185. if op != nil and sfOverriden in op.flags:
  186. if op.ast[genericParamsPos].kind != nkEmpty:
  187. # patch generic destructor:
  188. op = instantiateGeneric(c, op, t, t.typeInst)
  189. t.attachedOps[attachedDestructor] = op
  190. #markUsed(c.g.config, c.info, op, c.g.usageSym)
  191. onUse(c.info, op)
  192. body.add destructorCall(c.g, op, x)
  193. result = true
  194. #result = addDestructorCall(c, t, body, x)
  195. of attachedAsgn:
  196. result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
  197. of attachedSink:
  198. result = considerAsgnOrSink(c, t, body, x, y, t.asink)
  199. of attachedDeepCopy:
  200. let op = t.attachedOps[attachedDeepCopy]
  201. if op != nil:
  202. #markUsed(c.g.config, c.info, op, c.g.usageSym)
  203. onUse(c.info, op)
  204. body.add newDeepCopyCall(op, x, y)
  205. result = true
  206. proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  207. if c.kind != attachedDestructor:
  208. body.add newAsgnStmt(x, y)
  209. proc addVar(father, v, value: PNode) =
  210. var vpart = newNodeI(nkIdentDefs, v.info, 3)
  211. vpart.sons[0] = v
  212. vpart.sons[1] = newNodeI(nkEmpty, v.info)
  213. vpart.sons[2] = value
  214. addSon(father, vpart)
  215. proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
  216. var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.fn, c.info)
  217. temp.typ = getSysType(c.g, body.info, tyInt)
  218. incl(temp.flags, sfFromGeneric)
  219. var v = newNodeI(nkVarSection, c.info)
  220. result = newSymNode(temp)
  221. v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
  222. body.add v
  223. proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
  224. result = newNodeI(nkCall, i.info)
  225. result.add createMagic(g, name, magic).newSymNode
  226. result.add i
  227. proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
  228. result = newNodeI(nkWhileStmt, c.info, 2)
  229. let cmp = genBuiltin(c.g, mLtI, "<", i)
  230. cmp.add genLen(c.g, dest)
  231. cmp.typ = getSysType(c.g, c.info, tyBool)
  232. result.sons[0] = cmp
  233. result.sons[1] = newNodeI(nkStmtList, c.info)
  234. proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
  235. result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
  236. proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
  237. let incCall = genBuiltin(c.g, mInc, "inc", i)
  238. incCall.add lowerings.newIntLit(c.g, c.info, 1)
  239. body.add incCall
  240. proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
  241. # don't call genAddr(c, x) here:
  242. result = genBuiltin(g, mNewSeq, "newSeq", x)
  243. let lenCall = genBuiltin(g, mLengthSeq, "len", y)
  244. lenCall.typ = getSysType(g, x.info, tyInt)
  245. result.add lenCall
  246. proc setLenStrCall(g: ModuleGraph; x, y: PNode): PNode =
  247. let lenCall = genBuiltin(g, mLengthStr, "len", y)
  248. lenCall.typ = getSysType(g, x.info, tyInt)
  249. result = genBuiltin(g, mSetLengthStr, "setLen", x) # genAddr(g, x))
  250. result.add lenCall
  251. proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
  252. let lenCall = genBuiltin(c.g, mLengthSeq, "len", y)
  253. lenCall.typ = getSysType(c.g, x.info, tyInt)
  254. var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
  255. op = instantiateGeneric(c, op, t, t)
  256. result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
  257. proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  258. let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
  259. let whileLoop = genWhileLoop(c, i, x)
  260. let elemType = t.lastSon
  261. fillBody(c, elemType, whileLoop.sons[1], x.at(i, elemType),
  262. y.at(i, elemType))
  263. addIncStmt(c, whileLoop.sons[1], i)
  264. body.add whileLoop
  265. proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  266. case c.kind
  267. of attachedAsgn, attachedDeepCopy:
  268. # we generate:
  269. # setLen(dest, y.len)
  270. # var i = 0
  271. # while i < y.len: dest[i] = y[i]; inc(i)
  272. # This is usually more efficient than a destroy/create pair.
  273. body.add setLenSeqCall(c, t, x, y)
  274. forallElements(c, t, body, x, y)
  275. of attachedSink:
  276. let moveCall = genBuiltin(c.g, mMove, "move", x)
  277. moveCall.add y
  278. doAssert t.destructor != nil
  279. moveCall.add destructorCall(c.g, t.destructor, x)
  280. body.add moveCall
  281. of attachedDestructor:
  282. # destroy all elements:
  283. forallElements(c, t, body, x, y)
  284. body.add genBuiltin(c.g, mDestroy, "destroy", x)
  285. proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  286. createTypeBoundOps(c.g, c.c, t, body.info)
  287. case c.kind
  288. of attachedAsgn, attachedDeepCopy:
  289. doAssert t.assignment != nil
  290. body.add newAsgnCall(c.g, t.assignment, x, y)
  291. of attachedSink:
  292. # we always inline the move for better performance:
  293. let moveCall = genBuiltin(c.g, mMove, "move", x)
  294. moveCall.add y
  295. doAssert t.destructor != nil
  296. moveCall.add destructorCall(c.g, t.destructor, x)
  297. body.add moveCall
  298. # alternatively we could do this:
  299. when false:
  300. doAssert t.asink != nil
  301. body.add newAsgnCall(c.g, t.asink, x, y)
  302. of attachedDestructor:
  303. doAssert t.destructor != nil
  304. body.add destructorCall(c.g, t.destructor, x)
  305. proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  306. case c.kind
  307. of attachedAsgn, attachedDeepCopy:
  308. body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c.g, x), y)
  309. of attachedSink:
  310. let moveCall = genBuiltin(c.g, mMove, "move", x)
  311. moveCall.add y
  312. doAssert t.destructor != nil
  313. moveCall.add destructorCall(c.g, t.destructor, x)
  314. body.add moveCall
  315. of attachedDestructor:
  316. body.add genBuiltin(c.g, mDestroy, "destroy", x)
  317. proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  318. case c.kind
  319. of attachedSink:
  320. # we 'nil' y out afterwards so we *need* to take over its reference
  321. # count value:
  322. body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
  323. body.add newAsgnStmt(x, y)
  324. of attachedAsgn:
  325. body.add genIf(c, y, callCodegenProc(c.g, "nimIncWeakRef", c.info, y))
  326. body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
  327. body.add newAsgnStmt(x, y)
  328. of attachedDestructor:
  329. # it's better to prepend the destruction of weak refs in order to
  330. # prevent wrong "dangling refs exist" problems:
  331. let des = genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
  332. if body.len == 0:
  333. body.add des
  334. else:
  335. body.sons.insert(des, 0)
  336. of attachedDeepCopy: assert(false, "cannot happen")
  337. proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  338. var actions = newNodeI(nkStmtList, c.info)
  339. let elemType = t.lastSon
  340. #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
  341. #var disposeCall = genBuiltin(c.g, mDispose, "dispose", x)
  342. if isFinal(elemType):
  343. addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
  344. actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
  345. else:
  346. addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
  347. actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
  348. case c.kind
  349. of attachedSink, attachedAsgn:
  350. body.add genIf(c, x, actions)
  351. body.add newAsgnStmt(x, y)
  352. of attachedDestructor:
  353. body.add genIf(c, x, actions)
  354. of attachedDeepCopy: assert(false, "cannot happen")
  355. proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  356. if c.kind == attachedDeepCopy:
  357. # a big problem is that we don't know the environment's type here, so we
  358. # have to go through some indirection; we delegate this to the codegen:
  359. let call = newNodeI(nkCall, c.info, 2)
  360. call.typ = t
  361. call.sons[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
  362. call.sons[1] = y
  363. body.add newAsgnStmt(x, call)
  364. elif optOwnedRefs in c.g.config.globalOptions and
  365. optRefCheck in c.g.config.options:
  366. let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
  367. xx.typ = getSysType(c.g, c.info, tyPointer)
  368. case c.kind
  369. of attachedSink:
  370. # we 'nil' y out afterwards so we *need* to take over its reference
  371. # count value:
  372. body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
  373. body.add newAsgnStmt(x, y)
  374. of attachedAsgn:
  375. let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
  376. yy.typ = getSysType(c.g, c.info, tyPointer)
  377. body.add genIf(c, yy, callCodegenProc(c.g, "nimIncWeakRef", c.info, yy))
  378. body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
  379. body.add newAsgnStmt(x, y)
  380. of attachedDestructor:
  381. let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
  382. if body.len == 0:
  383. body.add des
  384. else:
  385. body.sons.insert(des, 0)
  386. of attachedDeepCopy: assert(false, "cannot happen")
  387. proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  388. let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
  389. xx.typ = getSysType(c.g, c.info, tyPointer)
  390. var actions = newNodeI(nkStmtList, c.info)
  391. let elemType = t.lastSon
  392. #discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
  393. actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
  394. case c.kind
  395. of attachedSink, attachedAsgn:
  396. body.add genIf(c, xx, actions)
  397. body.add newAsgnStmt(x, y)
  398. of attachedDestructor:
  399. body.add genIf(c, xx, actions)
  400. of attachedDeepCopy: assert(false, "cannot happen")
  401. proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
  402. case t.kind
  403. of tyNone, tyEmpty, tyVoid: discard
  404. of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
  405. tyPtr, tyOpt, tyUncheckedArray:
  406. defaultOp(c, t, body, x, y)
  407. of tyRef:
  408. if optOwnedRefs in c.g.config.globalOptions and
  409. optRefCheck in c.g.config.options:
  410. weakrefOp(c, t, body, x, y)
  411. else:
  412. defaultOp(c, t, body, x, y)
  413. of tyProc:
  414. if t.callConv == ccClosure:
  415. closureOp(c, t, body, x, y)
  416. else:
  417. defaultOp(c, t, body, x, y)
  418. of tyOwned:
  419. let base = t.skipTypes(abstractInstOwned)
  420. if optOwnedRefs in c.g.config.globalOptions:
  421. case base.kind
  422. of tyRef:
  423. ownedRefOp(c, base, body, x, y)
  424. return
  425. of tyProc:
  426. if base.callConv == ccClosure:
  427. ownedClosureOp(c, base, body, x, y)
  428. return
  429. else: discard
  430. defaultOp(c, base, body, x, y)
  431. of tyArray:
  432. if tfHasAsgn in t.flags or useNoGc(c, t):
  433. forallElements(c, t, body, x, y)
  434. else:
  435. defaultOp(c, t, body, x, y)
  436. of tySequence:
  437. if useNoGc(c, t):
  438. useSeqOrStrOp(c, t, body, x, y)
  439. elif c.g.config.selectedGC == gcDestructors:
  440. # note that tfHasAsgn is propagated so we need the check on
  441. # 'selectedGC' here to determine if we have the new runtime.
  442. discard considerUserDefinedOp(c, t, body, x, y)
  443. elif tfHasAsgn in t.flags:
  444. if c.kind != attachedDestructor:
  445. body.add newSeqCall(c.g, x, y)
  446. forallElements(c, t, body, x, y)
  447. else:
  448. defaultOp(c, t, body, x, y)
  449. of tyString:
  450. if useNoGc(c, t):
  451. useSeqOrStrOp(c, t, body, x, y)
  452. elif tfHasAsgn in t.flags:
  453. discard considerUserDefinedOp(c, t, body, x, y)
  454. else:
  455. defaultOp(c, t, body, x, y)
  456. of tyObject:
  457. if not considerUserDefinedOp(c, t, body, x, y):
  458. fillBodyObjT(c, t, body, x, y)
  459. of tyDistinct:
  460. if not considerUserDefinedOp(c, t, body, x, y):
  461. fillBody(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
  462. of tyTuple:
  463. fillBodyTup(c, t, body, x, y)
  464. of tyVarargs, tyOpenArray:
  465. if c.kind == attachedDestructor:
  466. forallElements(c, t, body, x, y)
  467. else:
  468. discard "cannot copy openArray"
  469. of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
  470. tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
  471. tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
  472. tyTypeDesc, tyGenericInvocation, tyForward:
  473. #internalError(c.g.config, c.info, "assignment requested for type: " & typeToString(t))
  474. discard
  475. of tyVar, tyLent:
  476. if c.kind != attachedDestructor:
  477. fillBody(c, lastSon(t), body, x, y)
  478. of tyOrdinal, tyRange, tyInferred,
  479. tyGenericInst, tyStatic, tyAlias, tySink:
  480. fillBody(c, lastSon(t), body, x, y)
  481. proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
  482. assert typ.kind == tyDistinct
  483. let baseType = typ[0]
  484. if baseType.attachedOps[kind] == nil:
  485. discard produceSym(g, c, baseType, kind, info)
  486. typ.attachedOps[kind] = baseType.attachedOps[kind]
  487. result = typ.attachedOps[kind]
  488. proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
  489. info: TLineInfo): PSym =
  490. if typ.kind == tyDistinct:
  491. return produceSymDistinctType(g, c, typ, kind, info)
  492. var a: TLiftCtx
  493. a.info = info
  494. a.g = g
  495. a.kind = kind
  496. a.c = c
  497. let body = newNodeI(nkStmtList, info)
  498. let procname = getIdent(g.cache, AttachedOpToStr[kind])
  499. result = newSym(skProc, procname, typ.owner, info)
  500. a.fn = result
  501. a.asgnForType = typ
  502. let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
  503. let src = newSym(skParam, getIdent(g.cache, "src"), result, info)
  504. dest.typ = makeVarType(typ.owner, typ)
  505. src.typ = typ
  506. result.typ = newProcType(info, typ.owner)
  507. result.typ.addParam dest
  508. if kind != attachedDestructor:
  509. result.typ.addParam src
  510. # register this operation already:
  511. typ.attachedOps[kind] = result
  512. var tk: TTypeKind
  513. if g.config.selectedGC == gcDestructors:
  514. tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
  515. else:
  516. tk = tyNone # no special casing for strings and seqs
  517. case tk
  518. of tySequence:
  519. fillSeqOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
  520. of tyString:
  521. fillStrOp(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
  522. else:
  523. fillBody(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
  524. var n = newNodeI(nkProcDef, info, bodyPos+1)
  525. for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
  526. n.sons[namePos] = newSymNode(result)
  527. n.sons[paramsPos] = result.typ.n
  528. n.sons[bodyPos] = body
  529. result.ast = n
  530. incl result.flags, sfFromGeneric
  531. incl result.flags, sfGeneratedOp
  532. template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
  533. discard "now a nop"
  534. proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo) =
  535. if n.kind in nkCallKinds:
  536. if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
  537. let t = n[1].typ.skipTypes(abstractVar)
  538. if t.destructor == nil:
  539. discard produceSym(g, c, t, attachedDestructor, info)
  540. if t.destructor != nil:
  541. if t.destructor.ast[genericParamsPos].kind != nkEmpty:
  542. internalError(g.config, info, "resolved destructor is generic")
  543. if t.destructor.magic == mDestroy:
  544. internalError(g.config, info, "patching mDestroy with mDestroy?")
  545. n.sons[0] = newSymNode(t.destructor)
  546. for x in n: patchBody(g, c, x, info)
  547. template inst(field, t) =
  548. if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
  549. if t.typeInst != nil:
  550. var a: TLiftCtx
  551. a.info = info
  552. a.g = g
  553. a.kind = k
  554. a.c = c
  555. field = instantiateGeneric(a, field, t, t.typeInst)
  556. if field.ast != nil:
  557. patchBody(g, c, field.ast, info)
  558. else:
  559. localError(g.config, info, "unresolved generic parameter")
  560. proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0
  561. proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo) =
  562. ## In the semantic pass this is called in strategic places
  563. ## to ensure we lift assignment, destructors and moves properly.
  564. ## The later 'injectdestructors' pass depends on it.
  565. if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.skipTypes({tyAlias}).flags != {}: return
  566. incl orig.flags, tfCheckedForDestructor
  567. let h = sighashes.hashType(orig, {CoType, CoConsiderOwned, CoDistinct})
  568. var canon = g.canonTypes.getOrDefault(h)
  569. var overwrite = false
  570. if canon == nil:
  571. let typ = orig.skipTypes({tyGenericInst, tyAlias, tySink})
  572. g.canonTypes[h] = typ
  573. canon = typ
  574. if canon != orig:
  575. overwrite = true
  576. # multiple cases are to distinguish here:
  577. # 1. we don't know yet if 'typ' has a nontrival destructor.
  578. # 2. we have a nop destructor. --> mDestroy
  579. # 3. we have a lifted destructor.
  580. # 4. We have a custom destructor.
  581. # 5. We have a (custom) generic destructor.
  582. # we generate the destructor first so that other operators can depend on it:
  583. for k in attachedDestructor..attachedSink:
  584. if canon.attachedOps[k] == nil:
  585. discard produceSym(g, c, canon, k, info)
  586. else:
  587. inst(canon.attachedOps[k], canon)
  588. if overwrite:
  589. for k in attachedDestructor..attachedSink:
  590. orig.attachedOps[k] = canon.attachedOps[k]
  591. if not isTrival(orig.destructor):
  592. #or not isTrival(orig.assignment) or
  593. # not isTrival(orig.sink):
  594. orig.flags.incl tfHasAsgn