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:
parent
3b963a8150
commit
979148e863
26 changed files with 1241 additions and 178 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue