summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/importer.nim20
-rw-r--r--compiler/modulegraphs.nim4
-rw-r--r--compiler/modules.nim1
-rw-r--r--compiler/msgs.nim5
-rw-r--r--compiler/nim.nim6
-rw-r--r--doc/manual/modules.txt32
-rw-r--r--tests/modules/trecinca.nim4
-rw-r--r--tests/modules/trecincb.nim2
-rw-r--r--tests/modules/trecmod.nim5
-rw-r--r--tests/modules/trecmod2.nim5
-rw-r--r--tests/modules/tselfimport.nim2
-rw-r--r--[-rwxr-xr-x]tests/system/tdeepcopy.nim0
-rw-r--r--tools/nimsuggest/nimsuggest.nim4
-rw-r--r--web/news/e029_version_0_16_0.rst2
14 files changed, 47 insertions, 45 deletions
diff --git a/compiler/importer.nim b/compiler/importer.nim
index ce365c4dc..46e4c159f 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -162,12 +162,26 @@ proc importModuleAs(n: PNode, realModule: PSym): PSym =
 proc myImportModule(c: PContext, n: PNode): PSym =
   var f = checkModuleName(n)
   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))
+    #echo "set back to ", L
+    c.graph.importStack.setLen(L)
     # we cannot perform this check reliably because of
     # test: modules/import_in_config)
-    if result.info.fileIndex == c.module.info.fileIndex and
-        result.info.fileIndex == n.info.fileIndex:
-      localError(n.info, errGenerated, "A module cannot import itself")
+    when false:
+      if result.info.fileIndex == c.module.info.fileIndex and
+          result.info.fileIndex == n.info.fileIndex:
+        localError(n.info, errGenerated, "A module cannot import itself")
     if sfDeprecated in result.flags:
       message(n.info, warnDeprecated, result.name.s)
     #suggestSym(n.info, result, false)
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 9a3caa663..38fd4f89f 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -36,6 +36,8 @@ type
     invalidTransitiveClosure: bool
     inclToMod*: Table[int32, int32] # mapping of include file to the
                                     # first module that included it
+    importStack*: seq[int32]  # The current import stack. Used for detecting recursive
+                              # module dependencies.
 
 {.this: g.}
 
@@ -44,12 +46,14 @@ proc newModuleGraph*(): ModuleGraph =
   initStrTable(result.packageSyms)
   result.deps = initIntSet()
   result.modules = @[]
+  result.importStack = @[]
   result.inclToMod = initTable[int32, int32]()
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
   deps = initIntSet()
   modules = @[]
+  importStack = @[]
   inclToMod = initTable[int32, int32]()
 
 proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 26ca2177b..3451d85ec 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -231,6 +231,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache;
   wantMainModule()
   let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
   let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
+  graph.importStack.add projectFile
   if projectFile == systemFileIdx:
     discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
   else:
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index a44a1306c..0f39eb4d3 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -676,9 +676,8 @@ proc getInfoContext*(index: int): TLineInfo =
   if i >=% L: result = unknownLineInfo()
   else: result = msgContext[i]
 
-proc toFilename*(fileIdx: int32): string =
-  if fileIdx < 0: result = "???"
-  else: result = fileInfos[fileIdx].projPath
+template toFilename*(fileIdx: int32): string =
+  (if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath)
 
 proc toFullPath*(fileIdx: int32): string =
   if fileIdx < 0: result = "???"
diff --git a/compiler/nim.nim b/compiler/nim.nim
index f8d6b607a..35afecf20 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -46,7 +46,7 @@ proc handleCmdLine(cache: IdentCache) =
     if gProjectName == "-":
       gProjectName = "stdinfile"
       gProjectFull = "stdinfile"
-      gProjectPath = getCurrentDir()
+      gProjectPath = canonicalizePath getCurrentDir()
       gProjectIsStdin = true
     elif gProjectName != "":
       try:
@@ -54,10 +54,10 @@ proc handleCmdLine(cache: IdentCache) =
       except OSError:
         gProjectFull = gProjectName
       let p = splitFile(gProjectFull)
-      gProjectPath = p.dir
+      gProjectPath = canonicalizePath p.dir
       gProjectName = p.name
     else:
-      gProjectPath = getCurrentDir()
+      gProjectPath = canonicalizePath getCurrentDir()
     loadConfigs(DefaultConfig) # load all config files
     let scriptFile = gProjectFull.changeFileExt("nims")
     if fileExists(scriptFile):
diff --git a/doc/manual/modules.txt b/doc/manual/modules.txt
index 9cb6a11af..8a9f5ff65 100644
--- a/doc/manual/modules.txt
+++ b/doc/manual/modules.txt
@@ -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
 filename is ``identifier.nim``).
 
-The algorithm for compiling modules is:
-
-- 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
+Recursive module dependencies are not allowed. This restriction might be mitigated
+or removed in later versions of the language.
 
 
 Import statement
diff --git a/tests/modules/trecinca.nim b/tests/modules/trecinca.nim
index 14a91ba5c..7a74d7a46 100644
--- a/tests/modules/trecinca.nim
+++ b/tests/modules/trecinca.nim
@@ -1,7 +1,7 @@
 discard """
-  file: "tests/reject/trecincb.nim"
+  file: "trecincb.nim"
   line: 9
-  errormsg: "recursive dependency: 'tests/modules/trecincb.nim'"
+  errormsg: "recursive dependency: 'trecincb.nim'"
 """
 # Test recursive includes
 
diff --git a/tests/modules/trecincb.nim b/tests/modules/trecincb.nim
index 299a242e1..1d3eb5503 100644
--- a/tests/modules/trecincb.nim
+++ b/tests/modules/trecincb.nim
@@ -1,7 +1,7 @@
 discard """
   file: "trecincb.nim"
   line: 9
-  errormsg: "recursive dependency: 'tests/modules/trecincb.nim'"
+  errormsg: "recursive dependency: 'trecincb.nim'"
 """
 # Test recursive includes
 
diff --git a/tests/modules/trecmod.nim b/tests/modules/trecmod.nim
index d567e293b..c670bec55 100644
--- a/tests/modules/trecmod.nim
+++ b/tests/modules/trecmod.nim
@@ -1,2 +1,7 @@
+discard """
+  file: "mrecmod.nim"
+  line: 1
+  errormsg: "recursive module dependency detected"
+"""
 # recursive module
 import mrecmod
diff --git a/tests/modules/trecmod2.nim b/tests/modules/trecmod2.nim
index 85fe2215f..aa88f5e91 100644
--- a/tests/modules/trecmod2.nim
+++ b/tests/modules/trecmod2.nim
@@ -1,3 +1,8 @@
+discard """
+  file: "mrecmod2.nim"
+  line: 2
+  errormsg: "recursive module dependency detected"
+"""
 type
   T1* = int  # Module A exports the type ``T1``
 
diff --git a/tests/modules/tselfimport.nim b/tests/modules/tselfimport.nim
index ddb3a5b09..b9109deae 100644
--- a/tests/modules/tselfimport.nim
+++ b/tests/modules/tselfimport.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tselfimport.nim"
   line: 7
-  errormsg: "A module cannot import itself"
+  errormsg: "recursive module dependency detected"
 """
 import strutils as su # guard against regression
 import tselfimport #ERROR
diff --git a/tests/system/tdeepcopy.nim b/tests/system/tdeepcopy.nim
index f7a6e87fa..f7a6e87fa 100755..100644
--- a/tests/system/tdeepcopy.nim
+++ b/tests/system/tdeepcopy.nim
diff --git a/tools/nimsuggest/nimsuggest.nim b/tools/nimsuggest/nimsuggest.nim
index 822ef7224..b5e7b282f 100644
--- a/tools/nimsuggest/nimsuggest.nim
+++ b/tools/nimsuggest/nimsuggest.nim
@@ -431,10 +431,10 @@ proc handleCmdLine(cache: IdentCache) =
       except OSError:
         gProjectFull = gProjectName
       var p = splitFile(gProjectFull)
-      gProjectPath = p.dir
+      gProjectPath = canonicalizePath p.dir
       gProjectName = p.name
     else:
-      gProjectPath = getCurrentDir()
+      gProjectPath = canonicalizePath getCurrentDir()
 
     # Find Nim's prefix dir.
     let binaryPath = findExe("nim")
diff --git a/web/news/e029_version_0_16_0.rst b/web/news/e029_version_0_16_0.rst
index 94c9757a7..42fdfc0e4 100644
--- a/web/news/e029_version_0_16_0.rst
+++ b/web/news/e029_version_0_16_0.rst
@@ -29,6 +29,8 @@ Changes affecting backwards compatibility
 - ``TimeInfo.tzname`` has been removed from ``times`` module because it was
   broken. Because of this, the option ``"ZZZ"`` will no longer work in format
   strings for formatting and parsing.
+- Recursive module dependencies are now completely disallowed.
+
 
 Library Additions
 -----------------