disallow recursive module dependencies
This commit is contained in:
parent
204838b358
commit
074f276c8a
14 changed files with 47 additions and 45 deletions
|
|
@ -162,12 +162,26 @@ proc importModuleAs(n: PNode, realModule: PSym): PSym =
|
||||||
proc myImportModule(c: PContext, n: PNode): PSym =
|
proc myImportModule(c: PContext, n: PNode): PSym =
|
||||||
var f = checkModuleName(n)
|
var f = checkModuleName(n)
|
||||||
if f != InvalidFileIDX:
|
if f != InvalidFileIDX:
|
||||||
|
let L = c.graph.importStack.len
|
||||||
|
let recursion = c.graph.importStack.find(f)
|
||||||
|
c.graph.importStack.add f
|
||||||
|
#echo "adding ", toFullPath(f), " at ", L+1
|
||||||
|
if recursion >= 0:
|
||||||
|
var err = ""
|
||||||
|
for i in countup(recursion, L-1):
|
||||||
|
if i > 0: err.add "\n"
|
||||||
|
err.add toFullPath(c.graph.importStack[i]) & " imports " &
|
||||||
|
toFullPath(c.graph.importStack[i+1])
|
||||||
|
localError(n.info, "recursive module dependency detected:\n" & err)
|
||||||
result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache))
|
result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache))
|
||||||
|
#echo "set back to ", L
|
||||||
|
c.graph.importStack.setLen(L)
|
||||||
# we cannot perform this check reliably because of
|
# we cannot perform this check reliably because of
|
||||||
# test: modules/import_in_config)
|
# test: modules/import_in_config)
|
||||||
if result.info.fileIndex == c.module.info.fileIndex and
|
when false:
|
||||||
result.info.fileIndex == n.info.fileIndex:
|
if result.info.fileIndex == c.module.info.fileIndex and
|
||||||
localError(n.info, errGenerated, "A module cannot import itself")
|
result.info.fileIndex == n.info.fileIndex:
|
||||||
|
localError(n.info, errGenerated, "A module cannot import itself")
|
||||||
if sfDeprecated in result.flags:
|
if sfDeprecated in result.flags:
|
||||||
message(n.info, warnDeprecated, result.name.s)
|
message(n.info, warnDeprecated, result.name.s)
|
||||||
#suggestSym(n.info, result, false)
|
#suggestSym(n.info, result, false)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ type
|
||||||
invalidTransitiveClosure: bool
|
invalidTransitiveClosure: bool
|
||||||
inclToMod*: Table[int32, int32] # mapping of include file to the
|
inclToMod*: Table[int32, int32] # mapping of include file to the
|
||||||
# first module that included it
|
# first module that included it
|
||||||
|
importStack*: seq[int32] # The current import stack. Used for detecting recursive
|
||||||
|
# module dependencies.
|
||||||
|
|
||||||
{.this: g.}
|
{.this: g.}
|
||||||
|
|
||||||
|
|
@ -44,12 +46,14 @@ proc newModuleGraph*(): ModuleGraph =
|
||||||
initStrTable(result.packageSyms)
|
initStrTable(result.packageSyms)
|
||||||
result.deps = initIntSet()
|
result.deps = initIntSet()
|
||||||
result.modules = @[]
|
result.modules = @[]
|
||||||
|
result.importStack = @[]
|
||||||
result.inclToMod = initTable[int32, int32]()
|
result.inclToMod = initTable[int32, int32]()
|
||||||
|
|
||||||
proc resetAllModules*(g: ModuleGraph) =
|
proc resetAllModules*(g: ModuleGraph) =
|
||||||
initStrTable(packageSyms)
|
initStrTable(packageSyms)
|
||||||
deps = initIntSet()
|
deps = initIntSet()
|
||||||
modules = @[]
|
modules = @[]
|
||||||
|
importStack = @[]
|
||||||
inclToMod = initTable[int32, int32]()
|
inclToMod = initTable[int32, int32]()
|
||||||
|
|
||||||
proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
|
proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache;
|
||||||
wantMainModule()
|
wantMainModule()
|
||||||
let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
|
let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
|
||||||
let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
|
let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
|
||||||
|
graph.importStack.add projectFile
|
||||||
if projectFile == systemFileIdx:
|
if projectFile == systemFileIdx:
|
||||||
discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
|
discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -676,9 +676,8 @@ proc getInfoContext*(index: int): TLineInfo =
|
||||||
if i >=% L: result = unknownLineInfo()
|
if i >=% L: result = unknownLineInfo()
|
||||||
else: result = msgContext[i]
|
else: result = msgContext[i]
|
||||||
|
|
||||||
proc toFilename*(fileIdx: int32): string =
|
template toFilename*(fileIdx: int32): string =
|
||||||
if fileIdx < 0: result = "???"
|
(if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath)
|
||||||
else: result = fileInfos[fileIdx].projPath
|
|
||||||
|
|
||||||
proc toFullPath*(fileIdx: int32): string =
|
proc toFullPath*(fileIdx: int32): string =
|
||||||
if fileIdx < 0: result = "???"
|
if fileIdx < 0: result = "???"
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ proc handleCmdLine(cache: IdentCache) =
|
||||||
if gProjectName == "-":
|
if gProjectName == "-":
|
||||||
gProjectName = "stdinfile"
|
gProjectName = "stdinfile"
|
||||||
gProjectFull = "stdinfile"
|
gProjectFull = "stdinfile"
|
||||||
gProjectPath = getCurrentDir()
|
gProjectPath = canonicalizePath getCurrentDir()
|
||||||
gProjectIsStdin = true
|
gProjectIsStdin = true
|
||||||
elif gProjectName != "":
|
elif gProjectName != "":
|
||||||
try:
|
try:
|
||||||
|
|
@ -54,10 +54,10 @@ proc handleCmdLine(cache: IdentCache) =
|
||||||
except OSError:
|
except OSError:
|
||||||
gProjectFull = gProjectName
|
gProjectFull = gProjectName
|
||||||
let p = splitFile(gProjectFull)
|
let p = splitFile(gProjectFull)
|
||||||
gProjectPath = p.dir
|
gProjectPath = canonicalizePath p.dir
|
||||||
gProjectName = p.name
|
gProjectName = p.name
|
||||||
else:
|
else:
|
||||||
gProjectPath = getCurrentDir()
|
gProjectPath = canonicalizePath getCurrentDir()
|
||||||
loadConfigs(DefaultConfig) # load all config files
|
loadConfigs(DefaultConfig) # load all config files
|
||||||
let scriptFile = gProjectFull.changeFileExt("nims")
|
let scriptFile = gProjectFull.changeFileExt("nims")
|
||||||
if fileExists(scriptFile):
|
if fileExists(scriptFile):
|
||||||
|
|
|
||||||
|
|
@ -9,36 +9,8 @@ subtle. Only top-level symbols that are marked with an asterisk (``*``) are
|
||||||
exported. A valid module name can only be a valid Nim identifier (and thus its
|
exported. A valid module name can only be a valid Nim identifier (and thus its
|
||||||
filename is ``identifier.nim``).
|
filename is ``identifier.nim``).
|
||||||
|
|
||||||
The algorithm for compiling modules is:
|
Recursive module dependencies are not allowed. This restriction might be mitigated
|
||||||
|
or removed in later versions of the language.
|
||||||
- compile the whole module as usual, following import statements recursively
|
|
||||||
|
|
||||||
- if there is a cycle only import the already parsed symbols (that are
|
|
||||||
exported); if an unknown identifier occurs then abort
|
|
||||||
|
|
||||||
This is best illustrated by an example:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
# Module A
|
|
||||||
type
|
|
||||||
T1* = int # Module A exports the type ``T1``
|
|
||||||
import B # the compiler starts parsing B
|
|
||||||
|
|
||||||
proc main() =
|
|
||||||
var i = p(3) # works because B has been parsed completely here
|
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
# Module B
|
|
||||||
import A # A is not parsed here! Only the already known symbols
|
|
||||||
# of A are imported.
|
|
||||||
|
|
||||||
proc p*(x: A.T1): A.T1 =
|
|
||||||
# this works because the compiler has already
|
|
||||||
# added T1 to A's interface symbol table
|
|
||||||
result = x + 1
|
|
||||||
|
|
||||||
|
|
||||||
Import statement
|
Import statement
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tests/reject/trecincb.nim"
|
file: "trecincb.nim"
|
||||||
line: 9
|
line: 9
|
||||||
errormsg: "recursive dependency: 'tests/modules/trecincb.nim'"
|
errormsg: "recursive dependency: 'trecincb.nim'"
|
||||||
"""
|
"""
|
||||||
# Test recursive includes
|
# Test recursive includes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "trecincb.nim"
|
file: "trecincb.nim"
|
||||||
line: 9
|
line: 9
|
||||||
errormsg: "recursive dependency: 'tests/modules/trecincb.nim'"
|
errormsg: "recursive dependency: 'trecincb.nim'"
|
||||||
"""
|
"""
|
||||||
# Test recursive includes
|
# Test recursive includes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,7 @@
|
||||||
|
discard """
|
||||||
|
file: "mrecmod.nim"
|
||||||
|
line: 1
|
||||||
|
errormsg: "recursive module dependency detected"
|
||||||
|
"""
|
||||||
# recursive module
|
# recursive module
|
||||||
import mrecmod
|
import mrecmod
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
discard """
|
||||||
|
file: "mrecmod2.nim"
|
||||||
|
line: 2
|
||||||
|
errormsg: "recursive module dependency detected"
|
||||||
|
"""
|
||||||
type
|
type
|
||||||
T1* = int # Module A exports the type ``T1``
|
T1* = int # Module A exports the type ``T1``
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tselfimport.nim"
|
file: "tselfimport.nim"
|
||||||
line: 7
|
line: 7
|
||||||
errormsg: "A module cannot import itself"
|
errormsg: "recursive module dependency detected"
|
||||||
"""
|
"""
|
||||||
import strutils as su # guard against regression
|
import strutils as su # guard against regression
|
||||||
import tselfimport #ERROR
|
import tselfimport #ERROR
|
||||||
|
|
|
||||||
0
tests/system/tdeepcopy.nim
Executable file → Normal file
0
tests/system/tdeepcopy.nim
Executable file → Normal file
|
|
@ -431,10 +431,10 @@ proc handleCmdLine(cache: IdentCache) =
|
||||||
except OSError:
|
except OSError:
|
||||||
gProjectFull = gProjectName
|
gProjectFull = gProjectName
|
||||||
var p = splitFile(gProjectFull)
|
var p = splitFile(gProjectFull)
|
||||||
gProjectPath = p.dir
|
gProjectPath = canonicalizePath p.dir
|
||||||
gProjectName = p.name
|
gProjectName = p.name
|
||||||
else:
|
else:
|
||||||
gProjectPath = getCurrentDir()
|
gProjectPath = canonicalizePath getCurrentDir()
|
||||||
|
|
||||||
# Find Nim's prefix dir.
|
# Find Nim's prefix dir.
|
||||||
let binaryPath = findExe("nim")
|
let binaryPath = findExe("nim")
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ Changes affecting backwards compatibility
|
||||||
- ``TimeInfo.tzname`` has been removed from ``times`` module because it was
|
- ``TimeInfo.tzname`` has been removed from ``times`` module because it was
|
||||||
broken. Because of this, the option ``"ZZZ"`` will no longer work in format
|
broken. Because of this, the option ``"ZZZ"`` will no longer work in format
|
||||||
strings for formatting and parsing.
|
strings for formatting and parsing.
|
||||||
|
- Recursive module dependencies are now completely disallowed.
|
||||||
|
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue