importer.nim 13 KB

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