importer.nim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. if n.kind == nkPragmaExpr:
  108. if n.len == 2 and n[1].kind == nkPragma:
  109. result[0] = n[0]
  110. for ni in n[1]:
  111. if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident)
  112. else: bail()
  113. else: bail()
  114. else:
  115. result[0] = n
  116. if result[0].safeLen > 0:
  117. (result[0][^1], result[1]) = splitPragmas(c, result[0][^1])
  118. proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
  119. let (n, kws) = splitPragmas(c, n)
  120. if kws.len > 0:
  121. globalError(c.config, n.info, "unexpected pragma")
  122. let ident = lookups.considerQuotedIdent(c, n)
  123. let s = someSym(c.graph, fromMod, ident)
  124. if s == nil:
  125. errorUndeclaredIdentifier(c, n.info, ident.s)
  126. else:
  127. when false:
  128. if s.kind == skStub: loadStub(s)
  129. let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
  130. # for an enumeration we have to add all identifiers
  131. if multiImport:
  132. # for a overloadable syms add all overloaded routines
  133. var it: ModuleIter
  134. var e = initModuleIter(it, c.graph, fromMod, s.name)
  135. while e != nil:
  136. if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
  137. if s.kind in ExportableSymKinds:
  138. rawImportSymbol(c, e, fromMod, importSet)
  139. e = nextModuleIter(it, c.graph)
  140. else:
  141. rawImportSymbol(c, s, fromMod, importSet)
  142. suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
  143. proc addImport(c: PContext; im: sink ImportedModule) =
  144. for i in 0..high(c.imports):
  145. if c.imports[i].m == im.m:
  146. # we have already imported the module: Check which import
  147. # is more "powerful":
  148. case c.imports[i].mode
  149. of importAll: discard "already imported all symbols"
  150. of importSet:
  151. case im.mode
  152. of importAll, importExcept:
  153. # XXX: slightly wrong semantics for 'importExcept'...
  154. # But we should probably change the spec and disallow this case.
  155. c.imports[i] = im
  156. of importSet:
  157. # merge the import sets:
  158. c.imports[i].imported.incl im.imported
  159. of importExcept:
  160. case im.mode
  161. of importAll:
  162. c.imports[i] = im
  163. of importSet:
  164. discard
  165. of importExcept:
  166. var cut = initIntSet()
  167. # only exclude what is consistent between the two sets:
  168. for j in im.exceptSet:
  169. if j in c.imports[i].exceptSet:
  170. cut.incl j
  171. c.imports[i].exceptSet = cut
  172. return
  173. c.imports.add im
  174. template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
  175. for it in mitems c.graph.ifaces[fromMod.position].converters:
  176. if filter:
  177. loadPackedSym(c.graph, it)
  178. if sfExported in it.sym.flags:
  179. addConverter(c, it)
  180. for it in mitems c.graph.ifaces[fromMod.position].patterns:
  181. if filter:
  182. loadPackedSym(c.graph, it)
  183. if sfExported in it.sym.flags:
  184. addPattern(c, it)
  185. for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
  186. if filter:
  187. loadPackedSym(c.graph, it)
  188. importPureEnumFields(c, it.sym, it.sym.typ)
  189. proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
  190. c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
  191. addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
  192. proc importAllSymbols*(c: PContext, fromMod: PSym) =
  193. c.addImport ImportedModule(m: fromMod, mode: importAll)
  194. addUnnamedIt(c, fromMod, true)
  195. when false:
  196. var exceptSet: IntSet
  197. importAllSymbolsExcept(c, fromMod, exceptSet)
  198. proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
  199. if n.isNil: return
  200. case n.kind
  201. of nkExportStmt:
  202. for a in n:
  203. assert a.kind == nkSym
  204. let s = a.sym
  205. if s.kind == skModule:
  206. importAllSymbolsExcept(c, s, exceptSet)
  207. elif exceptSet.isNil or s.name.id notin exceptSet:
  208. rawImportSymbol(c, s, fromMod, importSet)
  209. of nkExportExceptStmt:
  210. localError(c.config, n.info, "'export except' not implemented")
  211. else:
  212. for i in 0..n.safeLen-1:
  213. importForwarded(c, n[i], exceptSet, fromMod, importSet)
  214. proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
  215. result = realModule
  216. template createModuleAliasImpl(ident): untyped =
  217. createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
  218. if n.kind != nkImportAs: discard
  219. elif n.len != 2 or n[1].kind != nkIdent:
  220. localError(c.config, n.info, "module alias must be an identifier")
  221. elif n[1].ident.id != realModule.name.id:
  222. # some misguided guy will write 'import abc.foo as foo' ...
  223. result = createModuleAliasImpl(n[1].ident)
  224. if result == realModule:
  225. # avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
  226. result = createModuleAliasImpl(realModule.name)
  227. if importHidden:
  228. result.options.incl optImportHidden
  229. c.unusedImports.add((result, n.info))
  230. c.importModuleMap[result.id] = realModule.id
  231. proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
  232. var ret: typeof(result)
  233. proc processPragma(n2: PNode): PNode =
  234. let (result2, kws) = splitPragmas(c, n2)
  235. result = result2
  236. for ai in kws:
  237. case ai
  238. of wImportHidden: ret.importHidden = true
  239. else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden})
  240. if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
  241. ret.node = newNodeI(nkImportAs, n.info)
  242. ret.node.add n[1].processPragma
  243. ret.node.add n[2]
  244. else:
  245. ret.node = n.processPragma
  246. return ret
  247. proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
  248. let transf = transformImportAs(c, n)
  249. n = transf.node
  250. let f = checkModuleName(c.config, n)
  251. if f != InvalidFileIdx:
  252. addImportFileDep(c, f)
  253. let L = c.graph.importStack.len
  254. let recursion = c.graph.importStack.find(f)
  255. c.graph.importStack.add f
  256. #echo "adding ", toFullPath(f), " at ", L+1
  257. if recursion >= 0:
  258. var err = ""
  259. for i in recursion..<L:
  260. if i > recursion: err.add "\n"
  261. err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
  262. toFullPath(c.config, c.graph.importStack[i+1])
  263. c.recursiveDep = err
  264. var realModule: PSym
  265. discard pushOptionEntry(c)
  266. realModule = c.graph.importModuleCallback(c.graph, c.module, f)
  267. result = importModuleAs(c, n, realModule, transf.importHidden)
  268. popOptionEntry(c)
  269. #echo "set back to ", L
  270. c.graph.importStack.setLen(L)
  271. # we cannot perform this check reliably because of
  272. # test: modules/import_in_config) # xxx is that still true?
  273. if realModule == c.module:
  274. localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s)
  275. if sfDeprecated in realModule.flags:
  276. var prefix = ""
  277. if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
  278. message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
  279. suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
  280. importStmtResult.add newSymNode(result, n.info)
  281. #newStrNode(toFullPath(c.config, f), n.info)
  282. proc afterImport(c: PContext, m: PSym) =
  283. # fixes bug #17510, for re-exported symbols
  284. let realModuleId = c.importModuleMap[m.id]
  285. for s in allSyms(c.graph, m):
  286. if s.owner.id != realModuleId:
  287. c.exportIndirections.incl((m.id, s.id))
  288. proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
  289. var it = it
  290. let m = myImportModule(c, it, importStmtResult)
  291. if m != nil:
  292. # ``addDecl`` needs to be done before ``importAllSymbols``!
  293. addDecl(c, m, it.info) # add symbol to symbol table of module
  294. importAllSymbols(c, m)
  295. #importForwarded(c, m.ast, emptySet, m)
  296. afterImport(c, m)
  297. proc evalImport*(c: PContext, n: PNode): PNode =
  298. result = newNodeI(nkImportStmt, n.info)
  299. for i in 0..<n.len:
  300. let it = n[i]
  301. if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
  302. let sep = it[0]
  303. let dir = it[1]
  304. var imp = newNodeI(nkInfix, it.info)
  305. imp.add sep
  306. imp.add dir
  307. imp.add sep # dummy entry, replaced in the loop
  308. for x in it[2]:
  309. # transform `a/b/[c as d]` to `/a/b/c as d`
  310. if x.kind == nkInfix and x[0].ident.s == "as":
  311. let impAs = copyTree(x)
  312. imp[2] = x[1]
  313. impAs[1] = imp
  314. impMod(c, imp, result)
  315. else:
  316. imp[2] = x
  317. impMod(c, imp, result)
  318. else:
  319. impMod(c, it, result)
  320. proc evalFrom*(c: PContext, n: PNode): PNode =
  321. result = newNodeI(nkImportStmt, n.info)
  322. checkMinSonsLen(n, 2, c.config)
  323. var m = myImportModule(c, n[0], result)
  324. if m != nil:
  325. n[0] = newSymNode(m)
  326. addDecl(c, m, n.info) # add symbol to symbol table of module
  327. var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
  328. for i in 1..<n.len:
  329. if n[i].kind != nkNilLit:
  330. importSymbol(c, n[i], m, im.imported)
  331. c.addImport im
  332. afterImport(c, m)
  333. proc evalImportExcept*(c: PContext, n: PNode): PNode =
  334. result = newNodeI(nkImportStmt, n.info)
  335. checkMinSonsLen(n, 2, c.config)
  336. var m = myImportModule(c, n[0], result)
  337. if m != nil:
  338. n[0] = newSymNode(m)
  339. addDecl(c, m, n.info) # add symbol to symbol table of module
  340. importAllSymbolsExcept(c, m, readExceptSet(c, n))
  341. #importForwarded(c, m.ast, exceptSet, m)
  342. afterImport(c, m)