cgmeth.nim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements code generation for methods.
  10. import
  11. options, ast, msgs, idents, renderer, types, magicsys,
  12. sempass2, modulegraphs, lineinfos, astalgo
  13. import std/intsets
  14. when defined(nimPreviewSlimSystem):
  15. import std/assertions
  16. import std/[tables]
  17. proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
  18. var dest = skipTypes(d, abstractPtrs)
  19. var source = skipTypes(n.typ, abstractPtrs)
  20. if (source.kind == tyObject) and (dest.kind == tyObject):
  21. var diff = inheritanceDiff(dest, source)
  22. if diff == high(int):
  23. # no subtype relation, nothing to do
  24. result = n
  25. elif diff < 0:
  26. result = newNodeIT(nkObjUpConv, n.info, d)
  27. result.add n
  28. if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
  29. elif diff > 0:
  30. result = newNodeIT(nkObjDownConv, n.info, d)
  31. result.add n
  32. if not downcast:
  33. internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
  34. else:
  35. result = n
  36. else:
  37. result = n
  38. proc getDispatcher*(s: PSym): PSym =
  39. ## can return nil if is has no dispatcher.
  40. if dispatcherPos < s.ast.len:
  41. result = s.ast[dispatcherPos].sym
  42. doAssert sfDispatcher in result.flags
  43. else:
  44. result = nil
  45. proc methodCall*(n: PNode; conf: ConfigRef): PNode =
  46. result = n
  47. # replace ordinary method by dispatcher method:
  48. let disp = getDispatcher(result[0].sym)
  49. if disp != nil:
  50. result[0].typ = disp.typ
  51. result[0].sym = disp
  52. # change the arguments to up/downcasts to fit the dispatcher's parameters:
  53. for i in 1..<result.len:
  54. result[i] = genConv(result[i], disp.typ[i], true, conf)
  55. else:
  56. localError(conf, n.info, "'" & $result[0] & "' lacks a dispatcher")
  57. type
  58. MethodResult = enum No, Invalid, Yes
  59. proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
  60. result = No
  61. if a.name.id != b.name.id: return
  62. if a.typ.signatureLen != b.typ.signatureLen:
  63. return
  64. var i = 0
  65. for x, y in paramTypePairs(a.typ, b.typ):
  66. inc i
  67. var aa = x
  68. var bb = y
  69. while true:
  70. aa = skipTypes(aa, {tyGenericInst, tyAlias})
  71. bb = skipTypes(bb, {tyGenericInst, tyAlias})
  72. if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
  73. aa = aa.elementType
  74. bb = bb.elementType
  75. else:
  76. break
  77. if sameType(x, y):
  78. if aa.kind == tyObject and result != Invalid:
  79. result = Yes
  80. elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
  81. let diff = inheritanceDiff(bb, aa)
  82. if diff < 0:
  83. if result != Invalid:
  84. result = Yes
  85. else:
  86. return No
  87. elif diff != high(int) and sfFromGeneric notin (a.flags+b.flags):
  88. result = Invalid
  89. else:
  90. return No
  91. else:
  92. return No
  93. if result == Yes:
  94. # check for return type:
  95. # ignore flags of return types; # bug #22673
  96. if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
  97. if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
  98. # infer 'auto' from the base to make it consistent:
  99. b.typ.setReturnType a.typ.returnType
  100. else:
  101. return No
  102. proc attachDispatcher(s: PSym, dispatcher: PNode) =
  103. if dispatcherPos < s.ast.len:
  104. # we've added a dispatcher already, so overwrite it
  105. s.ast[dispatcherPos] = dispatcher
  106. else:
  107. setLen(s.ast.sons, dispatcherPos+1)
  108. if s.ast[resultPos] == nil:
  109. s.ast[resultPos] = newNodeI(nkEmpty, s.info)
  110. s.ast[dispatcherPos] = dispatcher
  111. proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
  112. var disp = copySym(s, idgen)
  113. incl(disp.flags, sfDispatcher)
  114. excl(disp.flags, sfExported)
  115. let old = disp.typ
  116. disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
  117. copyTypeProps(g, idgen.module, disp.typ, old)
  118. # we can't inline the dispatcher itself (for now):
  119. if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
  120. disp.ast = copyTree(s.ast)
  121. disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
  122. disp.loc.r = ""
  123. if s.typ.returnType != nil:
  124. if disp.ast.len > resultPos:
  125. disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
  126. else:
  127. # We've encountered a method prototype without a filled-in
  128. # resultPos slot. We put a placeholder in there that will
  129. # be updated in fixupDispatcher().
  130. disp.ast.add newNodeI(nkEmpty, s.info)
  131. attachDispatcher(s, newSymNode(disp))
  132. # attach to itself to prevent bugs:
  133. attachDispatcher(disp, newSymNode(disp))
  134. return disp
  135. proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
  136. # We may have constructed the dispatcher from a method prototype
  137. # and need to augment the incomplete dispatcher with information
  138. # from later definitions, particularly the resultPos slot. Also,
  139. # the lock level of the dispatcher needs to be updated/checked
  140. # against that of the method.
  141. if disp.ast.len > resultPos and meth.ast.len > resultPos and
  142. disp.ast[resultPos].kind == nkEmpty:
  143. disp.ast[resultPos] = copyTree(meth.ast[resultPos])
  144. proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
  145. var witness: PSym = nil
  146. if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
  147. g.config.isDefined("nimInternalNonVtablesTesting"):
  148. localError(g.config, s.info, errGenerated, "method `" & s.name.s &
  149. "` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
  150. if sfImportc in s.flags:
  151. localError(g.config, s.info, errGenerated, "method `" & s.name.s &
  152. "` is not allowed to have 'importc' pragmas")
  153. for i in 0..<g.methods.len:
  154. let disp = g.methods[i].dispatcher
  155. case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
  156. of Yes:
  157. g.methods[i].methods.add(s)
  158. attachDispatcher(s, disp.ast[dispatcherPos])
  159. fixupDispatcher(s, disp, g.config)
  160. #echo "fixup ", disp.name.s, " ", disp.id
  161. when useEffectSystem: checkMethodEffects(g, disp, s)
  162. if {sfBase, sfFromGeneric} * s.flags == {sfBase} and
  163. g.methods[i].methods[0] != s:
  164. # already exists due to forwarding definition?
  165. localError(g.config, s.info, "method is not a base")
  166. return
  167. of No: discard
  168. of Invalid:
  169. if witness.isNil: witness = g.methods[i].methods[0]
  170. # create a new dispatcher:
  171. # stores the id and the position
  172. if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
  173. g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
  174. else:
  175. g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
  176. g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
  177. #echo "adding ", s.info
  178. if witness != nil:
  179. localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
  180. "' to method defined here: " & g.config$witness.info)
  181. elif sfBase notin s.flags:
  182. message(g.config, s.info, warnUseBase)
  183. proc relevantCol*(methods: seq[PSym], col: int): bool =
  184. # returns true iff the position is relevant
  185. result = false
  186. var t = methods[0].typ[col].skipTypes(skipPtrs)
  187. if t.kind == tyObject:
  188. for i in 1..high(methods):
  189. let t2 = skipTypes(methods[i].typ[col], skipPtrs)
  190. if not sameType(t2, t):
  191. return true
  192. proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
  193. result = 0
  194. for col in FirstParamAt..<a.typ.signatureLen:
  195. if contains(relevantCols, col):
  196. var aa = skipTypes(a.typ[col], skipPtrs)
  197. var bb = skipTypes(b.typ[col], skipPtrs)
  198. var d = inheritanceDiff(aa, bb)
  199. if (d != high(int)) and d != 0:
  200. return d
  201. proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
  202. # we use shellsort here; fast and simple
  203. var n = a.len
  204. var h = 1
  205. while true:
  206. h = 3 * h + 1
  207. if h > n: break
  208. while true:
  209. h = h div 3
  210. for i in h..<n:
  211. var v = a[i]
  212. var j = i
  213. while cmpSignatures(a[j - h], v, relevantCols) >= 0:
  214. a[j] = a[j - h]
  215. j = j - h
  216. if j < h: break
  217. a[j] = v
  218. if h == 1: break
  219. proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
  220. var base = methods[0].ast[dispatcherPos].sym
  221. result = base
  222. var paramLen = base.typ.signatureLen
  223. var nilchecks = newNodeI(nkStmtList, base.info)
  224. var disp = newNodeI(nkIfStmt, base.info)
  225. var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
  226. var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
  227. let boolType = getSysType(g, unknownLineInfo, tyBool)
  228. for col in FirstParamAt..<paramLen:
  229. if contains(relevantCols, col):
  230. let param = base.typ.n[col].sym
  231. if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
  232. nilchecks.add newTree(nkCall,
  233. newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param))
  234. for meth in 0..high(methods):
  235. var curr = methods[meth] # generate condition:
  236. var cond: PNode = nil
  237. for col in FirstParamAt..<paramLen:
  238. if contains(relevantCols, col):
  239. var isn = newNodeIT(nkCall, base.info, boolType)
  240. isn.add newSymNode(iss)
  241. let param = base.typ.n[col].sym
  242. isn.add newSymNode(param)
  243. isn.add newNodeIT(nkType, base.info, curr.typ[col])
  244. if cond != nil:
  245. var a = newNodeIT(nkCall, base.info, boolType)
  246. a.add newSymNode(ands)
  247. a.add cond
  248. a.add isn
  249. cond = a
  250. else:
  251. cond = isn
  252. let retTyp = base.typ.returnType
  253. let call = newNodeIT(nkCall, base.info, retTyp)
  254. call.add newSymNode(curr)
  255. for col in 1..<paramLen:
  256. call.add genConv(newSymNode(base.typ.n[col].sym),
  257. curr.typ[col], false, g.config)
  258. var ret: PNode
  259. if retTyp != nil:
  260. var a = newNodeI(nkFastAsgn, base.info)
  261. a.add newSymNode(base.ast[resultPos].sym)
  262. a.add call
  263. ret = newNodeI(nkReturnStmt, base.info)
  264. ret.add a
  265. else:
  266. ret = call
  267. if cond != nil:
  268. var a = newNodeI(nkElifBranch, base.info)
  269. a.add cond
  270. a.add ret
  271. disp.add a
  272. else:
  273. disp = ret
  274. nilchecks.add disp
  275. nilchecks.flags.incl nfTransf # should not be further transformed
  276. result.ast[bodyPos] = nilchecks
  277. proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
  278. for bucket in 0..<g.methods.len:
  279. var relevantCols = initIntSet()
  280. for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
  281. if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
  282. if optMultiMethods notin g.config.globalOptions:
  283. # if multi-methods are not enabled, we are interested only in the first field
  284. break
  285. sortBucket(g.methods[bucket].methods, relevantCols)
  286. g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)