mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
recursive modules are only detected to improve error messages
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -110,6 +110,7 @@ type
|
||||
cache*: IdentCache
|
||||
graph*: ModuleGraph
|
||||
signatures*: TStrTable
|
||||
recursiveDep*: string
|
||||
|
||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||
result.genericSym = s
|
||||
|
||||
@@ -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}:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,7 @@ discard """
|
||||
file: "mrecmod.nim"
|
||||
line: 1
|
||||
errormsg: "recursive module dependency detected"
|
||||
disabled: true
|
||||
"""
|
||||
# recursive module
|
||||
import mrecmod
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
-----------------
|
||||
|
||||
Reference in New Issue
Block a user