refactorings to prepare the compiler for IC (#15935)

* added ic specific Nim code; WIP
* make the symbol import mechanism lazy; WIP
* ensure that modules can be imported multiple times
* ambiguity checking
* handle converters and TR macros properly
* make 'enum' test category green again
* special logic for semi-pure enums
* makes nimsuggest tests green again
* fixes nimdata
* makes nimpy green again
* makes more important packages work
This commit is contained in:
Andreas Rumpf 2020-12-17 08:01:36 +01:00 • committed by GitHub
commit 979148e863
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1241 additions and 178 deletions

View file

@ -74,12 +74,17 @@ proc closeScope*(c: PContext) =
ensureNoMissingOrUnusedSymbols(c, c.currentScope)
rawCloseScope(c)
iterator walkScopes*(scope: PScope): PScope =
iterator allScopes(scope: PScope): PScope =
var current = scope
while current != nil:
yield current
current = current.parent
iterator localScopesFrom*(c: PContext; scope: PScope): PScope =
for s in allScopes(scope):
if s == c.topLevelScope: break
yield s
proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
if s == nil or s.kind != skAlias:
result = s
@ -91,7 +96,8 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
s.name.s & " is deprecated")
proc isShadowScope*(s: PScope): bool {.inline.} = s.parent != nil and s.parent.depthLevel == s.depthLevel
proc isShadowScope*(s: PScope): bool {.inline.} =
s.parent != nil and s.parent.depthLevel == s.depthLevel
proc localSearchInScope*(c: PContext, s: PIdent): PSym =
var scope = c.currentScope
@ -101,15 +107,92 @@ proc localSearchInScope*(c: PContext, s: PIdent): PSym =
scope = scope.parent
result = strTableGet(scope.symbols, s)
proc searchInScopes*(c: PContext, s: PIdent): PSym =
for scope in walkScopes(c.currentScope):
result = strTableGet(scope.symbols, s)
if result != nil: return
proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; name: PIdent): PSym =
result = initIdentIter(ti, im.m.tab, name)
while result != nil:
let b =
case im.mode
of importAll: true
of importSet: result.id in im.imported
of importExcept: name.id notin im.exceptSet
if b and not containsOrIncl(marked, result.id):
return result
result = nextIdentIter(ti, im.m.tab)
proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule): PSym =
while true:
result = nextIdentIter(ti, im.m.tab)
if result == nil: return nil
case im.mode
of importAll:
if not containsOrIncl(marked, result.id):
return result
of importSet:
if result.id in im.imported and not containsOrIncl(marked, result.id):
return result
of importExcept:
if result.name.id notin im.exceptSet and not containsOrIncl(marked, result.id):
return result
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent): PSym =
var ti: TIdentIter
var candidate = initIdentIter(ti, marked, im, name)
while candidate != nil:
yield candidate
candidate = nextIdentIter(ti, marked, im)
iterator importedItems*(c: PContext; name: PIdent): PSym =
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, name):
yield s
proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
var ti: TIdentIter
result = @[]
var res = initIdentIter(ti, c.pureEnumFields, name)
while res != nil:
result.add res
res = nextIdentIter(ti, c.pureEnumFields)
iterator allSyms*(c: PContext): (PSym, int, bool) =
# really iterate over all symbols in all the scopes. This is expensive
# and only used by suggest.nim.
var isLocal = true
var scopeN = 0
for scope in allScopes(c.currentScope):
if scope == c.topLevelScope: isLocal = false
dec scopeN
for item in scope.symbols:
yield (item, scopeN, isLocal)
dec scopeN
isLocal = false
for im in c.imports.mitems:
for s in im.m.tab.data:
if s != nil:
yield (s, scopeN, isLocal)
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
var marked = initIntSet()
result = nil
for im in c.imports.mitems:
for s in symbols(im, marked, name):
if result == nil:
result = s
else:
if s.kind notin OverloadableSyms or result.kind notin OverloadableSyms:
ambiguous = true
proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
for scope in allScopes(c.currentScope):
result = strTableGet(scope.symbols, s)
if result != nil: return result
result = someSymFromImportTable(c, s, ambiguous)
proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
var i = 0
for scope in walkScopes(c.currentScope):
for scope in allScopes(c.currentScope):
echo "scope ", i
for h in 0..high(scope.symbols.data):
if scope.symbols.data[h] != nil:
@ -117,14 +200,23 @@ proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
if i == limit: break
inc i
proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
for scope in walkScopes(c.currentScope):
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
for scope in allScopes(c.currentScope):
var ti: TIdentIter
var candidate = initIdentIter(ti, scope.symbols, s)
while candidate != nil:
if candidate.kind in filter: return candidate
if candidate.kind in filter:
if result.len == 0:
result.add candidate
candidate = nextIdentIter(ti, scope.symbols)
result = nil
if result.len == 0:
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s):
if s.kind in filter:
result.add s
proc errorSym*(c: PContext, n: PNode): PSym =
## creates an error symbol to avoid cascading errors (for IDE support)
@ -138,9 +230,9 @@ proc errorSym*(c: PContext, n: PNode): PSym =
result = newSym(skError, ident, nextId(c.idgen), getCurrOwner(c), n.info, {})
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors:
# pretend it's from the top level scope to prevent cascading errors:
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
c.importTable.addSym(result)
c.moduleScope.addSym(result)
type
TOverloadIterMode* = enum
@ -151,8 +243,9 @@ type
m*: PSym
mode*: TOverloadIterMode
symChoiceIndex*: int
scope*: PScope
inSymChoice: IntSet
currentScope: PScope
importIdx: int
marked: IntSet
proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
case s.kind
@ -276,15 +369,23 @@ else:
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
var err = "ambiguous identifier: '" & s.name.s & "'"
var ti: TIdentIter
var candidate = initIdentIter(ti, c.importTable.symbols, s.name)
var i = 0
while candidate != nil:
for candidate in importedItems(c, s.name):
if i == 0: err.add " -- use one of the following:\n"
else: err.add "\n"
err.add " " & candidate.owner.name.s & "." & candidate.name.s
err.add ": " & typeToString(candidate.typ)
inc i
localError(c.config, info, errGenerated, err)
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
var i = 0
for candidate in candidates:
if i == 0: err.add " -- use one of the following:\n"
else: err.add "\n"
err.add " " & candidate.owner.name.s & "." & candidate.name.s
err.add ": " & typeToString(candidate.typ)
candidate = nextIdentIter(ti, c.importTable.symbols)
inc i
localError(c.config, info, errGenerated, err)
@ -299,9 +400,10 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil.
var amb = false
case n.kind
of nkIdent:
result = searchInScopes(c, n.ident).skipAlias(n, c.config)
result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config)
if result == nil:
fixSpelling(n, n.ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, n.ident.s)
@ -310,7 +412,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
result = n.sym
of nkAccQuoted:
var ident = considerQuotedIdent(c, n)
result = searchInScopes(c, ident).skipAlias(n, c.config)
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
if result == nil:
fixSpelling(n, ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, ident.s)
@ -318,7 +420,8 @@ proc lookUp*(c: PContext, n: PNode): PSym =
else:
internalError(c.config, n.info, "lookUp")
return
if contains(c.ambiguousSymbols, result.id):
if amb:
#contains(c.ambiguousSymbols, result.id):
errorUseQualifier(c, n.info, result)
when false:
if result.kind == skStub: loadStub(result)
@ -328,29 +431,40 @@ type
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
case n.kind
of nkIdent, nkAccQuoted:
var amb = false
var ident = considerQuotedIdent(c, n)
if checkModule in flags:
result = searchInScopes(c, ident).skipAlias(n, c.config)
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
else:
result = searchInScopes(c, ident, allExceptModule).skipAlias(n, c.config)
if result == nil and checkPureEnumFields in flags:
result = strTableGet(c.pureEnumFields, ident)
let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config)
if candidates.len > 0:
result = candidates[0]
amb = candidates.len > 1
if amb and checkAmbiguity in flags:
errorUseQualifier(c, n.info, candidates)
if result == nil:
let candidates = allPureEnumFields(c, ident)
if candidates.len > 0:
result = candidates[0]
amb = candidates.len > 1
if amb and checkAmbiguity in flags:
errorUseQualifier(c, n.info, candidates)
if result == nil and checkUndeclared in flags:
fixSpelling(n, ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, ident.s)
result = errorSym(c, n)
elif checkAmbiguity in flags and result != nil and result.id in c.ambiguousSymbols:
elif checkAmbiguity in flags and result != nil and amb:
errorUseQualifier(c, n.info, result)
c.isAmbiguous = amb
of nkSym:
result = n.sym
if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id):
errorUseQualifier(c, n.info, n.sym)
of nkDotExpr:
result = nil
var m = qualifiedLookUp(c, n[0], (flags*{checkUndeclared})+{checkModule})
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
if m != nil and m.kind == skModule:
var ident: PIdent = nil
if n[1].kind == nkIdent:
@ -379,18 +493,29 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
if result != nil and result.kind == skStub: loadStub(result)
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
o.importIdx = -1
o.marked = initIntSet()
case n.kind
of nkIdent, nkAccQuoted:
var ident = considerQuotedIdent(c, n)
o.scope = c.currentScope
var scope = c.currentScope
o.mode = oimNoQualifier
while true:
result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n, c.config)
result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config)
if result != nil:
o.currentScope = scope
break
else:
o.scope = o.scope.parent
if o.scope == nil: break
scope = scope.parent
if scope == nil:
for i in 0..c.imports.high:
result = initIdentIter(o.it, o.marked, c.imports[i], ident).skipAlias(n, c.config)
if result != nil:
o.currentScope = nil
o.importIdx = i
return result
return nil
of nkSym:
result = n.sym
o.mode = oimDone
@ -422,31 +547,69 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
o.mode = oimDone
return nil
o.symChoiceIndex = 1
o.inSymChoice = initIntSet()
incl(o.inSymChoice, result.id)
o.marked = initIntSet()
incl(o.marked, result.id)
else: discard
when false:
if result != nil and result.kind == skStub: loadStub(result)
proc lastOverloadScope*(o: TOverloadIter): int =
case o.mode
of oimNoQualifier: result = if o.scope.isNil: -1 else: o.scope.depthLevel
of oimNoQualifier:
result = if o.importIdx >= 0: 0
elif o.currentScope.isNil: -1
else: o.currentScope.depthLevel
of oimSelfModule: result = 1
of oimOtherModule: result = 0
else: result = -1
proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym =
assert o.currentScope == nil
var idx = o.importIdx+1
o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
while idx < c.imports.len:
result = initIdentIter(o.it, o.marked, c.imports[idx], o.it.name).skipAlias(n, c.config)
if result != nil:
# oh, we were wrong, some other module had the symbol, so remember that:
o.importIdx = idx
break
inc idx
proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
assert o.currentScope == nil
while o.importIdx < c.imports.len:
result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config)
#while result != nil and result.id in o.marked:
# result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
if result != nil:
#assert result.id notin o.marked
return result
inc o.importIdx
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode
of oimDone:
result = nil
of oimNoQualifier:
if o.scope != nil:
result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config)
if o.currentScope != nil:
assert o.importIdx < 0
result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config)
while result == nil:
o.scope = o.scope.parent
if o.scope == nil: break
result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config)
# BUGFIX: o.it.name <-> n.ident
o.currentScope = o.currentScope.parent
if o.currentScope != nil:
result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config)
# BUGFIX: o.it.name <-> n.ident
else:
o.importIdx = 0
if c.imports.len > 0:
result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config)
if result == nil:
result = nextOverloadIterImports(o, c, n)
break
elif o.importIdx < c.imports.len:
result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config)
if result == nil:
result = nextOverloadIterImports(o, c, n)
else:
result = nil
of oimSelfModule:
@ -456,26 +619,48 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
of oimSymChoice:
if o.symChoiceIndex < n.len:
result = n[o.symChoiceIndex].sym
incl(o.inSymChoice, result.id)
incl(o.marked, result.id)
inc o.symChoiceIndex
elif n.kind == nkOpenSymChoice:
# try 'local' symbols too for Koenig's lookup:
o.mode = oimSymChoiceLocalLookup
o.scope = c.currentScope
result = firstIdentExcluding(o.it, o.scope.symbols,
n[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
o.currentScope = c.currentScope
result = firstIdentExcluding(o.it, o.currentScope.symbols,
n[0].sym.name, o.marked).skipAlias(n, c.config)
while result == nil:
o.scope = o.scope.parent
if o.scope == nil: break
result = firstIdentExcluding(o.it, o.scope.symbols,
n[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
o.currentScope = o.currentScope.parent
if o.currentScope != nil:
result = firstIdentExcluding(o.it, o.currentScope.symbols,
n[0].sym.name, o.marked).skipAlias(n, c.config)
else:
o.importIdx = 0
result = symChoiceExtension(o, c, n)
break
if result != nil:
incl o.marked, result.id
of oimSymChoiceLocalLookup:
result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config)
while result == nil:
o.scope = o.scope.parent
if o.scope == nil: break
result = firstIdentExcluding(o.it, o.scope.symbols,
n[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
if o.currentScope != nil:
result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config)
while result == nil:
o.currentScope = o.currentScope.parent
if o.currentScope != nil:
result = firstIdentExcluding(o.it, o.currentScope.symbols,
n[0].sym.name, o.marked).skipAlias(n, c.config)
else:
o.importIdx = 0
result = symChoiceExtension(o, c, n)
break
if result != nil:
incl o.marked, result.id
elif o.importIdx < c.imports.len:
result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config)
#assert result.id notin o.marked
#while result != nil and result.id in o.marked:
# result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config)
if result == nil:
inc o.importIdx
result = symChoiceExtension(o, c, n)
when false:
if result != nil and result.kind == skStub: loadStub(result)