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

@ -799,6 +799,7 @@ type
libHeader, libDynamic libHeader, libDynamic
TLib* = object # also misused for headers! TLib* = object # also misused for headers!
# keep in sync with PackedLib
kind*: TLibKind kind*: TLibKind
generated*: bool # needed for the backends: generated*: bool # needed for the backends:
isOverriden*: bool isOverriden*: bool
@ -823,7 +824,7 @@ type
PScope* = ref TScope PScope* = ref TScope
PLib* = ref TLib PLib* = ref TLib
TSym* {.acyclic.} = object of TIdObj TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
# proc and type instantiations are cached in the generic symbol # proc and type instantiations are cached in the generic symbol
case kind*: TSymKind case kind*: TSymKind
of skType, skGenericParam: of skType, skGenericParam:
@ -904,6 +905,7 @@ type
# types are identical iff they have the # types are identical iff they have the
# same id; there may be multiple copies of a type # same id; there may be multiple copies of a type
# in memory! # in memory!
# Keep in sync with PackedType
kind*: TTypeKind # kind of type kind*: TTypeKind # kind of type
callConv*: TCallingConvention # for procs callConv*: TCallingConvention # for procs
flags*: TTypeFlags # flags of the type flags*: TTypeFlags # flags of the type

119
compiler/ic/bitabs.nim Normal file
View file

@ -0,0 +1,119 @@
## A BiTable is a table that can be seen as an optimized pair
## of (Table[LitId, Val], Table[Val, LitId]).
import hashes
type
LitId* = distinct uint32
BiTable*[T] = object
vals: seq[T] # indexed by LitId
keys: seq[LitId] # indexed by hash(val)
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash
template maxHash(t): untyped = high(t.keys)
template isFilled(x: LitId): bool = x.uint32 > 0'u32
proc `$`*(x: LitId): string {.borrow.}
proc `<`*(x, y: LitId): bool {.borrow.}
proc `<=`*(x, y: LitId): bool {.borrow.}
proc `==`*(x, y: LitId): bool {.borrow.}
proc hash*(x: LitId): Hash {.borrow.}
proc len*[T](t: BiTable[T]): int = t.vals.len
proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
const
idStart = 256 # Ids do not start with 0 but with this value. The IR needs it.
template idToIdx(x: LitId): int = x.int - idStart
proc enlarge[T](t: var BiTable[T]) =
var n: seq[LitId]
newSeq(n, len(t.keys) * 2)
swap(t.keys, n)
for i in 0..high(n):
let eh = n[i]
if isFilled(eh):
var j = hash(t.vals[idToIdx eh]) and maxHash(t)
while isFilled(t.keys[j]):
j = nextTry(j, maxHash(t))
t.keys[j] = move n[i]
proc getKeyId*[T](t: BiTable[T]; v: T): LitId =
let origH = hash(v)
var h = origH and maxHash(t)
if t.keys.len != 0:
while true:
let litId = t.keys[h]
if not isFilled(litId): break
if t.vals[idToIdx t.keys[h]] == v: return litId
h = nextTry(h, maxHash(t))
return LitId(0)
proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
let origH = hash(v)
var h = origH and maxHash(t)
if t.keys.len != 0:
while true:
let litId = t.keys[h]
if not isFilled(litId): break
if t.vals[idToIdx t.keys[h]] == v: return litId
h = nextTry(h, maxHash(t))
# not found, we need to insert it:
if mustRehash(t.keys.len, t.vals.len):
enlarge(t)
# recompute where to insert:
h = origH and maxHash(t)
while true:
let litId = t.keys[h]
if not isFilled(litId): break
h = nextTry(h, maxHash(t))
else:
setLen(t.keys, 16)
h = origH and maxHash(t)
result = LitId(t.vals.len + idStart)
t.keys[h] = result
t.vals.add v
proc `[]`*[T](t: var BiTable[T]; LitId: LitId): var T {.inline.} =
let idx = idToIdx LitId
assert idx < t.vals.len
result = t.vals[idx]
proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} =
let idx = idToIdx LitId
assert idx < t.vals.len
result = t.vals[idx]
when isMainModule:
var t: BiTable[string]
echo getOrIncl(t, "hello")
echo getOrIncl(t, "hello")
echo getOrIncl(t, "hello3")
echo getOrIncl(t, "hello4")
echo getOrIncl(t, "helloasfasdfdsa")
echo getOrIncl(t, "hello")
echo getKeyId(t, "hello")
echo getKeyId(t, "none")
for i in 0 ..< 100_000:
discard t.getOrIncl($i & "___" & $i)
for i in 0 ..< 100_000:
assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
echo t.vals.len
echo t.vals[0]
echo t.vals[1004]

42
compiler/ic/design.rst Normal file
View file

@ -0,0 +1,42 @@
====================================
Incremental Recompilations
====================================
We split the Nim compiler into a frontend and a backend.
The frontend produces a set of `.rod` files. Every `.nim` module
produces its own `.rod` file.
- The IR must be a faithful representation of the AST in memory.
- The backend can do its own caching but doesn't have to.
- We know by comparing 'nim check compiler/nim' against 'nim c compiler/nim'
that 2/3 of the compiler's runtime is spent in the frontend. Hence we
implement IC for the frontend first and only later for the backend. The
backend will recompile everything until we implement its own caching
mechanisms.
Advantage of the "set of files" vs the previous global database:
- By construction, we either read from the `.rod` file or from the
`.nim` file, there can be no inconsistency. There can also be no
partial updates.
- No dependency to external packages (SQLite). SQLite simply is too
slow and the old way of serialization was too slow too. We use a
format designed for Nim and expect to base further tools on this
file format.
References to external modules must be (moduleId, symId) pairs.
The symbol IDs are module specific. This way no global ID increment
mechanism needs to be implemented that we could get wrong. ModuleIds
are rod-file specific too.
Configuration setup changes
---------------------------
For a MVP these are not detected. Later the configuration will be
stored in every `.rod` file.
Global state
------------
Global persistent state will be kept in a project specific `.rod` file.

View file

@ -0,0 +1,12 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import std / [hashes, tables]
import bitabs
import ".." / [ast, lineinfos, options, pathutils]

461
compiler/ic/packed_ast.nim Normal file
View file

@ -0,0 +1,461 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Packed AST representation, mostly based on a seq of nodes.
## For IC support. Far future: Rewrite the compiler passes to
## use this representation directly in all the transformations,
## it is superior.
import std / [hashes, tables]
import bitabs
import ".." / [ast, lineinfos, options, pathutils]
const
localNamePos* = 0
localExportMarkerPos* = 1
localPragmaPos* = 2
localTypePos* = 3
localValuePos* = 4
typeNamePos* = 0
typeExportMarkerPos* = 1
typeGenericParamsPos* = 2
typePragmaPos* = 3
typeBodyPos* = 4
routineNamePos* = 0
routineExportMarkerPos* = 1
routinePatternPos* = 2
routineGenericParamsPos* = 3
routineParamsPos* = 4
routineResultPos* = 5
routinePragmasPos* = 6
routineBodyPos* = 7
const
nkModuleRef = nkNone # pair of (ModuleId, SymId)
type
SymId* = distinct int32
TypeId* = distinct int32
ModuleId* = distinct int32
NodePos* = distinct int
NodeId* = distinct int32
PackedLineInfo* = object
line*: uint16
col*: int16
file*: LitId
PackedLib* = object
kind*: TLibKind
generated*: bool
isOverriden*: bool
name*: LitId
path*: NodeId
PackedSym* = object
kind*: TSymKind
name*: LitId
typeId*: TypeId
flags*: TSymFlags
magic*: TMagic
info*: PackedLineInfo
ast*: NodePos
owner*: ItemId
guard*: ItemId
bitsize*: int
alignment*: int # for alignment
options*: TOptions
position*: int
offset*: int
externalName*: LitId # instead of TLoc
annex*: PackedLib
when hasFFI:
cname*: LitId
constraint*: NodeId
PackedType* = object
kind*: TTypeKind
nodekind*: TNodeKind
flags*: TTypeFlags
types*: int32
nodes*: int32
methods*: int32
nodeflags*: TNodeFlags
info*: PackedLineInfo
sym*: ItemId
owner*: ItemId
attachedOps*: array[TTypeAttachedOp, ItemId]
size*: BiggestInt
align*: int16
paddingAtEnd*: int16
lockLevel*: TLockLevel # lock level as required for deadlock checking
# not serialized: loc*: TLoc because it is backend-specific
typeInst*: TypeId
nonUniqueId*: ItemId
Node* = object # 20 bytes
kind*: TNodeKind
flags*: TNodeFlags
operand*: int32 # for kind in {nkSym, nkSymDef}: SymId
# for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
# for kind in nkInt32Lit: direct value
# for non-atom kinds: the number of nodes (for easy skipping)
typeId*: TypeId
info*: PackedLineInfo
ModulePhase* = enum
preLookup, lookedUpTopLevelStmts
Module* = object
name*: string
file*: AbsoluteFile
ast*: PackedTree
phase*: ModulePhase
iface*: Table[string, seq[SymId]] # 'seq' because of overloading
Program* = ref object
modules*: seq[Module]
Shared* = ref object # shared between different versions of 'Module'.
# (though there is always exactly one valid
# version of a module)
syms*: seq[PackedSym]
types*: seq[seq[Node]]
strings*: BiTable[string] # we could share these between modules.
integers*: BiTable[BiggestInt]
floats*: BiTable[BiggestFloat]
config*: ConfigRef
#thisModule*: ModuleId
#program*: Program
PackedTree* = object ## usually represents a full Nim module
nodes*: seq[Node]
toPosition*: Table[SymId, NodePos]
sh*: Shared
proc `==`*(a, b: SymId): bool {.borrow.}
proc hash*(a: SymId): Hash {.borrow.}
proc `==`*(a, b: NodePos): bool {.borrow.}
proc `==`*(a, b: TypeId): bool {.borrow.}
proc `==`*(a, b: ModuleId): bool {.borrow.}
proc declareSym*(tree: var PackedTree; kind: TSymKind;
name: LitId; info: PackedLineInfo): SymId =
result = SymId(tree.sh.syms.len)
tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
proc newTreeFrom*(old: PackedTree): PackedTree =
result.nodes = @[]
result.sh = old.sh
proc litIdFromName*(tree: PackedTree; name: string): LitId =
result = tree.sh.strings.getOrIncl(name)
proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
tree.nodes.add Node(kind: kind, operand: int32 getOrIncl(tree.sh.strings, token), info: info)
proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
tree.nodes.add Node(kind: kind, operand: 0, info: info)
proc throwAwayLastNode*(tree: var PackedTree) =
tree.nodes.setLen(tree.nodes.len-1)
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
tree.nodes.add Node(kind: nkIdent, operand: int32(s), info: info)
proc addSym*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info)
proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
tree.nodes.add Node(kind: nkInt32Lit, operand: int32(s), info: info)
proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info)
proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
# and this is why the IR is superior. We can copy subtrees
# via a linear scan.
let pos = n.int
let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
let d = dest.nodes.len
dest.nodes.setLen(d + L)
for i in 0..<L:
dest.nodes[d+i] = tree.nodes[pos+i]
proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
result = SymId(dest.sh.syms.len)
assert int(s) < tree.sh.syms.len
let oldSym = tree.sh.syms[s.int]
dest.sh.syms.add oldSym
type
PatchPos = distinct int
when false:
proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add Node(kind: kind, operand: 0, info: info)
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: TypeId; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add Node(kind: kind, flags: flags, operand: 0, typeId: typeId, info: info)
proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
result = PatchPos dest.nodes.len
dest.nodes.add source.nodes[sourcePos.int]
proc patch*(tree: var PackedTree; pos: PatchPos) =
let pos = pos.int
assert tree.nodes[pos].kind > nkNilLit
let distance = int32(tree.nodes.len - pos)
tree.nodes[pos].operand = distance
proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
proc `[]`*(tree: PackedTree; i: int): lent Node {.inline.} = tree.nodes[i]
proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
if tree.nodes[pos].kind > nkNilLit:
assert tree.nodes[pos].operand > 0
inc pos, tree.nodes[pos].operand
else:
inc pos
iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].operand
inc pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
let patchPos = prepare(dest, tree, n)
for x in sonsReadonly(tree, n): yield x
patch dest, patchPos
iterator isons*(dest: var PackedTree; tree: PackedTree; n: NodePos): (int, NodePos) =
var i = 0
for ch0 in sons(dest, tree, n):
yield (i, ch0)
inc i
iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].operand
inc pos
if pos < last:
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
var count = 0
for child in sonsReadonly(tree, n):
inc count
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].operand
inc pos
while pos < last and count > 2:
yield NodePos pos
dec count
nextChild tree, pos
proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
# finding the parent of a node is rather easy:
var pos = n.int - 1
while pos >= 0 and isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int):
dec pos
assert pos >= 0, "node has no parent"
result = NodePos(pos)
template parent*(n: NodePos): NodePos = parentImpl(tree, n)
proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool =
var count = 0
if tree.nodes[n.int].kind > nkNilLit:
for child in sonsReadonly(tree, n): inc count
result = count == x
proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
inc count
if count >= x: return true
return false
proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} = tree.nodes[n.int].kind
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} = LitId tree.nodes[n.int].operand
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} = tree.nodes[n.int].info
proc span(tree: PackedTree; pos: int): int {.inline.} =
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (NodePos a, NodePos b)
proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (NodePos a, NodePos b, NodePos c)
proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
if count == i: return child
inc count
assert false, "node has no i-th child"
proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} = tree.sh.strings[lit]
template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand
template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand
proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
proc strLit*(tree: PackedTree; n: NodePos): lent string =
assert n.kind == nkStrLit
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
proc strVal*(tree: PackedTree; n: NodePos): string =
assert n.kind == nkStrLit
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
#result = cookedStrLit(raw)
proc filenameVal*(tree: PackedTree; n: NodePos): string =
case n.kind
of nkStrLit:
result = strVal(tree, n)
of nkIdent:
result = tree.sh.strings[n.litId]
of nkSym:
result = tree.sh.strings[tree.sh.syms[int n.symId].name]
else:
result = ""
proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
assert n.kind == nkIdent
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
const
externIntLit* = {nkCharLit,
nkIntLit,
nkInt8Lit,
nkInt16Lit,
nkInt64Lit,
nkUIntLit,
nkUInt8Lit,
nkUInt16Lit,
nkUInt32Lit,
nkUInt64Lit} # nkInt32Lit is missing by design!
externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
directIntLit* = nkInt32Lit
proc toString*(tree: PackedTree; n: NodePos; nesting: int; result: var string) =
let pos = n.int
if result.len > 0 and result[^1] notin {' ', '\n'}:
result.add ' '
result.add $tree[pos].kind
case tree.nodes[pos].kind
of nkNone, nkEmpty, nkNilLit, nkType: discard
of nkIdent, nkStrLit..nkTripleStrLit:
result.add " "
result.add tree.sh.strings[LitId tree.nodes[pos].operand]
of nkSym:
result.add " "
result.add tree.sh.strings[tree.sh.syms[tree.nodes[pos].operand].name]
of directIntLit:
result.add " "
result.addInt tree.nodes[pos].operand
of externSIntLit:
result.add " "
result.addInt tree.sh.integers[LitId tree.nodes[pos].operand]
of externUIntLit:
result.add " "
result.add $cast[uint64](tree.sh.integers[LitId tree.nodes[pos].operand])
else:
result.add "(\n"
for i in 1..(nesting+1)*2: result.add ' '
for child in sonsReadonly(tree, n):
toString(tree, child, nesting + 1, result)
result.add "\n"
for i in 1..nesting*2: result.add ' '
result.add ")"
#for i in 1..nesting*2: result.add ' '
proc toString*(tree: PackedTree; n: NodePos): string =
result = ""
toString(tree, n, 0, result)
proc debug*(tree: PackedTree) =
stdout.write toString(tree, NodePos 0)
proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
if n.kind == nkIdent:
result = n.litId
elif n.kind == nkSym:
result = tree.sh.syms[int n.symId].name
else:
result = LitId(0)
template identId*(n: NodePos): LitId = identIdImpl(tree, n)
template copyInto*(dest, n, body) =
let patchPos = prepare(dest, tree, n)
body
patch dest, patchPos
template copyIntoKind*(dest, kind, info, body) =
let patchPos = prepare(dest, kind, info)
body
patch dest, patchPos
proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
let litId = tree.sh.strings.getKeyId(pragma)
if litId == LitId(0):
return false
assert n.kind == nkPragma
for ch0 in sonsReadonly(tree, n):
if ch0.kind == nkExprColonExpr:
if ch0.firstSon.identId == litId:
return true
elif ch0.identId == litId:
return true
when false:
proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
let patchPos = prepare(dest, nkError, n.info)
dest.add nkStrLit, msg, n.info
copyTree(dest, tree, n)
patch dest, patchPos

View file

@ -0,0 +1,90 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import std / [hashes, tables]
import packed_ast, bitabs
import ".." / [ast, idents, lineinfos, options, pathutils, msgs]
type
Context = object
thisModule: int32
lastFile: FileIndex # remember the last lookup entry.
lastLit: LitId
filenames: Table[FileIndex, LitId]
proc toLitId(x: FileIndex; ir: var PackedTree; c: var Context): LitId =
if x == c.lastFile:
result = c.lastLit
else:
result = c.filenames.getOrDefault(x)
if result == LitId(0):
let p = msgs.toFullPath(ir.sh.config, x)
result = getOrIncl(ir.sh.strings, p)
c.filenames[x] = result
c.lastFile = x
c.lastLit = result
proc toPackedInfo(x: TLineInfo; ir: var PackedTree; c: var Context): PackedLineInfo =
PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, ir, c))
proc toPackedType(t: PType; ir: var PackedTree; c: var Context): TypeId =
result = TypeId(0)
proc toPackedSym(s: PSym; ir: var PackedTree; c: var Context): SymId =
result = SymId(0)
proc toPackedSymNode(n: PNode; ir: var PackedTree; c: var Context) =
assert n.kind == nkSym
let t = toPackedType(n.typ, ir, c)
if n.sym.itemId.module == c.thisModule:
# it is a symbol that belongs to the module we're currently
# packing:
let sid = toPackedSym(n.sym, ir, c)
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(sid),
typeId: t, info: toPackedInfo(n.info, ir, c))
else:
# store it as an external module reference:
# nkModuleRef
discard
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var Context) =
template toP(x: TLineInfo): PackedLineInfo = toPackedInfo(x, ir, c)
case n.kind
of nkNone, nkEmpty, nkNilLit:
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: 0,
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
of nkIdent:
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.ident.s),
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
of nkSym:
toPackedSymNode(n, ir, c)
of directIntLit:
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(n.intVal),
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
of externIntLit:
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.integers, n.intVal),
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
of nkStrLit..nkTripleStrLit:
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.strVal),
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
of nkFloatLit..nkFloat128Lit:
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.floats, n.floatVal),
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
else:
let patchPos = ir.prepare(n.kind, n.flags, toPackedType(n.typ, ir, c), toP n.info)
for i in 0..<n.len:
toPackedNode(n[i], ir, c)
ir.patch patchPos
proc moduleToIr*(n: PNode; ir: var PackedTree; module: PSym) =
var c = Context(thisModule: module.itemId.module)
toPackedNode(n, ir, c)

View file

@ -20,30 +20,67 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet =
let ident = lookups.considerQuotedIdent(c, n[i]) let ident = lookups.considerQuotedIdent(c, n[i])
result.incl(ident.id) result.incl(ident.id)
proc importPureEnumField*(c: PContext; s: PSym) = proc declarePureEnumField*(c: PContext; s: PSym) =
let check = strTableGet(c.importTable.symbols, s.name) # XXX Remove the outer 'if' statement and see what breaks.
if check == nil: var amb = false
let checkB = strTableGet(c.pureEnumFields, s.name) if someSymFromImportTable(c, s.name, amb) == nil:
if checkB == nil: strTableAdd(c.pureEnumFields, s)
strTableAdd(c.pureEnumFields, s) when false:
else: let checkB = strTableGet(c.pureEnumFields, s.name)
if checkB == nil:
strTableAdd(c.pureEnumFields, s)
when false:
# mark as ambiguous: # mark as ambiguous:
incl(c.ambiguousSymbols, checkB.id) incl(c.ambiguousSymbols, checkB.id)
incl(c.ambiguousSymbols, s.id) incl(c.ambiguousSymbols, s.id)
proc rawImportSymbol(c: PContext, s, origin: PSym) = proc importPureEnumField(c: PContext; s: PSym) =
var amb = false
if someSymFromImportTable(c, s.name, amb) == nil:
strTableAdd(c.pureEnumFields, s)
when false:
let checkB = strTableGet(c.pureEnumFields, s.name)
if checkB == nil:
strTableAdd(c.pureEnumFields, s)
when false:
# mark as ambiguous:
incl(c.ambiguousSymbols, checkB.id)
incl(c.ambiguousSymbols, s.id)
proc importPureEnumFields(c: PContext; s: PSym; etyp: PType) =
assert sfPure in s.flags
for j in 0..<etyp.n.len:
var e = etyp.n[j].sym
if e.kind != skEnumField:
internalError(c.config, s.info, "rawImportSymbol")
# BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table
# BUGFIX: but only iff they are the same symbols!
for check in importedItems(c, e.name):
if check.id == e.id:
e = nil
break
if e != nil:
importPureEnumField(c, e)
proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
# This does not handle stubs, because otherwise loading on demand would be # This does not handle stubs, because otherwise loading on demand would be
# pointless in practice. So importing stubs is fine here! # pointless in practice. So importing stubs is fine here!
# check if we have already a symbol of the same name: # check if we have already a symbol of the same name:
var check = strTableGet(c.importTable.symbols, s.name) when false:
if check != nil and check.id != s.id: var check = someSymFromImportTable(c, s.name)
if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms: if check != nil and check.id != s.id:
# s and check need to be qualified: if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
incl(c.ambiguousSymbols, s.id) # s and check need to be qualified:
incl(c.ambiguousSymbols, check.id) incl(c.ambiguousSymbols, s.id)
incl(c.ambiguousSymbols, check.id)
# thanks to 'export' feature, it could be we import the same symbol from # thanks to 'export' feature, it could be we import the same symbol from
# multiple sources, so we need to call 'StrTableAdd' here: # multiple sources, so we need to call 'strTableAdd' here:
strTableAdd(c.importTable.symbols, s) when false:
# now lazy. Speeds up the compiler and is a prerequisite for IC.
strTableAdd(c.importTable.symbols, s)
else:
importSet.incl s.id
if s.kind == skType: if s.kind == skType:
var etyp = s.typ var etyp = s.typ
if etyp.kind in {tyBool, tyEnum}: if etyp.kind in {tyBool, tyEnum}:
@ -54,16 +91,13 @@ proc rawImportSymbol(c: PContext, s, origin: PSym) =
# BUGFIX: because of aliases for enums the symbol may already # BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table # have been put into the symbol table
# BUGFIX: but only iff they are the same symbols! # BUGFIX: but only iff they are the same symbols!
var it: TIdentIter for check in importedItems(c, e.name):
check = initIdentIter(it, c.importTable.symbols, e.name)
while check != nil:
if check.id == e.id: if check.id == e.id:
e = nil e = nil
break break
check = nextIdentIter(it, c.importTable.symbols)
if e != nil: if e != nil:
if sfPure notin s.flags: if sfPure notin s.flags:
rawImportSymbol(c, e, origin) rawImportSymbol(c, e, origin, importSet)
else: else:
importPureEnumField(c, e) importPureEnumField(c, e)
else: else:
@ -72,7 +106,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym) =
if s.owner != origin: if s.owner != origin:
c.exportIndirections.incl((origin.id, s.id)) c.exportIndirections.incl((origin.id, s.id))
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
let ident = lookups.considerQuotedIdent(c, n) let ident = lookups.considerQuotedIdent(c, n)
let s = strTableGet(fromMod.tab, ident) let s = strTableGet(fromMod.tab, ident)
if s == nil: if s == nil:
@ -89,29 +123,79 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
while e != nil: while e != nil:
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3") if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
if s.kind in ExportableSymKinds: if s.kind in ExportableSymKinds:
rawImportSymbol(c, e, fromMod) rawImportSymbol(c, e, fromMod, importSet)
e = nextIdentIter(it, fromMod.tab) e = nextIdentIter(it, fromMod.tab)
else: else:
rawImportSymbol(c, s, fromMod) rawImportSymbol(c, s, fromMod, importSet)
suggestSym(c.config, n.info, s, c.graph.usageSym, false) suggestSym(c.config, n.info, s, c.graph.usageSym, false)
proc addImport(c: PContext; im: sink ImportedModule) =
for i in 0..high(c.imports):
if c.imports[i].m == im.m:
# we have already imported the module: Check which import
# is more "powerful":
case c.imports[i].mode
of importAll: discard "already imported all symbols"
of importSet:
case im.mode
of importAll, importExcept:
# XXX: slightly wrong semantics for 'importExcept'...
# But we should probably change the spec and disallow this case.
c.imports[i] = im
of importSet:
# merge the import sets:
c.imports[i].imported.incl im.imported
of importExcept:
case im.mode
of importAll:
c.imports[i] = im
of importSet:
discard
of importExcept:
var cut = initIntSet()
# only exclude what is consistent between the two sets:
for j in im.exceptSet:
if j in c.imports[i].exceptSet:
cut.incl j
c.imports[i].exceptSet = cut
return
c.imports.add im
template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
for it in c.graph.ifaces[fromMod.position].converters:
if filter:
addConverter(c, it)
for it in c.graph.ifaces[fromMod.position].patterns:
if filter:
addPattern(c, it)
for it in c.graph.ifaces[fromMod.position].pureEnums:
if filter:
importPureEnumFields(c, it, it.typ)
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
var i: TTabIter c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
var s = initTabIter(i, fromMod.tab) addUnnamedIt(c, fromMod, it.id notin exceptSet)
while s != nil:
if s.kind != skModule: when false:
if s.kind != skEnumField: var i: TTabIter
if s.kind notin ExportableSymKinds: var s = initTabIter(i, fromMod.tab)
internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s) while s != nil:
if exceptSet.isNil or s.name.id notin exceptSet: if s.kind != skModule:
rawImportSymbol(c, s, fromMod) if s.kind != skEnumField:
s = nextIter(i, fromMod.tab) if s.kind notin ExportableSymKinds:
internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s)
if exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s, fromMod)
s = nextIter(i, fromMod.tab)
proc importAllSymbols*(c: PContext, fromMod: PSym) = proc importAllSymbols*(c: PContext, fromMod: PSym) =
var exceptSet: IntSet c.addImport ImportedModule(m: fromMod, mode: importAll)
importAllSymbolsExcept(c, fromMod, exceptSet) addUnnamedIt(c, fromMod, true)
when false:
var exceptSet: IntSet
importAllSymbolsExcept(c, fromMod, exceptSet)
proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym) = proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
if n.isNil: return if n.isNil: return
case n.kind case n.kind
of nkExportStmt: of nkExportStmt:
@ -121,12 +205,12 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym) =
if s.kind == skModule: if s.kind == skModule:
importAllSymbolsExcept(c, s, exceptSet) importAllSymbolsExcept(c, s, exceptSet)
elif exceptSet.isNil or s.name.id notin exceptSet: elif exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s, fromMod) rawImportSymbol(c, s, fromMod, importSet)
of nkExportExceptStmt: of nkExportExceptStmt:
localError(c.config, n.info, "'export except' not implemented") localError(c.config, n.info, "'export except' not implemented")
else: else:
for i in 0..n.safeLen-1: for i in 0..n.safeLen-1:
importForwarded(c, n[i], exceptSet, fromMod) importForwarded(c, n[i], exceptSet, fromMod, importSet)
proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
result = realModule result = realModule
@ -224,9 +308,12 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
if m != nil: if m != nil:
n[0] = newSymNode(m) n[0] = newSymNode(m)
addDecl(c, m, n.info) # add symbol to symbol table of module addDecl(c, m, n.info) # add symbol to symbol table of module
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
for i in 1..<n.len: for i in 1..<n.len:
if n[i].kind != nkNilLit: if n[i].kind != nkNilLit:
importSymbol(c, n[i], m) importSymbol(c, n[i], m, im.imported)
c.addImport im
proc evalImportExcept*(c: PContext, n: PNode): PNode = proc evalImportExcept*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info) result = newNodeI(nkImportStmt, n.info)

View file

@ -74,12 +74,17 @@ proc closeScope*(c: PContext) =
ensureNoMissingOrUnusedSymbols(c, c.currentScope) ensureNoMissingOrUnusedSymbols(c, c.currentScope)
rawCloseScope(c) rawCloseScope(c)
iterator walkScopes*(scope: PScope): PScope = iterator allScopes(scope: PScope): PScope =
var current = scope var current = scope
while current != nil: while current != nil:
yield current yield current
current = current.parent 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 = proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
if s == nil or s.kind != skAlias: if s == nil or s.kind != skAlias:
result = s 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; " & message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
s.name.s & " is deprecated") 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 = proc localSearchInScope*(c: PContext, s: PIdent): PSym =
var scope = c.currentScope var scope = c.currentScope
@ -101,15 +107,92 @@ proc localSearchInScope*(c: PContext, s: PIdent): PSym =
scope = scope.parent scope = scope.parent
result = strTableGet(scope.symbols, s) result = strTableGet(scope.symbols, s)
proc searchInScopes*(c: PContext, s: PIdent): PSym = proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; name: PIdent): PSym =
for scope in walkScopes(c.currentScope): result = initIdentIter(ti, im.m.tab, name)
result = strTableGet(scope.symbols, s) while result != nil:
if result != nil: return 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 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.} = proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
var i = 0 var i = 0
for scope in walkScopes(c.currentScope): for scope in allScopes(c.currentScope):
echo "scope ", i echo "scope ", i
for h in 0..high(scope.symbols.data): for h in 0..high(scope.symbols.data):
if scope.symbols.data[h] != nil: if scope.symbols.data[h] != nil:
@ -117,14 +200,23 @@ proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
if i == limit: break if i == limit: break
inc i inc i
proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym = proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
for scope in walkScopes(c.currentScope): result = @[]
for scope in allScopes(c.currentScope):
var ti: TIdentIter var ti: TIdentIter
var candidate = initIdentIter(ti, scope.symbols, s) var candidate = initIdentIter(ti, scope.symbols, s)
while candidate != nil: 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) 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 = proc errorSym*(c: PContext, n: PNode): PSym =
## creates an error symbol to avoid cascading errors (for IDE support) ## 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 = newSym(skError, ident, nextId(c.idgen), getCurrOwner(c), n.info, {})
result.typ = errorType(c) result.typ = errorType(c)
incl(result.flags, sfDiscardable) 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: if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
c.importTable.addSym(result) c.moduleScope.addSym(result)
type type
TOverloadIterMode* = enum TOverloadIterMode* = enum
@ -151,8 +243,9 @@ type
m*: PSym m*: PSym
mode*: TOverloadIterMode mode*: TOverloadIterMode
symChoiceIndex*: int symChoiceIndex*: int
scope*: PScope currentScope: PScope
inSymChoice: IntSet importIdx: int
marked: IntSet
proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string = proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
case s.kind case s.kind
@ -276,15 +369,23 @@ else:
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
var err = "ambiguous identifier: '" & s.name.s & "'" var err = "ambiguous identifier: '" & s.name.s & "'"
var ti: TIdentIter
var candidate = initIdentIter(ti, c.importTable.symbols, s.name)
var i = 0 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" if i == 0: err.add " -- use one of the following:\n"
else: err.add "\n" else: err.add "\n"
err.add " " & candidate.owner.name.s & "." & candidate.name.s err.add " " & candidate.owner.name.s & "." & candidate.name.s
err.add ": " & typeToString(candidate.typ) err.add ": " & typeToString(candidate.typ)
candidate = nextIdentIter(ti, c.importTable.symbols)
inc i inc i
localError(c.config, info, errGenerated, err) 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 = proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil. # Looks up a symbol. Generates an error in case of nil.
var amb = false
case n.kind case n.kind
of nkIdent: of nkIdent:
result = searchInScopes(c, n.ident).skipAlias(n, c.config) result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config)
if result == nil: if result == nil:
fixSpelling(n, n.ident, searchInScopes) fixSpelling(n, n.ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, n.ident.s) errorUndeclaredIdentifier(c, n.info, n.ident.s)
@ -310,7 +412,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
result = n.sym result = n.sym
of nkAccQuoted: of nkAccQuoted:
var ident = considerQuotedIdent(c, n) 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: if result == nil:
fixSpelling(n, ident, searchInScopes) fixSpelling(n, ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
@ -318,7 +420,8 @@ proc lookUp*(c: PContext, n: PNode): PSym =
else: else:
internalError(c.config, n.info, "lookUp") internalError(c.config, n.info, "lookUp")
return return
if contains(c.ambiguousSymbols, result.id): if amb:
#contains(c.ambiguousSymbols, result.id):
errorUseQualifier(c, n.info, result) errorUseQualifier(c, n.info, result)
when false: when false:
if result.kind == skStub: loadStub(result) if result.kind == skStub: loadStub(result)
@ -328,29 +431,40 @@ type
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = 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 case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var amb = false
var ident = considerQuotedIdent(c, n) var ident = considerQuotedIdent(c, n)
if checkModule in flags: if checkModule in flags:
result = searchInScopes(c, ident).skipAlias(n, c.config) result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
else: else:
result = searchInScopes(c, ident, allExceptModule).skipAlias(n, c.config) let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config)
if result == nil and checkPureEnumFields in flags: if candidates.len > 0:
result = strTableGet(c.pureEnumFields, ident) 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: if result == nil and checkUndeclared in flags:
fixSpelling(n, ident, searchInScopes) fixSpelling(n, ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
result = errorSym(c, n) 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) errorUseQualifier(c, n.info, result)
c.isAmbiguous = amb
of nkSym: of nkSym:
result = n.sym result = n.sym
if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id):
errorUseQualifier(c, n.info, n.sym)
of nkDotExpr: of nkDotExpr:
result = nil 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: if m != nil and m.kind == skModule:
var ident: PIdent = nil var ident: PIdent = nil
if n[1].kind == nkIdent: 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) if result != nil and result.kind == skStub: loadStub(result)
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
o.importIdx = -1
o.marked = initIntSet()
case n.kind case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var ident = considerQuotedIdent(c, n) var ident = considerQuotedIdent(c, n)
o.scope = c.currentScope var scope = c.currentScope
o.mode = oimNoQualifier o.mode = oimNoQualifier
while true: 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: if result != nil:
o.currentScope = scope
break break
else: else:
o.scope = o.scope.parent scope = scope.parent
if o.scope == nil: break 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: of nkSym:
result = n.sym result = n.sym
o.mode = oimDone o.mode = oimDone
@ -422,31 +547,69 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
o.mode = oimDone o.mode = oimDone
return nil return nil
o.symChoiceIndex = 1 o.symChoiceIndex = 1
o.inSymChoice = initIntSet() o.marked = initIntSet()
incl(o.inSymChoice, result.id) incl(o.marked, result.id)
else: discard else: discard
when false: when false:
if result != nil and result.kind == skStub: loadStub(result) if result != nil and result.kind == skStub: loadStub(result)
proc lastOverloadScope*(o: TOverloadIter): int = proc lastOverloadScope*(o: TOverloadIter): int =
case o.mode 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 oimSelfModule: result = 1
of oimOtherModule: result = 0 of oimOtherModule: result = 0
else: result = -1 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 = proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode case o.mode
of oimDone: of oimDone:
result = nil result = nil
of oimNoQualifier: of oimNoQualifier:
if o.scope != nil: if o.currentScope != nil:
result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config) assert o.importIdx < 0
result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config)
while result == nil: while result == nil:
o.scope = o.scope.parent o.currentScope = o.currentScope.parent
if o.scope == nil: break if o.currentScope != nil:
result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config) result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config)
# BUGFIX: o.it.name <-> n.ident # 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: else:
result = nil result = nil
of oimSelfModule: of oimSelfModule:
@ -456,26 +619,48 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
of oimSymChoice: of oimSymChoice:
if o.symChoiceIndex < n.len: if o.symChoiceIndex < n.len:
result = n[o.symChoiceIndex].sym result = n[o.symChoiceIndex].sym
incl(o.inSymChoice, result.id) incl(o.marked, result.id)
inc o.symChoiceIndex inc o.symChoiceIndex
elif n.kind == nkOpenSymChoice: elif n.kind == nkOpenSymChoice:
# try 'local' symbols too for Koenig's lookup: # try 'local' symbols too for Koenig's lookup:
o.mode = oimSymChoiceLocalLookup o.mode = oimSymChoiceLocalLookup
o.scope = c.currentScope o.currentScope = c.currentScope
result = firstIdentExcluding(o.it, o.scope.symbols, result = firstIdentExcluding(o.it, o.currentScope.symbols,
n[0].sym.name, o.inSymChoice).skipAlias(n, c.config) n[0].sym.name, o.marked).skipAlias(n, c.config)
while result == nil: while result == nil:
o.scope = o.scope.parent o.currentScope = o.currentScope.parent
if o.scope == nil: break if o.currentScope != nil:
result = firstIdentExcluding(o.it, o.scope.symbols, result = firstIdentExcluding(o.it, o.currentScope.symbols,
n[0].sym.name, o.inSymChoice).skipAlias(n, c.config) 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: of oimSymChoiceLocalLookup:
result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config) if o.currentScope != nil:
while result == nil: result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config)
o.scope = o.scope.parent while result == nil:
if o.scope == nil: break o.currentScope = o.currentScope.parent
result = firstIdentExcluding(o.it, o.scope.symbols, if o.currentScope != nil:
n[0].sym.name, o.inSymChoice).skipAlias(n, c.config) 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: when false:
if result != nil and result.kind == skStub: loadStub(result) if result != nil and result.kind == skStub: loadStub(result)

View file

@ -31,9 +31,9 @@ proc semanticPasses(g: ModuleGraph) =
proc writeDepsFile(g: ModuleGraph) = proc writeDepsFile(g: ModuleGraph) =
let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps") let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
let f = open(fname.string, fmWrite) let f = open(fname.string, fmWrite)
for m in g.modules: for m in g.ifaces:
if m != nil: if m.module != nil:
f.writeLine(toFullPath(g.config, m.position.FileIndex)) f.writeLine(toFullPath(g.config, m.module.position.FileIndex))
for k in g.inclToMod.keys: for k in g.inclToMod.keys:
if g.getModule(k).isNil: # don't repeat includes which are also modules if g.getModule(k).isNil: # don't repeat includes which are also modules
f.writeLine(toFullPath(g.config, k)) f.writeLine(toFullPath(g.config, k))

View file

@ -28,11 +28,20 @@
import ast, intsets, tables, options, lineinfos, hashes, idents, import ast, intsets, tables, options, lineinfos, hashes, idents,
incremental, btrees, md5 incremental, btrees, md5
import ic / packed_ast
type type
SigHash* = distinct MD5Digest SigHash* = distinct MD5Digest
Iface* = object ## data we don't want to store directly in the
## ast.PSym type for s.kind == skModule
module*: PSym ## module this "Iface" belongs to
converters*: seq[PSym]
patterns*: seq[PSym]
pureEnums*: seq[PSym]
ModuleGraph* = ref object ModuleGraph* = ref object
modules*: seq[PSym] ## indexed by int32 fileIdx ifaces*: seq[Iface] ## indexed by int32 fileIdx
packageSyms*: TStrTable packageSyms*: TStrTable
deps*: IntSet # the dependency graph or potentially its transitive closure. deps*: IntSet # the dependency graph or potentially its transitive closure.
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
@ -171,13 +180,21 @@ proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
result.magic = m result.magic = m
result.flags = {sfNeverRaises} result.flags = {sfNeverRaises}
proc registerModule*(g: ModuleGraph; m: PSym) =
assert m != nil
assert m.kind == skModule
if m.position >= g.ifaces.len:
setLen(g.ifaces, m.position + 1)
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[])
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result = ModuleGraph() result = ModuleGraph()
result.idgen = IdGenerator(module: -1'i32, item: 0'i32) result.idgen = IdGenerator(module: -1'i32, item: 0'i32)
initStrTable(result.packageSyms) initStrTable(result.packageSyms)
result.deps = initIntSet() result.deps = initIntSet()
result.importDeps = initTable[FileIndex, seq[FileIndex]]() result.importDeps = initTable[FileIndex, seq[FileIndex]]()
result.modules = @[] result.ifaces = @[]
result.importStack = @[] result.importStack = @[]
result.inclToMod = initTable[FileIndex, FileIndex]() result.inclToMod = initTable[FileIndex, FileIndex]()
result.config = config result.config = config
@ -201,7 +218,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
proc resetAllModules*(g: ModuleGraph) = proc resetAllModules*(g: ModuleGraph) =
initStrTable(g.packageSyms) initStrTable(g.packageSyms)
g.deps = initIntSet() g.deps = initIntSet()
g.modules = @[] g.ifaces = @[]
g.importStack = @[] g.importStack = @[]
g.inclToMod = initTable[FileIndex, FileIndex]() g.inclToMod = initTable[FileIndex, FileIndex]()
g.usageSym = nil g.usageSym = nil
@ -211,8 +228,8 @@ proc resetAllModules*(g: ModuleGraph) =
initStrTable(g.exposed) initStrTable(g.exposed)
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len: if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len:
result = g.modules[fileIdx.int32] result = g.ifaces[fileIdx.int32].module
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
@ -233,7 +250,7 @@ proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
## returns 'fileIdx' if the file belonging to this index is ## returns 'fileIdx' if the file belonging to this index is
## directly used as a module or else the module that first ## directly used as a module or else the module that first
## references this include file. ## references this include file.
if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len and g.modules[fileIdx.int32] != nil: if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len and g.ifaces[fileIdx.int32].module != nil:
result = fileIdx result = fileIdx
else: else:
result = g.inclToMod.getOrDefault(fileIdx) result = g.inclToMod.getOrDefault(fileIdx)
@ -257,11 +274,11 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
# cleared but D still needs to be remembered as 'dirty'. # cleared but D still needs to be remembered as 'dirty'.
if g.invalidTransitiveClosure: if g.invalidTransitiveClosure:
g.invalidTransitiveClosure = false g.invalidTransitiveClosure = false
transitiveClosure(g.deps, g.modules.len) transitiveClosure(g.deps, g.ifaces.len)
# every module that *depends* on this file is also dirty: # every module that *depends* on this file is also dirty:
for i in 0i32..<g.modules.len.int32: for i in 0i32..<g.ifaces.len.int32:
let m = g.modules[i] let m = g.ifaces[i].module
if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)): if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
incl m.flags, sfDirty incl m.flags, sfDirty

View file

@ -59,12 +59,14 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
result.owner = packSym result.owner = packSym
result.position = int fileIdx result.position = int fileIdx
if int(fileIdx) >= graph.modules.len: graph.registerModule(result)
setLen(graph.modules, int(fileIdx) + 1)
graph.modules[result.position] = result
initStrTable(result.tab) initStrTable(result.tab)
strTableAdd(result.tab, result) # a module knows itself when false:
strTableAdd(result.tab, result) # a module knows itself
# This is now implemented via
# c.moduleScope.addSym(module) # a module knows itself
# in sem.nim, around line 527
strTableAdd(packSym.tab, result) strTableAdd(packSym.tab, result)
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =

View file

@ -19,8 +19,7 @@ proc semLocals*(c: PContext, n: PNode): PNode =
tupleType.n = newNodeI(nkRecList, n.info) tupleType.n = newNodeI(nkRecList, n.info)
let owner = getCurrOwner(c) let owner = getCurrOwner(c)
# for now we skip openarrays ... # for now we skip openarrays ...
for scope in walkScopes(c.currentScope): for scope in localScopesFrom(c, c.currentScope):
if scope == c.topLevelScope: break
for it in items(scope.symbols): for it in items(scope.symbols):
if it.kind in skLocalVars and if it.kind in skLocalVars and
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin it.typ.skipTypes({tyGenericInst, tyVar}).kind notin

View file

@ -559,7 +559,9 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
if c < 0: sub = substr(str, b + 1) if c < 0: sub = substr(str, b + 1)
else: sub = substr(str, b + 1, c - 1) else: sub = substr(str, b + 1, c - 1)
if sub != "": if sub != "":
var e = searchInScopes(con, getIdent(con.cache, sub)) var amb = false
var e = searchInScopes(con, getIdent(con.cache, sub), amb)
# XXX what to do here if 'amb' is true?
if e != nil: if e != nil:
when false: when false:
if e.kind == skStub: loadStub(e) if e.kind == skStub: loadStub(e)

View file

@ -22,7 +22,7 @@ when not nimIncremental:
template storeRemaining*(g: ModuleGraph; module: PSym) = discard template storeRemaining*(g: ModuleGraph; module: PSym) = discard
template registerModule*(g: ModuleGraph; module: PSym) = discard #template registerModule*(g: ModuleGraph; module: PSym) = discard
else: else:
include rodimpl include rodimpl

View file

@ -522,8 +522,10 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext
pushProcCon(c, module) pushProcCon(c, module)
pushOwner(c, c.module) pushOwner(c, c.module)
c.importTable = openScope(c)
c.importTable.addSym(module) # a module knows itself c.moduleScope = openScope(c)
c.moduleScope.addSym(module) # a module knows itself
if sfSystemModule in module.flags: if sfSystemModule in module.flags:
graph.systemModule = module graph.systemModule = module
c.topLevelScope = openScope(c) c.topLevelScope = openScope(c)
@ -557,7 +559,7 @@ proc isEmptyTree(n: PNode): bool =
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if c.topStmts == 0 and not isImportSystemStmt(c.graph, n): if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
if sfSystemModule notin c.module.flags and not isEmptyTree(n): if sfSystemModule notin c.module.flags and not isEmptyTree(n):
c.importTable.addSym c.graph.systemModule # import the "System" identifier c.moduleScope.addSym c.graph.systemModule # import the "System" identifier
importAllSymbols(c, c.graph.systemModule) importAllSymbols(c, c.graph.systemModule)
inc c.topStmts inc c.topStmts
else: else:

View file

@ -70,13 +70,25 @@ type
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
ImportMode* = enum
importAll, importSet, importExcept
ImportedModule* = object
m*: PSym
case mode*: ImportMode
of importAll: discard
of importSet:
imported*: IntSet
of importExcept:
exceptSet*: IntSet
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents the module TContext* = object of TPassContext # a context represents the module
# that is currently being compiled # that is currently being compiled
enforceVoidContext*: PType enforceVoidContext*: PType
module*: PSym # the module sym belonging to the context module*: PSym # the module sym belonging to the context
currentScope*: PScope # current scope currentScope*: PScope # current scope
importTable*: PScope # scope for all imported symbols moduleScope*: PScope # scope for modules
imports*: seq[ImportedModule] # scope for all imported symbols
topLevelScope*: PScope # scope for all top-level symbols topLevelScope*: PScope # scope for all top-level symbols
p*: PProcCon # procedure context p*: PProcCon # procedure context
matchedConcept*: ptr TMatchedConcept # the current concept being matched matchedConcept*: ptr TMatchedConcept # the current concept being matched
@ -85,9 +97,6 @@ type
# can access private object fields # can access private object fields
instCounter*: int # to prevent endless instantiations instCounter*: int # to prevent endless instantiations
templInstCounter*: ref int # gives every template instantiation a unique id templInstCounter*: ref int # gives every template instantiation a unique id
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
inGenericContext*: int # > 0 if we are in a generic type inGenericContext*: int # > 0 if we are in a generic type
inStaticContext*: int # > 0 if we are inside a static: block inStaticContext*: int # > 0 if we are inside a static: block
inUnrolledContext*: int # > 0 if we are unrolling a loop inUnrolledContext*: int # > 0 if we are unrolling a loop
@ -134,6 +143,7 @@ type
signatures*: TStrTable signatures*: TStrTable
recursiveDep*: string recursiveDep*: string
suggestionsMade*: bool suggestionsMade*: bool
isAmbiguous*: bool # little hack
features*: set[Feature] features*: set[Feature]
inTypeContext*: int inTypeContext*: int
typesWithOps*: seq[(PType, PType)] #\ typesWithOps*: seq[(PType, PType)] #\
@ -238,7 +248,6 @@ proc popOptionEntry*(c: PContext) =
proc newContext*(graph: ModuleGraph; module: PSym): PContext = proc newContext*(graph: ModuleGraph; module: PSym): PContext =
new(result) new(result)
result.enforceVoidContext = PType(kind: tyTyped) result.enforceVoidContext = PType(kind: tyTyped)
result.ambiguousSymbols = initIntSet()
result.optionStack = @[newOptionEntry(graph.config)] result.optionStack = @[newOptionEntry(graph.config)]
result.libs = @[] result.libs = @[]
result.module = module result.module = module
@ -263,9 +272,14 @@ proc inclSym(sq: var seq[PSym], s: PSym) =
proc addConverter*(c: PContext, conv: PSym) = proc addConverter*(c: PContext, conv: PSym) =
inclSym(c.converters, conv) inclSym(c.converters, conv)
inclSym(c.graph.ifaces[c.module.position].converters, conv)
proc addPureEnum*(c: PContext, e: PSym) =
inclSym(c.graph.ifaces[c.module.position].pureEnums, e)
proc addPattern*(c: PContext, p: PSym) = proc addPattern*(c: PContext, p: PSym) =
inclSym(c.patterns, p) inclSym(c.patterns, p)
inclSym(c.graph.ifaces[c.module.position].patterns, p)
proc newLib*(kind: TLibKind): PLib = proc newLib*(kind: TLibKind): PLib =
new(result) new(result)

View file

@ -495,7 +495,8 @@ proc semOpAux(c: PContext, n: PNode) =
proc overloadedCallOpr(c: PContext, n: PNode): PNode = proc overloadedCallOpr(c: PContext, n: PNode): PNode =
# quick check if there is *any* () operator overloaded: # quick check if there is *any* () operator overloaded:
var par = getIdent(c.cache, "()") var par = getIdent(c.cache, "()")
if searchInScopes(c, par) == nil: var amb = false
if searchInScopes(c, par, amb) == nil:
result = nil result = nil
else: else:
result = newNodeI(nkCall, n.info) result = newNodeI(nkCall, n.info)
@ -653,7 +654,8 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
return isUnresolvedSym(n.sym) return isUnresolvedSym(n.sym)
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
let ident = considerQuotedIdent(c, n) let ident = considerQuotedIdent(c, n)
let sym = searchInScopes(c, ident) var amb = false
let sym = searchInScopes(c, ident, amb)
if sym != nil: if sym != nil:
return isUnresolvedSym(sym) return isUnresolvedSym(sym)
else: else:
@ -1880,10 +1882,12 @@ proc semDefined(c: PContext, n: PNode): PNode =
proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
case n.kind case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var amb = false
let ident = considerQuotedIdent(c, n)
result = if onlyCurrentScope: result = if onlyCurrentScope:
localSearchInScope(c, considerQuotedIdent(c, n)) localSearchInScope(c, ident)
else: else:
searchInScopes(c, considerQuotedIdent(c, n)) searchInScopes(c, ident, amb)
of nkDotExpr: of nkDotExpr:
result = nil result = nil
if onlyCurrentScope: return if onlyCurrentScope: return
@ -2531,6 +2535,11 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
markUsed(c, n.info, exported) markUsed(c, n.info, exported)
proc semExport(c: PContext, n: PNode): PNode = proc semExport(c: PContext, n: PNode): PNode =
proc specialSyms(c: PContext; s: PSym) {.inline.} =
if s.kind == skConverter: addConverter(c, s)
elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags:
addPureEnum(c, s)
result = newNodeI(nkExportStmt, n.info) result = newNodeI(nkExportStmt, n.info)
for i in 0..<n.len: for i in 0..<n.len:
let a = n[i] let a = n[i]
@ -2547,6 +2556,7 @@ proc semExport(c: PContext, n: PNode): PNode =
if it.kind in ExportableSymKinds+{skModule}: if it.kind in ExportableSymKinds+{skModule}:
strTableAdd(c.module.tab, it) strTableAdd(c.module.tab, it)
result.add newSymNode(it, a.info) result.add newSymNode(it, a.info)
specialSyms(c, it)
it = nextIter(ti, s.tab) it = nextIter(ti, s.tab)
markUsed(c, n.info, s) markUsed(c, n.info, s)
else: else:
@ -2558,6 +2568,16 @@ proc semExport(c: PContext, n: PNode): PNode =
result.add(newSymNode(s, a.info)) result.add(newSymNode(s, a.info))
strTableAdd(c.module.tab, s) strTableAdd(c.module.tab, s)
markUsed(c, n.info, s) markUsed(c, n.info, s)
specialSyms(c, s)
if s.kind == skType and sfPure notin s.flags:
var etyp = s.typ
if etyp.kind in {tyBool, tyEnum}:
for j in 0..<etyp.n.len:
var e = etyp.n[j].sym
if e.kind != skEnumField:
internalError(c.config, s.info, "rawImportSymbol")
strTableAdd(c.module.tab, e)
s = nextOverloadIter(o, c, a) s = nextOverloadIter(o, c, a)
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
@ -2722,6 +2742,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
#when defined(nimsuggest): #when defined(nimsuggest):
# if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n)
let mode = if nfDotField in n.flags: {} else: {checkUndeclared} let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
c.isAmbiguous = false
var s = qualifiedLookUp(c, n[0], mode) var s = qualifiedLookUp(c, n[0], mode)
if s != nil: if s != nil:
#if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr: #if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr:
@ -2731,7 +2752,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
of skType: of skType:
# XXX think about this more (``set`` procs) # XXX think about this more (``set`` procs)
let ambig = contains(c.ambiguousSymbols, s.id) let ambig = c.isAmbiguous
if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
result = semConv(c, n) result = semConv(c, n)
elif ambig and n.len == 1: elif ambig and n.len == 1:

View file

@ -115,11 +115,12 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var GenericCtx): PNode = ctx: var GenericCtx): PNode =
result = n result = n
let ident = considerQuotedIdent(c, n) let ident = considerQuotedIdent(c, n)
var s = searchInScopes(c, ident).skipAlias(n, c.config) var amb = false
var s = searchInScopes(c, ident, amb).skipAlias(n, c.config)
if s == nil: if s == nil:
s = strTableGet(c.pureEnumFields, ident) s = strTableGet(c.pureEnumFields, ident)
if s != nil and contains(c.ambiguousSymbols, s.id): #if s != nil and contains(c.ambiguousSymbols, s.id):
s = nil # s = nil
if s == nil: if s == nil:
if ident.id notin ctx.toMixin and withinMixin notin flags: if ident.id notin ctx.toMixin and withinMixin notin flags:
errorUndeclaredIdentifier(c, n.info, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
@ -152,8 +153,9 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
result = n result = n
let n = n[1] let n = n[1]
let ident = considerQuotedIdent(c, n) let ident = considerQuotedIdent(c, n)
var s = searchInScopes(c, ident, routineKinds).skipAlias(n, c.config) var candidates = searchInScopesFilterBy(c, ident, routineKinds) # .skipAlias(n, c.config)
if s != nil: if candidates.len > 0:
let s = candidates[0] # XXX take into account the other candidates!
isMacro = s.kind in {skTemplate, skMacro} isMacro = s.kind in {skTemplate, skMacro}
if withinBind in flags or s.id in ctx.toBind: if withinBind in flags or s.id in ctx.toBind:
result = newDot(result, symChoice(c, n, s, scClosed)) result = newDot(result, symChoice(c, n, s, scClosed))

View file

@ -298,8 +298,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
changeType(c, result, typ, check=false) changeType(c, result, typ, check=false)
proc findShadowedVar(c: PContext, v: PSym): PSym = proc findShadowedVar(c: PContext, v: PSym): PSym =
for scope in walkScopes(c.currentScope.parent): for scope in localScopesFrom(c, c.currentScope.parent):
if scope == c.topLevelScope: break
let shadowed = strTableGet(scope.symbols, v.name) let shadowed = strTableGet(scope.symbols, v.name)
if shadowed != nil and shadowed.kind in skLocalVars: if shadowed != nil and shadowed.kind in skLocalVars:
return shadowed return shadowed
@ -451,7 +450,9 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
n.kind == nkConstSection and w in constPragmas: n.kind == nkConstSection and w in constPragmas:
return nil return nil
let sym = searchInScopes(c, ident) var amb = false
let sym = searchInScopes(c, ident, amb)
# XXX what if amb is true?
if sym == nil or sfCustomPragma in sym.flags: return nil if sym == nil or sfCustomPragma in sym.flags: return nil
# skip if not in scope; skip `template myAttr() {.pragma.}` # skip if not in scope; skip `template myAttr() {.pragma.}`
let lhs = b[0] let lhs = b[0]
@ -1476,7 +1477,8 @@ proc semProcAnnotation(c: PContext, prc: PNode;
if strTableGet(c.userPragmas, ident) != nil: if strTableGet(c.userPragmas, ident) != nil:
continue # User defined pragma continue # User defined pragma
else: else:
let sym = searchInScopes(c, ident) var amb = false
let sym = searchInScopes(c, ident, amb)
if sym != nil and sfCustomPragma in sym.flags: if sym != nil and sfCustomPragma in sym.flags:
continue # User custom pragma continue # User custom pragma

View file

@ -146,10 +146,12 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
onDef(e.info, e) onDef(e.info, e)
if sfGenSym notin e.flags: if sfGenSym notin e.flags:
if not isPure: addDecl(c, e) if not isPure: addDecl(c, e)
else: importPureEnumField(c, e) else: declarePureEnumField(c, e)
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
wrongRedefinition(c, e.info, e.name.s, conflict.info) wrongRedefinition(c, e.info, e.name.s, conflict.info)
inc(counter) inc(counter)
if isPure and sfExported in result.sym.flags:
addPureEnum(c, result.sym)
if tfNotNil in e.typ.flags and not hasNull: if tfNotNil in e.typ.flags and not hasNull:
result.flags.incl tfRequiresInit result.flags.incl tfRequiresInit
@ -1593,7 +1595,9 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
if strTableGet(c.userPragmas, ident) != nil: if strTableGet(c.userPragmas, ident) != nil:
discard "User-defined pragma" discard "User-defined pragma"
else: else:
let sym = searchInScopes(c, ident) var amb = false
let sym = searchInScopes(c, ident, amb)
# XXX: What to do here if amb is true?
if sym != nil and sfCustomPragma in sym.flags: if sym != nil and sfCustomPragma in sym.flags:
discard "Custom user pragma" discard "Custom user pragma"
else: else:

View file

@ -271,17 +271,12 @@ proc getQuality(s: PSym): range[0..100] =
return 100 return 100
template wholeSymTab(cond, section: untyped) {.dirty.} = template wholeSymTab(cond, section: untyped) {.dirty.} =
var isLocal = true for (item, scopeN, isLocal) in allSyms(c):
var scopeN = 0 let it = item
for scope in walkScopes(c.currentScope): var pm: PrefixMatch
if scope == c.topLevelScope: isLocal = false if cond:
dec scopeN outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
for item in scope.symbols: pm, c.inTypeContext > 0, scopeN))
let it = item
var pm: PrefixMatch
if cond:
outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
pm, c.inTypeContext > 0, scopeN))
proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) = proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) =
for i in 0..<list.len: for i in 0..<list.len:
@ -348,17 +343,11 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges
proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
# do not produce too many symbols: # do not produce too many symbols:
var isLocal = true for (it, scopeN, isLocal) in allSyms(c):
var scopeN = 0 var pm: PrefixMatch
for scope in walkScopes(c.currentScope): if filterSym(it, f, pm):
if scope == c.topLevelScope: isLocal = false outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm,
dec scopeN c.inTypeContext > 0, scopeN))
for it in items(scope.symbols):
var pm: PrefixMatch
if filterSym(it, f, pm):
outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm,
c.inTypeContext > 0, scopeN))
#if scope == c.topLevelScope and f.isNil: break
proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) = proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) =
# special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
@ -656,17 +645,12 @@ proc suggestSentinel*(c: PContext) =
inc(c.compilesContextId) inc(c.compilesContextId)
var outputs: Suggestions = @[] var outputs: Suggestions = @[]
# suggest everything: # suggest everything:
var isLocal = true for (it, scopeN, isLocal) in allSyms(c):
var scopeN = 0 var pm: PrefixMatch
for scope in walkScopes(c.currentScope): if filterSymNoOpr(it, nil, pm):
if scope == c.topLevelScope: isLocal = false outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug,
dec scopeN newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0,
for it in items(scope.symbols): PrefixMatch.None, false, scopeN))
var pm: PrefixMatch
if filterSymNoOpr(it, nil, pm):
outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug,
newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0,
PrefixMatch.None, false, scopeN))
dec(c.compilesContextId) dec(c.compilesContextId)
produceOutput(outputs, c.config) produceOutput(outputs, c.config)

View file

@ -627,7 +627,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.}
## the sequence instead of adding them. Example: ## the sequence instead of adding them. Example:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var inputStrings : seq[string] ## var inputStrings: seq[string]
## newSeq(inputStrings, 3) ## newSeq(inputStrings, 3)
## assert len(inputStrings) == 3 ## assert len(inputStrings) == 3
## inputStrings[0] = "The fourth" ## inputStrings[0] = "The fourth"

View file

@ -76,7 +76,9 @@ pkg2 "nigui", "nim c -o:niguii -r src/nigui.nim"
pkg2 "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg2 "NimData", "nim c -o:nimdataa src/nimdata.nim"
pkg2 "nimes", "nim c src/nimes.nim" pkg2 "nimes", "nim c src/nimes.nim"
pkg2 "nimfp", "nim c -o:nfp -r src/fp.nim" pkg2 "nimfp", "nim c -o:nfp -r src/fp.nim"
pkg2 "nimgame2", "nim c nimgame2/nimgame.nim" when false:
pkg2 "nimgame2", "nim c nimgame2/nimgame.nim"
# XXX Doesn't work with deprecated 'randomize', will create a PR.
pkg2 "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg2 "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim"
pkg2 "nimlsp" pkg2 "nimlsp"
pkg2 "nimly", "nim c -r tests/test_readme_example.nim" pkg2 "nimly", "nim c -r tests/test_readme_example.nim"

View file

@ -0,0 +1,3 @@
import mforwarded_pure_enum2
export mforwarded_pure_enum2.PureEnum

View file

@ -0,0 +1,4 @@
type
PureEnum* {.pure.} = enum
x, y, z

View file

@ -0,0 +1,7 @@
discard """
output: '''z'''
"""
import mforwarded_pure_enum as t2
echo z