importer.nim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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 the symbol importing mechanism.
  10. import
  11. intsets, ast, astalgo, msgs, options, idents, lookups,
  12. semdata, modulepaths, sigmatch, lineinfos, sets,
  13. modulegraphs, wordrecg, tables
  14. from strutils import `%`
  15. when defined(nimPreviewSlimSystem):
  16. import std/assertions
  17. proc readExceptSet*(c: PContext, n: PNode): IntSet =
  18. assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
  19. result = initIntSet()
  20. for i in 1..<n.len:
  21. let ident = lookups.considerQuotedIdent(c, n[i])
  22. result.incl(ident.id)
  23. proc declarePureEnumField*(c: PContext; s: PSym) =
  24. # XXX Remove the outer 'if' statement and see what breaks.
  25. var amb = false
  26. if someSymFromImportTable(c, s.name, amb) == nil:
  27. strTableAdd(c.pureEnumFields, s)
  28. when false:
  29. let checkB = strTableGet(c.pureEnumFields, s.name)
  30. if checkB == nil:
  31. strTableAdd(c.pureEnumFields, s)
  32. when false:
  33. # mark as ambiguous:
  34. incl(c.ambiguousSymbols, checkB.id)
  35. incl(c.ambiguousSymbols, s.id)
  36. proc importPureEnumField(c: PContext; s: PSym) =
  37. var amb = false
  38. if someSymFromImportTable(c, s.name, amb) == nil:
  39. strTableAdd(c.pureEnumFields, s)
  40. when false:
  41. let checkB = strTableGet(c.pureEnumFields, s.name)
  42. if checkB == nil:
  43. strTableAdd(c.pureEnumFields, s)
  44. when false:
  45. # mark as ambiguous:
  46. incl(c.ambiguousSymbols, checkB.id)
  47. incl(c.ambiguousSymbols, s.id)
  48. proc importPureEnumFields(c: PContext; s: PSym; etyp: PType) =
  49. assert sfPure in s.flags
  50. for j in 0..<etyp.n.len:
  51. var e = etyp.n[j].sym
  52. if e.kind != skEnumField:
  53. internalError(c.config, s.info, "rawImportSymbol")
  54. # BUGFIX: because of aliases for enums the symbol may already
  55. # have been put into the symbol table
  56. # BUGFIX: but only iff they are the same symbols!
  57. for check in importedItems(c, e.name):
  58. if check.id == e.id:
  59. e = nil
  60. break
  61. if e != nil:
  62. importPureEnumField(c, e)
  63. proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
  64. # This does not handle stubs, because otherwise loading on demand would be
  65. # pointless in practice. So importing stubs is fine here!
  66. # check if we have already a symbol of the same name:
  67. when false:
  68. var check = someSymFromImportTable(c, s.name)
  69. if check != nil and check.id != s.id:
  70. if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
  71. # s and check need to be qualified:
  72. incl(c.ambiguousSymbols, s.id)
  73. incl(c.ambiguousSymbols, check.id)
  74. # thanks to 'export' feature, it could be we import the same symbol from
  75. # multiple sources, so we need to call 'strTableAdd' here:
  76. when false:
  77. # now lazy. Speeds up the compiler and is a prerequisite for IC.
  78. strTableAdd(c.importTable.symbols, s)
  79. else:
  80. importSet.incl s.id
  81. if s.kind == skType:
  82. var etyp = s.typ
  83. if etyp.kind in {tyBool, tyEnum}:
  84. for j in 0..<etyp.n.len:
  85. var e = etyp.n[j].sym
  86. if e.kind != skEnumField:
  87. internalError(c.config, s.info, "rawImportSymbol")
  88. # BUGFIX: because of aliases for enums the symbol may already
  89. # have been put into the symbol table
  90. # BUGFIX: but only iff they are the same symbols!
  91. for check in importedItems(c, e.name):
  92. if check.id == e.id:
  93. e = nil
  94. break
  95. if e != nil:
  96. if sfPure notin s.flags:
  97. rawImportSymbol(c, e, origin, importSet)
  98. else:
  99. importPureEnumField(c, e)
  100. else:
  101. if s.kind == skConverter: addConverter(c, LazySym(sym: s))
  102. if hasPattern(s): addPattern(c, LazySym(sym: s))
  103. if s.owner != origin:
  104. c.exportIndirections.incl((origin.id, s.id))
  105. proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) =
  106. template bail = globalError(c.config, n.info, "invalid pragma")
  107. result = (nil, @[])
  108. if n.kind == nkPragmaExpr:
  109. if n.len == 2 and n[1].kind == nkPragma:
  110. result[0] = n[0]
  111. for ni in n[1]:
  112. if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident)
  113. else: bail()
  114. else: bail()
  115. else:
  116. result[0] = n
  117. if result[0].safeLen > 0:
  118. (result[0][^1], result[1]) = splitPragmas(c, result[0][^1])
  119. proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
  120. let (n, kws) = splitPragmas(c, n)
  121. if kws.len > 0:
  122. globalError(c.config, n.info, "unexpected pragma")
  123. let ident = lookups.considerQuotedIdent(c, n)
  124. let s = someSym(c.graph, fromMod, ident)
  125. if s == nil:
  126. errorUndeclaredIdentifier(c, n.info, ident.s)
  127. else:
  128. when false:
  129. if s.kind == skStub: loadStub(s)
  130. let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
  131. # for an enumeration we have to add all identifiers
  132. if multiImport:
  133. # for a overloadable syms add all overloaded routines
  134. var it: ModuleIter
  135. var e = initModuleIter(it, c.graph, fromMod, s.name)
  136. while e != nil:
  137. if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
  138. if s.kind in ExportableSymKinds:
  139. rawImportSymbol(c, e, fromMod, importSet)
  140. e = nextModuleIter(it, c.graph)
  141. else:
  142. rawImportSymbol(c, s, fromMod, importSet)
  143. suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
  144. proc addImport(c: PContext; im: sink ImportedModule) =
  145. for i in 0..high(c.imports):
  146. if c.imports[i].m == im.m:
  147. # we have already imported the module: Check which import
  148. # is more "powerful":
  149. case c.imports[i].mode
  150. of importAll: discard "already imported all symbols"
  151. of importSet:
  152. case im.mode
  153. of importAll, importExcept:
  154. # XXX: slightly wrong semantics for 'importExcept'...
  155. # But we should probably change the spec and disallow this case.
  156. c.imports[i] = im
  157. of importSet:
  158. # merge the import sets:
  159. c.imports[i].imported.incl im.imported
  160. of importExcept:
  161. case im.mode
  162. of importAll:
  163. c.imports[i] = im
  164. of importSet:
  165. discard
  166. of importExcept:
  167. var cut = initIntSet()
  168. # only exclude what is consistent between the two sets:
  169. for j in im.exceptSet:
  170. if j in c.imports[i].exceptSet:
  171. cut.incl j
  172. c.imports[i].exceptSet = cut
  173. return
  174. c.imports.add im
  175. template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
  176. for it in mitems c.graph.ifaces[fromMod.position].converters:
  177. if filter:
  178. loadPackedSym(c.graph, it)
  179. if sfExported in it.sym.flags:
  180. addConverter(c, it)
  181. for it in mitems c.graph.ifaces[fromMod.position].patterns:
  182. if filter:
  183. loadPackedSym(c.graph, it)
  184. if sfExported in it.sym.flags:
  185. addPattern(c, it)
  186. for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
  187. if filter:
  188. loadPackedSym(c.graph, it)
  189. importPureEnumFields(c, it.sym, it.sym.typ)
  190. proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
  191. c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
  192. addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
  193. proc importAllSymbols*(c: PContext, fromMod: PSym) =
  194. c.addImport ImportedModule(m: fromMod, mode: importAll)
  195. addUnnamedIt(c, fromMod, true)
  196. when false:
  197. var exceptSet: IntSet
  198. importAllSymbolsExcept(c, fromMod, exceptSet)
  199. proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
  200. if n.isNil: return
  201. case n.kind
  202. of nkExportStmt:
  203. for a in n:
  204. assert a.kind == nkSym
  205. let s = a.sym
  206. if s.kind == skModule:
  207. importAllSymbolsExcept(c, s, exceptSet)
  208. elif exceptSet.isNil or s.name.id notin exceptSet:
  209. rawImportSymbol(c, s, fromMod, importSet)
  210. of nkExportExceptStmt:
  211. localError(c.config, n.info, "'export except' not implemented")
  212. else:
  213. for i in 0..n.safeLen-1:
  214. importForwarded(c, n[i], exceptSet, fromMod, importSet)
  215. proc addUnique[T](x: var seq[T], y: sink T) {.noSideEffect.} =
  216. for i in 0..high(x):
  217. if x[i] == y: return
  218. x.add y
  219. proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
  220. result = realModule
  221. template createModuleAliasImpl(ident): untyped =
  222. createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
  223. if n.kind != nkImportAs: discard
  224. elif n.len != 2 or n[1].kind != nkIdent:
  225. localError(c.config, n.info, "module alias must be an identifier")
  226. elif n[1].ident.id != realModule.name.id:
  227. # some misguided guy will write 'import abc.foo as foo' ...
  228. result = createModuleAliasImpl(n[1].ident)
  229. if result == realModule:
  230. # avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
  231. result = createModuleAliasImpl(realModule.name)
  232. if importHidden:
  233. result.options.incl optImportHidden
  234. c.unusedImports.add((result, n.info))
  235. c.importModuleMap[result.id] = realModule.id
  236. c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
  237. proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
  238. var ret: typeof(result)
  239. proc processPragma(n2: PNode): PNode =
  240. let (result2, kws) = splitPragmas(c, n2)
  241. result = result2
  242. for ai in kws:
  243. case ai
  244. of wImportHidden: ret.importHidden = true
  245. else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden})
  246. if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
  247. ret.node = newNodeI(nkImportAs, n.info)
  248. ret.node.add n[1].processPragma
  249. ret.node.add n[2]
  250. else:
  251. ret.node = n.processPragma
  252. return ret
  253. proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
  254. let transf = transformImportAs(c, n)
  255. n = transf.node
  256. let f = checkModuleName(c.config, n)
  257. if f != InvalidFileIdx:
  258. addImportFileDep(c, f)
  259. let L = c.graph.importStack.len
  260. let recursion = c.graph.importStack.find(f)
  261. c.graph.importStack.add f
  262. #echo "adding ", toFullPath(f), " at ", L+1
  263. if recursion >= 0:
  264. var err = ""
  265. for i in recursion..<L:
  266. if i > recursion: err.add "\n"
  267. err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
  268. toFullPath(c.config, c.graph.importStack[i+1])
  269. c.recursiveDep = err
  270. var realModule: PSym
  271. discard pushOptionEntry(c)
  272. realModule = c.graph.importModuleCallback(c.graph, c.module, f)
  273. result = importModuleAs(c, n, realModule, transf.importHidden)
  274. popOptionEntry(c)
  275. #echo "set back to ", L
  276. c.graph.importStack.setLen(L)
  277. # we cannot perform this check reliably because of
  278. # test: modules/import_in_config) # xxx is that still true?
  279. if realModule == c.module:
  280. localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s)
  281. if sfDeprecated in realModule.flags:
  282. var prefix = ""
  283. if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
  284. message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
  285. suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
  286. importStmtResult.add newSymNode(result, n.info)
  287. #newStrNode(toFullPath(c.config, f), n.info)
  288. else:
  289. result = nil
  290. proc afterImport(c: PContext, m: PSym) =
  291. # fixes bug #17510, for re-exported symbols
  292. let realModuleId = c.importModuleMap[m.id]
  293. for s in allSyms(c.graph, m):
  294. if s.owner.id != realModuleId:
  295. c.exportIndirections.incl((m.id, s.id))
  296. proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
  297. var it = it
  298. let m = myImportModule(c, it, importStmtResult)
  299. if m != nil:
  300. # ``addDecl`` needs to be done before ``importAllSymbols``!
  301. addDecl(c, m, it.info) # add symbol to symbol table of module
  302. importAllSymbols(c, m)
  303. #importForwarded(c, m.ast, emptySet, m)
  304. afterImport(c, m)
  305. proc evalImport*(c: PContext, n: PNode): PNode =
  306. result = newNodeI(nkImportStmt, n.info)
  307. for i in 0..<n.len:
  308. let it = n[i]
  309. if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
  310. let lastPos = it.len - 1
  311. var imp = copyNode(it)
  312. newSons(imp, it.len)
  313. for i in 0 ..< lastPos: imp[i] = it[i]
  314. imp[lastPos] = imp[0] # dummy entry, replaced in the loop
  315. for x in it[lastPos]:
  316. # transform `a/b/[c as d]` to `/a/b/c as d`
  317. if x.kind == nkInfix and x[0].ident.s == "as":
  318. var impAs = copyNode(x)
  319. newSons(impAs, 3)
  320. impAs[0] = x[0]
  321. imp[lastPos] = x[1]
  322. impAs[1] = imp
  323. impAs[2] = x[2]
  324. impMod(c, impAs, result)
  325. else:
  326. imp[lastPos] = x
  327. impMod(c, imp, result)
  328. else:
  329. impMod(c, it, result)
  330. proc evalFrom*(c: PContext, n: PNode): PNode =
  331. result = newNodeI(nkImportStmt, n.info)
  332. checkMinSonsLen(n, 2, c.config)
  333. var m = myImportModule(c, n[0], result)
  334. if m != nil:
  335. n[0] = newSymNode(m)
  336. addDecl(c, m, n.info) # add symbol to symbol table of module
  337. var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
  338. for i in 1..<n.len:
  339. if n[i].kind != nkNilLit:
  340. importSymbol(c, n[i], m, im.imported)
  341. c.addImport im
  342. afterImport(c, m)
  343. proc evalImportExcept*(c: PContext, n: PNode): PNode =
  344. result = newNodeI(nkImportStmt, n.info)
  345. checkMinSonsLen(n, 2, c.config)
  346. var m = myImportModule(c, n[0], result)
  347. if m != nil:
  348. n[0] = newSymNode(m)
  349. addDecl(c, m, n.info) # add symbol to symbol table of module
  350. importAllSymbolsExcept(c, m, readExceptSet(c, n))
  351. #importForwarded(c, m.ast, exceptSet, m)
  352. afterImport(c, m)