importer.nim 14 KB

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