summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/importer.nim6
-rw-r--r--compiler/lookups.nim17
-rw-r--r--compiler/msgs.nim3
-rw-r--r--compiler/semdata.nim1
-rw-r--r--compiler/semexprs.nim2
-rw-r--r--compiler/semgnrc.nim4
-rw-r--r--compiler/semmagic.nim2
-rw-r--r--doc/manual/modules.txt32
-rw-r--r--tests/modules/trecmod.nim1
-rw-r--r--tests/modules/trecmod2.nim8
-rw-r--r--tests/modules/tselfimport.nim2
-rw-r--r--web/news/e029_version_0_16_0.rst2
12 files changed, 57 insertions, 23 deletions
diff --git a/compiler/importer.nim b/compiler/importer.nim
index b7e574c62..feebf97c4 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -100,7 +100,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
   let ident = lookups.considerQuotedIdent(n)
   let s = strTableGet(fromMod.tab, ident)
   if s == nil:
-    localError(n.info, errUndeclaredIdentifier, ident.s)
+    errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
     if s.kind == skStub: loadStub(s)
     if s.kind notin ExportableSymKinds:
@@ -172,13 +172,13 @@ proc myImportModule(c: PContext, n: PNode): PSym =
         if i > recursion: 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)
+      c.recursiveDep = 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)
-    when false:
+    when true:
       if result.info.fileIndex == c.module.info.fileIndex and
           result.info.fileIndex == n.info.fileIndex:
         localError(n.info, errGenerated, "A module cannot import itself")
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index df19a6afb..fe159011c 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -242,6 +242,15 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
     inc i
   localError(info, errGenerated, err)
 
+proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
+  var err = "undeclared identifier: '" & name & "'"
+  if c.recursiveDep.len > 0:
+    err.add "\nThis might be caused by a recursive module dependency: "
+    err.add c.recursiveDep
+    # prevent excessive errors for 'nim check'
+    c.recursiveDep = nil
+  localError(info, errGenerated, err)
+
 proc lookUp*(c: PContext, n: PNode): PSym =
   # Looks up a symbol. Generates an error in case of nil.
   case n.kind
@@ -249,7 +258,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
     result = searchInScopes(c, n.ident).skipAlias(n)
     if result == nil:
       fixSpelling(n, n.ident, searchInScopes)
-      localError(n.info, errUndeclaredIdentifier, n.ident.s)
+      errorUndeclaredIdentifier(c, n.info, n.ident.s)
       result = errorSym(c, n)
   of nkSym:
     result = n.sym
@@ -258,7 +267,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
     result = searchInScopes(c, ident).skipAlias(n)
     if result == nil:
       fixSpelling(n, ident, searchInScopes)
-      localError(n.info, errUndeclaredIdentifier, ident.s)
+      errorUndeclaredIdentifier(c, n.info, ident.s)
       result = errorSym(c, n)
   else:
     internalError(n.info, "lookUp")
@@ -282,7 +291,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
       result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
     if result == nil and checkUndeclared in flags:
       fixSpelling(n, ident, searchInScopes)
-      localError(n.info, errUndeclaredIdentifier, ident.s)
+      errorUndeclaredIdentifier(c, n.info, ident.s)
       result = errorSym(c, n)
     elif checkAmbiguity in flags and result != nil and
         contains(c.ambiguousSymbols, result.id):
@@ -307,7 +316,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
           result = strTableGet(m.tab, ident).skipAlias(n)
         if result == nil and checkUndeclared in flags:
           fixSpelling(n.sons[1], ident, searchInScopes)
-          localError(n.sons[1].info, errUndeclaredIdentifier, ident.s)
+          errorUndeclaredIdentifier(c, n.sons[1].info, ident.s)
           result = errorSym(c, n.sons[1])
       elif n.sons[1].kind == nkSym:
         result = n.sons[1].sym
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 0f39eb4d3..94b0bee00 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -35,7 +35,7 @@ type
     errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
     errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
     errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
-    errExprExpected, errUndeclaredIdentifier, errUndeclaredField,
+    errExprExpected, errUndeclaredField,
     errUndeclaredRoutine, errUseQualifier,
     errTypeExpected,
     errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
@@ -197,7 +197,6 @@ const
     errInvalidMultipleAsgn: "multiple assignment is not allowed",
     errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
     errExprExpected: "expression expected, but found \'$1\'",
-    errUndeclaredIdentifier: "undeclared identifier: \'$1\'",
     errUndeclaredField: "undeclared field: \'$1\'",
     errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'",
     errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier",
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 5b84b7cdf..2fec8c757 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -110,6 +110,7 @@ type
     cache*: IdentCache
     graph*: ModuleGraph
     signatures*: TStrTable
+    recursiveDep*: string
 
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
   result.genericSym = s
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index d3431de70..8aaf4f9d8 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1551,7 +1551,7 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
   if isCallExpr(n):
     var expandedSym = qualifiedLookUp(c, n[0], {checkUndeclared})
     if expandedSym == nil:
-      localError(n.info, errUndeclaredIdentifier, n[0].renderTree)
+      errorUndeclaredIdentifier(c, n.info, n[0].renderTree)
       return errorSym(c, n[0])
 
     if expandedSym.kind notin {skMacro, skTemplate}:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index b8451865e..ab0ce7c4c 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -107,7 +107,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
   var s = searchInScopes(c, ident).skipAlias(n)
   if s == nil:
     if ident.id notin ctx.toMixin and withinMixin notin flags:
-      localError(n.info, errUndeclaredIdentifier, ident.s)
+      errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
     if withinBind in flags:
       result = symChoice(c, n, s, scClosed)
@@ -195,7 +195,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if s == nil and withinMixin notin flags and
         fn.kind in {nkIdent, nkAccQuoted} and
         considerQuotedIdent(fn).id notin ctx.toMixin:
-      localError(n.info, errUndeclaredIdentifier, fn.renderTree)
+      errorUndeclaredIdentifier(c, n.info, fn.renderTree)
 
     var first = 0
     var mixinContext = false
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index cd90782d1..e72172c81 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -143,7 +143,7 @@ proc semBindSym(c: PContext, n: PNode): PNode =
     var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal))
     result.add(sc)
   else:
-    localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal)
+    errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
 
diff --git a/doc/manual/modules.txt b/doc/manual/modules.txt
index 8a9f5ff65..9cb6a11af 100644
--- a/doc/manual/modules.txt
+++ b/doc/manual/modules.txt
@@ -9,8 +9,36 @@ 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``).
 
-Recursive module dependencies are not allowed. This restriction might be mitigated
-or removed in later versions of the language.
+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
 
 
 Import statement
diff --git a/tests/modules/trecmod.nim b/tests/modules/trecmod.nim
index c670bec55..5f053bcae 100644
--- a/tests/modules/trecmod.nim
+++ b/tests/modules/trecmod.nim
@@ -2,6 +2,7 @@ discard """
   file: "mrecmod.nim"
   line: 1
   errormsg: "recursive module dependency detected"
+  disabled: true
 """
 # recursive module
 import mrecmod
diff --git a/tests/modules/trecmod2.nim b/tests/modules/trecmod2.nim
index aa88f5e91..03c8cf70d 100644
--- a/tests/modules/trecmod2.nim
+++ b/tests/modules/trecmod2.nim
@@ -1,15 +1,13 @@
 discard """
-  file: "mrecmod2.nim"
-  line: 2
-  errormsg: "recursive module dependency detected"
+  output: "4"
 """
 type
   T1* = int  # Module A exports the type ``T1``
 
 import mrecmod2   # the compiler starts parsing B
-
+# the manual says this should work
 proc main() =
-  var i = p(3) # works because B has been parsed completely here
+  echo p(3) # works because B has been parsed completely here
 
 main()
 
diff --git a/tests/modules/tselfimport.nim b/tests/modules/tselfimport.nim
index b9109deae..ddb3a5b09 100644
--- a/tests/modules/tselfimport.nim
+++ b/tests/modules/tselfimport.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tselfimport.nim"
   line: 7
-  errormsg: "recursive module dependency detected"
+  errormsg: "A module cannot import itself"
 """
 import strutils as su # guard against regression
 import tselfimport #ERROR
diff --git a/web/news/e029_version_0_16_0.rst b/web/news/e029_version_0_16_0.rst
index 42fdfc0e4..94c9757a7 100644
--- a/web/news/e029_version_0_16_0.rst
+++ b/web/news/e029_version_0_16_0.rst
@@ -29,8 +29,6 @@ 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
 -----------------