Skip to content

Commit

Permalink
[feature] allow import foo {.privateImport.}
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Aug 1, 2019
1 parent 803406d commit d6a6b68
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 44 deletions.
9 changes: 8 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,8 @@ type
# No need, just leave it as skModule but set the owner accordingly and
# check for the owner when touching 'usedGenerics'.
usedGenerics*: seq[PInstantiation]
tab*: TStrTable # interface table for modules
tab*: TStrTable # interface table for modules
tabAll*: TStrTable # interface table for modules (all top-level)
of skLet, skVar, skField, skForVar:
guard*: PSym
bitsize*: int
Expand Down Expand Up @@ -1367,6 +1368,7 @@ proc copySym*(s: PSym): PSym =
result.magic = s.magic
if s.kind == skModule:
copyStrTable(result.tab, s.tab)
copyStrTable(result.tabAll, s.tabAll)
result.options = s.options
result.position = s.position
result.loc = s.loc
Expand All @@ -1382,6 +1384,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
result.id = s.id
result.flags = s.flags
system.shallowCopy(result.tab, s.tab)
system.shallowCopy(result.tabAll, s.tabAll)
result.options = s.options
result.position = s.position
result.loc = s.loc
Expand Down Expand Up @@ -1814,3 +1817,7 @@ proc addParam*(procType: PType; param: PSym) =
template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
template asink*(t: PType): PSym = t.attachedOps[attachedSink]

template tabOpt*(m: PSym): untyped =
if optPrivateImport in m.options: m.tabAll
else: m.tab
70 changes: 50 additions & 20 deletions compiler/importer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import
intsets, ast, astalgo, msgs, options, idents, lookups,
semdata, modulepaths, sigmatch, lineinfos
semdata, modulepaths, sigmatch, lineinfos, wordrecg

proc readExceptSet*(c: PContext, n: PNode): IntSet =
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
Expand Down Expand Up @@ -71,9 +71,17 @@ proc rawImportSymbol(c: PContext, s: PSym) =
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)

template getTab(c: PContext, fromMod: PSym): untyped =
if fromMod in c.friendModulesPrivateImport:
# so that `privateImport` also works with `import foo` without `as`
# another option is to force `createModuleAlias` to remove this branch
fromMod.tabAll
else:
fromMod.tabOpt

proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerQuotedIdent(c, n)
let s = strTableGet(fromMod.tab, ident)
let s = strTableGet(c.getTab(fromMod), ident)
if s == nil:
errorUndeclaredIdentifier(c, n.info, ident.s)
else:
Expand All @@ -84,27 +92,27 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
if multiImport:
# for a overloadable syms add all overloaded routines
var it: TIdentIter
var e = initIdentIter(it, fromMod.tab, s.name)
var e = initIdentIter(it, c.getTab(fromMod), s.name)
while e != nil:
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
if s.kind in ExportableSymKinds:
rawImportSymbol(c, e)
e = nextIdentIter(it, fromMod.tab)
e = nextIdentIter(it, c.getTab(fromMod))
else:
rawImportSymbol(c, s)
suggestSym(c.config, n.info, s, c.graph.usageSym, false)

proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
var i: TTabIter
var s = initTabIter(i, fromMod.tab)
var s = initTabIter(i, c.getTab(fromMod))
while s != nil:
if s.kind != skModule:
if s.kind != skEnumField:
if s.kind notin ExportableSymKinds:
internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s)
if exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s)
s = nextIter(i, fromMod.tab)
s = nextIter(i, c.getTab(fromMod))

proc importAllSymbols*(c: PContext, fromMod: PSym) =
var exceptSet: IntSet
Expand All @@ -127,7 +135,12 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
for i in 0..safeLen(n)-1:
importForwarded(c, n.sons[i], exceptSet)

proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
type
ImportFlag = enum
ifPrivateImport
ImportFlags = set[ImportFlag]

proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importFlags: ImportFlags): PSym =
result = realModule
c.unusedImports.add((realModule, n.info))
if n.kind != nkImportAs: discard
Expand All @@ -137,8 +150,35 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
# some misguided guy will write 'import abc.foo as foo' ...
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
c.config.options)
if ifPrivateImport in importFlags: result.options.incl optPrivateImport
if ifPrivateImport in importFlags:
c.friendModulesPrivateImport.add realModule # `realModule` needed, not `result`

proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importFlags: ImportFlags] =
var ret: typeof(result)
proc processPragma(n2: PNode): PNode =
if n2.kind == nkPragmaExpr:
if n2.len == 2 and n2[1].kind == nkPragma and n2[1].len == 1 and n2[1][0].kind == nkIdent and whichKeyword(n2[1][0].ident) == wPrivateImport: discard
else:
globalError(c.config, n.info, "invalid import pragma, expected: " & wPrivateImport.canonPragmaSpelling)
if allowPrivateImport notin c.features:
globalError(c.config, n.info, "requires --experimental:" & $allowPrivateImport)
ret.importFlags.incl ifPrivateImport
result = n2[0]
else:
result = n2

proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
ret.node = newNodeI(nkImportAs, n.info)
ret.node.add n.sons[1].processPragma
ret.node.add n.sons[2]
else:
ret.node = n.processPragma
return ret

proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
var importFlags: ImportFlags
(n,importFlags) = transformImportAs(c, n)
let f = checkModuleName(c.config, n)
if f != InvalidFileIdx:
let L = c.graph.importStack.len
Expand All @@ -152,7 +192,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
toFullPath(c.config, c.graph.importStack[i+1])
c.recursiveDep = err
result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f))
result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f), importFlags)
#echo "set back to ", L
c.graph.importStack.setLen(L)
# we cannot perform this check reliably because of
Expand All @@ -170,16 +210,8 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info)

proc transformImportAs(c: PContext; n: PNode): PNode =
if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
result = newNodeI(nkImportAs, n.info)
result.add n.sons[1]
result.add n.sons[2]
else:
result = n

proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
let it = transformImportAs(c, it)
var it = it
let m = myImportModule(c, it, importStmtResult)
if m != nil:
var emptySet: IntSet
Expand Down Expand Up @@ -215,7 +247,6 @@ proc evalImport*(c: PContext, n: PNode): PNode =
proc evalFrom*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info)
checkMinSonsLen(n, 2, c.config)
n.sons[0] = transformImportAs(c, n.sons[0])
var m = myImportModule(c, n.sons[0], result)
if m != nil:
n.sons[0] = newSymNode(m)
Expand All @@ -227,7 +258,6 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
proc evalImportExcept*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info)
checkMinSonsLen(n, 2, c.config)
n.sons[0] = transformImportAs(c, n.sons[0])
var m = myImportModule(c, n.sons[0], result)
if m != nil:
n.sons[0] = newSymNode(m)
Expand Down
6 changes: 3 additions & 3 deletions compiler/lookups.nim
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
if m == c.module:
result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
else:
result = strTableGet(m.tab, ident).skipAlias(n, c.config)
result = strTableGet(m.tabOpt, ident).skipAlias(n, c.config)
if result == nil and checkUndeclared in flags:
fixSpelling(n.sons[1], ident, searchInScopes)
errorUndeclaredIdentifier(c, n.sons[1].info, ident.s)
Expand Down Expand Up @@ -386,7 +386,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
ident).skipAlias(n, c.config)
o.mode = oimSelfModule
else:
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config)
result = initIdentIter(o.it, o.m.tabOpt, ident).skipAlias(n, c.config)
else:
noidentError(c.config, n.sons[1], n)
result = errorSym(c, n.sons[1])
Expand Down Expand Up @@ -428,7 +428,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
of oimSelfModule:
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
of oimOtherModule:
result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config)
result = nextIdentIter(o.it, o.m.tabOpt).skipAlias(n, c.config)
of oimSymChoice:
if o.symChoiceIndex < sonsLen(n):
result = n.sons[o.symChoiceIndex].sym
Expand Down
44 changes: 28 additions & 16 deletions compiler/modulegraphs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -133,31 +133,43 @@ proc hash*(u: SigHash): Hash =

proc hash*(x: FileIndex): Hash {.borrow.}

template getC(): untyped =
when compiles(c.c.graph): c.c
else: c

template onDefAux(info: TLineInfo; s0: PSym, c0: untyped, isFwd: bool) =
if s0.kind in ExportableSymKinds:
let c = c0 # in case c0 is an expression
var top = true
case s0.kind
of skProc, skMacro:
# unfortunately, can't use `c.isTopLevel` because the scope isn't closed yet
top = c.currentScope.depthLevel <= 3
else: top = c.currentScope.depthLevel <= 2
if top:
let loc = toFileLineCol(c.config, info)
if c.module != nil: strTableAdd(c.module.tabAll, s0)

when defined(nimfind):
template onUse*(info: TLineInfo; s: PSym) =
when compiles(c.c.graph):
if c.c.graph.onUsage != nil: c.c.graph.onUsage(c.c.graph, s, info)
else:
if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info)
let c2 = getC()
if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info)

template onDef*(info: TLineInfo; s: PSym) =
when compiles(c.c.graph):
if c.c.graph.onDefinition != nil: c.c.graph.onDefinition(c.c.graph, s, info)
else:
if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info)
let c2 = getC()
onDefAux(info, s, c2, false)
if c2.graph.onDefinition != nil: c2.graph.onDefinition(c2.graph, s, info)

template onDefResolveForward*(info: TLineInfo; s: PSym) =
when compiles(c.c.graph):
if c.c.graph.onDefinitionResolveForward != nil:
c.c.graph.onDefinitionResolveForward(c.c.graph, s, info)
else:
if c.graph.onDefinitionResolveForward != nil:
c.graph.onDefinitionResolveForward(c.graph, s, info)
let c2 = getC()
onDefAux(info, s, c2, true)
if c2.graph.onDefinitionResolveForward != nil:
c2.graph.onDefinitionResolveForward(c2.graph, s, info)

else:
template onUse*(info: TLineInfo; s: PSym) = discard
template onDef*(info: TLineInfo; s: PSym) = discard
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
template onDef*(info: TLineInfo; s: PSym) = onDefAux(info, s, getC(), false)
template onDefResolveForward*(info: TLineInfo; s: PSym) = onDefAux(info, s, getC(), true)

proc stopCompile*(g: ModuleGraph): bool {.inline.} =
result = g.doStopCompile != nil and g.doStopCompile()
Expand Down
3 changes: 3 additions & 0 deletions compiler/modules.nim
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil

incl(result.flags, sfUsed)
initStrTable(result.tab)
initStrTable(result.tabAll)
strTableAdd(result.tab, result) # a module knows itself
strTableAdd(result.tabAll, result)
strTableAdd(packSym.tab, result)

proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
Expand Down Expand Up @@ -91,6 +93,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
result.flags.excl sfDirty
# reset module fields:
initStrTable(result.tab)
initStrTable(result.tabAll)
result.ast = nil
discard processModule(graph, result,
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
Expand Down
4 changes: 3 additions & 1 deletion compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ type # please make sure we have under 32 options
optMemTracker,
optLaxStrings,
optNilSeqs,
optOldAst
optOldAst,
optPrivateImport,

TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
Expand Down Expand Up @@ -134,6 +135,7 @@ type
## This requires building nim with `-d:nimHasLibFFI`
## which itself requires `nimble install libffi`, see #10150
## Note: this feature can't be localized with {.push.}
allowPrivateImport,

SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf
Expand Down
4 changes: 3 additions & 1 deletion compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ type
friendModules*: seq[PSym] # friend modules; may access private data;
# this is used so that generic instantiations
# can access private object fields
instCounter*: int # to prevent endless instantiations
# to prevent endless instantiations
friendModulesPrivateImport*: seq[PSym] # enable access to private fields
instCounter*: int

ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
Expand Down
4 changes: 4 additions & 0 deletions compiler/suggest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
if fmoduleId == module.id:
result = true
break
for module in c.friendModulesPrivateImport:
if fmoduleId == module.id:
result = true
break

proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
var pm: PrefixMatch
Expand Down
6 changes: 4 additions & 2 deletions compiler/wordrecg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ type
wStdIn, wStdOut, wStdErr,

wInOut, wByCopy, wByRef, wOneWay,
wBitsize
wBitsize,
wPrivateImport,

TSpecialWords* = set[TSpecialWord]

Expand Down Expand Up @@ -176,7 +177,7 @@ const
"stdin", "stdout", "stderr",

"inout", "bycopy", "byref", "oneway",
"bitsize"
"bitsize", "privateimport"
]

proc findStr*(a: openArray[string], s: string): int =
Expand Down Expand Up @@ -215,4 +216,5 @@ proc canonPragmaSpelling*(w: TSpecialWord): string =
of wImplicitStatic: "implicitStatic"
of wCodegenDecl: "codegenDecl"
of wLiftLocals: "liftLocals"
of wPrivateImport: "privateImport"
else: specialWords[w]

0 comments on commit d6a6b68

Please sign in to comment.