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