diff options
-rw-r--r-- | compiler/importer.nim | 6 | ||||
-rw-r--r-- | compiler/lookups.nim | 17 | ||||
-rw-r--r-- | compiler/msgs.nim | 3 | ||||
-rw-r--r-- | compiler/semdata.nim | 1 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/semgnrc.nim | 4 | ||||
-rw-r--r-- | compiler/semmagic.nim | 2 | ||||
-rw-r--r-- | doc/manual/modules.txt | 32 | ||||
-rw-r--r-- | tests/modules/trecmod.nim | 1 | ||||
-rw-r--r-- | tests/modules/trecmod2.nim | 8 | ||||
-rw-r--r-- | tests/modules/tselfimport.nim | 2 | ||||
-rw-r--r-- | web/news/e029_version_0_16_0.rst | 2 |
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 ----------------- |