diff options
-rw-r--r-- | compiler/ast.nim | 4 | ||||
-rw-r--r-- | compiler/ic/bitabs.nim | 119 | ||||
-rw-r--r-- | compiler/ic/design.rst | 42 | ||||
-rw-r--r-- | compiler/ic/from_packed_ast.nim | 12 | ||||
-rw-r--r-- | compiler/ic/packed_ast.nim | 461 | ||||
-rw-r--r-- | compiler/ic/to_packed_ast.nim | 90 | ||||
-rw-r--r-- | compiler/importer.nim | 167 | ||||
-rw-r--r-- | compiler/lookups.nim | 301 | ||||
-rw-r--r-- | compiler/main.nim | 6 | ||||
-rw-r--r-- | compiler/modulegraphs.nim | 35 | ||||
-rw-r--r-- | compiler/modules.nim | 10 | ||||
-rw-r--r-- | compiler/plugins/locals.nim | 3 | ||||
-rw-r--r-- | compiler/pragmas.nim | 4 | ||||
-rw-r--r-- | compiler/rod.nim | 2 | ||||
-rw-r--r-- | compiler/sem.nim | 8 | ||||
-rw-r--r-- | compiler/semdata.nim | 24 | ||||
-rw-r--r-- | compiler/semexprs.nim | 31 | ||||
-rw-r--r-- | compiler/semgnrc.nim | 12 | ||||
-rw-r--r-- | compiler/semstmts.nim | 10 | ||||
-rw-r--r-- | compiler/semtypes.nim | 8 | ||||
-rw-r--r-- | compiler/suggest.nim | 50 | ||||
-rw-r--r-- | lib/system.nim | 2 | ||||
-rw-r--r-- | testament/important_packages.nim | 4 | ||||
-rw-r--r-- | tests/modules/mforwarded_pure_enum.nim | 3 | ||||
-rw-r--r-- | tests/modules/mforwarded_pure_enum2.nim | 4 | ||||
-rw-r--r-- | tests/modules/tfowarded_pure_enum.nim | 7 |
26 files changed, 1241 insertions, 178 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index fbad9e44e..4ca5035ed 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -799,6 +799,7 @@ type libHeader, libDynamic TLib* = object # also misused for headers! + # keep in sync with PackedLib kind*: TLibKind generated*: bool # needed for the backends: isOverriden*: bool @@ -823,7 +824,7 @@ type PScope* = ref TScope 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 case kind*: TSymKind of skType, skGenericParam: @@ -904,6 +905,7 @@ type # types are identical iff they have the # same id; there may be multiple copies of a type # in memory! + # Keep in sync with PackedType kind*: TTypeKind # kind of type callConv*: TCallingConvention # for procs flags*: TTypeFlags # flags of the type diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim new file mode 100644 index 000000000..91221ea49 --- /dev/null +++ b/compiler/ic/bitabs.nim @@ -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] diff --git a/compiler/ic/design.rst b/compiler/ic/design.rst new file mode 100644 index 000000000..1a33f6a27 --- /dev/null +++ b/compiler/ic/design.rst @@ -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. diff --git a/compiler/ic/from_packed_ast.nim b/compiler/ic/from_packed_ast.nim new file mode 100644 index 000000000..cb2ecee79 --- /dev/null +++ b/compiler/ic/from_packed_ast.nim @@ -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] diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim new file mode 100644 index 000000000..ef609e8c8 --- /dev/null +++ b/compiler/ic/packed_ast.nim @@ -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 diff --git a/compiler/ic/to_packed_ast.nim b/compiler/ic/to_packed_ast.nim new file mode 100644 index 000000000..85fec9081 --- /dev/null +++ b/compiler/ic/to_packed_ast.nim @@ -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) diff --git a/compiler/importer.nim b/compiler/importer.nim index 6722eab15..a055e16b7 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -20,30 +20,67 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet = let ident = lookups.considerQuotedIdent(c, n[i]) result.incl(ident.id) -proc importPureEnumField*(c: PContext; s: PSym) = - let check = strTableGet(c.importTable.symbols, s.name) - if check == nil: - let checkB = strTableGet(c.pureEnumFields, s.name) - if checkB == nil: - strTableAdd(c.pureEnumFields, s) - else: +proc declarePureEnumField*(c: PContext; s: PSym) = + # XXX Remove the outer 'if' statement and see what breaks. + 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 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 rawImportSymbol(c: PContext, s, origin: PSym) = +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 # pointless in practice. So importing stubs is fine here! # check if we have already a symbol of the same name: - var check = strTableGet(c.importTable.symbols, s.name) - if check != nil and check.id != s.id: - if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms: - # s and check need to be qualified: - incl(c.ambiguousSymbols, s.id) - incl(c.ambiguousSymbols, check.id) + when false: + var check = someSymFromImportTable(c, s.name) + if check != nil and check.id != s.id: + if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms: + # s and check need to be qualified: + incl(c.ambiguousSymbols, s.id) + incl(c.ambiguousSymbols, check.id) # thanks to 'export' feature, it could be we import the same symbol from - # multiple sources, so we need to call 'StrTableAdd' here: - strTableAdd(c.importTable.symbols, s) + # multiple sources, so we need to call 'strTableAdd' here: + 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: var etyp = s.typ 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 # have been put into the symbol table # BUGFIX: but only iff they are the same symbols! - var it: TIdentIter - check = initIdentIter(it, c.importTable.symbols, e.name) - while check != nil: + for check in importedItems(c, e.name): if check.id == e.id: e = nil break - check = nextIdentIter(it, c.importTable.symbols) if e != nil: if sfPure notin s.flags: - rawImportSymbol(c, e, origin) + rawImportSymbol(c, e, origin, importSet) else: importPureEnumField(c, e) else: @@ -72,7 +106,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym) = if s.owner != origin: 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 s = strTableGet(fromMod.tab, ident) if s == nil: @@ -89,29 +123,79 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = while e != nil: if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3") if s.kind in ExportableSymKinds: - rawImportSymbol(c, e, fromMod) + rawImportSymbol(c, e, fromMod, importSet) e = nextIdentIter(it, fromMod.tab) else: - rawImportSymbol(c, s, fromMod) + rawImportSymbol(c, s, fromMod, importSet) 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) = - var i: TTabIter - var s = initTabIter(i, fromMod.tab) - while s != nil: - if s.kind != skModule: - if s.kind != skEnumField: - 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) + c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet) + addUnnamedIt(c, fromMod, it.id notin exceptSet) + + when false: + var i: TTabIter + var s = initTabIter(i, fromMod.tab) + while s != nil: + if s.kind != skModule: + if s.kind != skEnumField: + 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) = - var exceptSet: IntSet - importAllSymbolsExcept(c, fromMod, exceptSet) + c.addImport ImportedModule(m: fromMod, mode: importAll) + 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 case n.kind of nkExportStmt: @@ -121,12 +205,12 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym) = if s.kind == skModule: importAllSymbolsExcept(c, s, exceptSet) elif exceptSet.isNil or s.name.id notin exceptSet: - rawImportSymbol(c, s, fromMod) + rawImportSymbol(c, s, fromMod, importSet) of nkExportExceptStmt: localError(c.config, n.info, "'export except' not implemented") else: 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 = result = realModule @@ -224,9 +308,12 @@ proc evalFrom*(c: PContext, n: PNode): PNode = if m != nil: n[0] = newSymNode(m) 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: 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 = result = newNodeI(nkImportStmt, n.info) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 0c624425d..fba032c1e 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -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) diff --git a/compiler/main.nim b/compiler/main.nim index 6bdbb9efd..73676ffd5 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -31,9 +31,9 @@ proc semanticPasses(g: ModuleGraph) = proc writeDepsFile(g: ModuleGraph) = let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps") let f = open(fname.string, fmWrite) - for m in g.modules: - if m != nil: - f.writeLine(toFullPath(g.config, m.position.FileIndex)) + for m in g.ifaces: + if m.module != nil: + f.writeLine(toFullPath(g.config, m.module.position.FileIndex)) for k in g.inclToMod.keys: if g.getModule(k).isNil: # don't repeat includes which are also modules f.writeLine(toFullPath(g.config, k)) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 9e7a487cf..abbefa9b6 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -28,11 +28,20 @@ import ast, intsets, tables, options, lineinfos, hashes, idents, incremental, btrees, md5 +import ic / packed_ast + type 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 - modules*: seq[PSym] ## indexed by int32 fileIdx + ifaces*: seq[Iface] ## indexed by int32 fileIdx packageSyms*: TStrTable deps*: IntSet # the dependency graph or potentially its transitive closure. 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.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 = result = ModuleGraph() result.idgen = IdGenerator(module: -1'i32, item: 0'i32) initStrTable(result.packageSyms) result.deps = initIntSet() result.importDeps = initTable[FileIndex, seq[FileIndex]]() - result.modules = @[] + result.ifaces = @[] result.importStack = @[] result.inclToMod = initTable[FileIndex, FileIndex]() result.config = config @@ -201,7 +218,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = proc resetAllModules*(g: ModuleGraph) = initStrTable(g.packageSyms) g.deps = initIntSet() - g.modules = @[] + g.ifaces = @[] g.importStack = @[] g.inclToMod = initTable[FileIndex, FileIndex]() g.usageSym = nil @@ -211,8 +228,8 @@ proc resetAllModules*(g: ModuleGraph) = initStrTable(g.exposed) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = - if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len: - result = g.modules[fileIdx.int32] + if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len: + result = g.ifaces[fileIdx.int32].module 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 ## directly used as a module or else the module that first ## 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 else: 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'. if g.invalidTransitiveClosure: 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: - for i in 0i32..<g.modules.len.int32: - let m = g.modules[i] + for i in 0i32..<g.ifaces.len.int32: + let m = g.ifaces[i].module if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)): incl m.flags, sfDirty diff --git a/compiler/modules.nim b/compiler/modules.nim index 748b74953..132f3788d 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -59,12 +59,14 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil result.owner = packSym result.position = int fileIdx - if int(fileIdx) >= graph.modules.len: - setLen(graph.modules, int(fileIdx) + 1) - graph.modules[result.position] = result + graph.registerModule(result) 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) proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim index efa8b1e94..6b0594197 100644 --- a/compiler/plugins/locals.nim +++ b/compiler/plugins/locals.nim @@ -19,8 +19,7 @@ proc semLocals*(c: PContext, n: PNode): PNode = tupleType.n = newNodeI(nkRecList, n.info) let owner = getCurrOwner(c) # for now we skip openarrays ... - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: break + for scope in localScopesFrom(c, c.currentScope): for it in items(scope.symbols): if it.kind in skLocalVars and it.typ.skipTypes({tyGenericInst, tyVar}).kind notin diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 5ee3dc414..dca8a94e9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -559,7 +559,9 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = if c < 0: sub = substr(str, b + 1) else: sub = substr(str, b + 1, c - 1) 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: when false: if e.kind == skStub: loadStub(e) diff --git a/compiler/rod.nim b/compiler/rod.nim index 13a4a53c9..711f5b2cb 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -22,7 +22,7 @@ when not nimIncremental: template storeRemaining*(g: ModuleGraph; module: PSym) = discard - template registerModule*(g: ModuleGraph; module: PSym) = discard + #template registerModule*(g: ModuleGraph; module: PSym) = discard else: include rodimpl diff --git a/compiler/sem.nim b/compiler/sem.nim index 0fec8b7e3..e95f3799c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -522,8 +522,10 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext pushProcCon(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: graph.systemModule = module c.topLevelScope = openScope(c) @@ -557,7 +559,7 @@ proc isEmptyTree(n: PNode): bool = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = if c.topStmts == 0 and not isImportSystemStmt(c.graph, 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) inc c.topStmts else: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8d1c25e4d..a6660e14c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -70,13 +70,25 @@ type 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 TContext* = object of TPassContext # a context represents the module # that is currently being compiled enforceVoidContext*: PType module*: PSym # the module sym belonging to the context 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 p*: PProcCon # procedure context matchedConcept*: ptr TMatchedConcept # the current concept being matched @@ -85,9 +97,6 @@ type # can access private object fields instCounter*: int # to prevent endless instantiations 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 inStaticContext*: int # > 0 if we are inside a static: block inUnrolledContext*: int # > 0 if we are unrolling a loop @@ -134,6 +143,7 @@ type signatures*: TStrTable recursiveDep*: string suggestionsMade*: bool + isAmbiguous*: bool # little hack features*: set[Feature] inTypeContext*: int typesWithOps*: seq[(PType, PType)] #\ @@ -238,7 +248,6 @@ proc popOptionEntry*(c: PContext) = proc newContext*(graph: ModuleGraph; module: PSym): PContext = new(result) result.enforceVoidContext = PType(kind: tyTyped) - result.ambiguousSymbols = initIntSet() result.optionStack = @[newOptionEntry(graph.config)] result.libs = @[] result.module = module @@ -263,9 +272,14 @@ proc inclSym(sq: var seq[PSym], s: PSym) = proc addConverter*(c: PContext, conv: PSym) = 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) = inclSym(c.patterns, p) + inclSym(c.graph.ifaces[c.module.position].patterns, p) proc newLib*(kind: TLibKind): PLib = new(result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3bbf353b1..bcb48515f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -495,7 +495,8 @@ proc semOpAux(c: PContext, n: PNode) = proc overloadedCallOpr(c: PContext, n: PNode): PNode = # quick check if there is *any* () operator overloaded: var par = getIdent(c.cache, "()") - if searchInScopes(c, par) == nil: + var amb = false + if searchInScopes(c, par, amb) == nil: result = nil else: result = newNodeI(nkCall, n.info) @@ -653,7 +654,8 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool = return isUnresolvedSym(n.sym) of nkIdent, nkAccQuoted: let ident = considerQuotedIdent(c, n) - let sym = searchInScopes(c, ident) + var amb = false + let sym = searchInScopes(c, ident, amb) if sym != nil: return isUnresolvedSym(sym) else: @@ -1880,10 +1882,12 @@ proc semDefined(c: PContext, n: PNode): PNode = proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = case n.kind of nkIdent, nkAccQuoted: + var amb = false + let ident = considerQuotedIdent(c, n) result = if onlyCurrentScope: - localSearchInScope(c, considerQuotedIdent(c, n)) + localSearchInScope(c, ident) else: - searchInScopes(c, considerQuotedIdent(c, n)) + searchInScopes(c, ident, amb) of nkDotExpr: result = nil if onlyCurrentScope: return @@ -2531,6 +2535,11 @@ proc semExportExcept(c: PContext, n: PNode): PNode = markUsed(c, n.info, exported) 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) for i in 0..<n.len: let a = n[i] @@ -2547,6 +2556,7 @@ proc semExport(c: PContext, n: PNode): PNode = if it.kind in ExportableSymKinds+{skModule}: strTableAdd(c.module.tab, it) result.add newSymNode(it, a.info) + specialSyms(c, it) it = nextIter(ti, s.tab) markUsed(c, n.info, s) else: @@ -2558,6 +2568,16 @@ proc semExport(c: PContext, n: PNode): PNode = result.add(newSymNode(s, a.info)) strTableAdd(c.module.tab, 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) 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): # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) let mode = if nfDotField in n.flags: {} else: {checkUndeclared} + c.isAmbiguous = false var s = qualifiedLookUp(c, n[0], mode) if s != nil: #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) of skType: # 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: result = semConv(c, n) elif ambig and n.len == 1: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 5f55772e9..0d80da9a6 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -115,11 +115,12 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = 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: s = strTableGet(c.pureEnumFields, ident) - if s != nil and contains(c.ambiguousSymbols, s.id): - s = nil + #if s != nil and contains(c.ambiguousSymbols, s.id): + # s = nil if s == nil: if ident.id notin ctx.toMixin and withinMixin notin flags: errorUndeclaredIdentifier(c, n.info, ident.s) @@ -152,8 +153,9 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, result = n let n = n[1] let ident = considerQuotedIdent(c, n) - var s = searchInScopes(c, ident, routineKinds).skipAlias(n, c.config) - if s != nil: + var candidates = searchInScopesFilterBy(c, ident, routineKinds) # .skipAlias(n, c.config) + if candidates.len > 0: + let s = candidates[0] # XXX take into account the other candidates! isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags or s.id in ctx.toBind: result = newDot(result, symChoice(c, n, s, scClosed)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d3d8c0fb8..b4026f3d7 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -298,8 +298,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = changeType(c, result, typ, check=false) proc findShadowedVar(c: PContext, v: PSym): PSym = - for scope in walkScopes(c.currentScope.parent): - if scope == c.topLevelScope: break + for scope in localScopesFrom(c, c.currentScope.parent): let shadowed = strTableGet(scope.symbols, v.name) if shadowed != nil and shadowed.kind in skLocalVars: return shadowed @@ -451,7 +450,9 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = n.kind == nkConstSection and w in constPragmas: 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 # skip if not in scope; skip `template myAttr() {.pragma.}` let lhs = b[0] @@ -1476,7 +1477,8 @@ proc semProcAnnotation(c: PContext, prc: PNode; if strTableGet(c.userPragmas, ident) != nil: continue # User defined pragma else: - let sym = searchInScopes(c, ident) + var amb = false + let sym = searchInScopes(c, ident, amb) if sym != nil and sfCustomPragma in sym.flags: continue # User custom pragma diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a9d00d2ee..827087c3d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -146,10 +146,12 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = onDef(e.info, e) if sfGenSym notin e.flags: if not isPure: addDecl(c, e) - else: importPureEnumField(c, e) + else: declarePureEnumField(c, e) if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) inc(counter) + if isPure and sfExported in result.sym.flags: + addPureEnum(c, result.sym) if tfNotNil in e.typ.flags and not hasNull: result.flags.incl tfRequiresInit @@ -1593,7 +1595,9 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode = if strTableGet(c.userPragmas, ident) != nil: discard "User-defined pragma" 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: discard "Custom user pragma" else: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index be337af12..186b23cd9 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -271,17 +271,12 @@ proc getQuality(s: PSym): range[0..100] = return 100 template wholeSymTab(cond, section: untyped) {.dirty.} = - var isLocal = true - var scopeN = 0 - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: isLocal = false - dec scopeN - for item in scope.symbols: - 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)) + for (item, scopeN, isLocal) in allSyms(c): + 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) = 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) = # do not produce too many symbols: - var isLocal = true - var scopeN = 0 - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: isLocal = false - dec 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 + for (it, scopeN, isLocal) in allSyms(c): + 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)) proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) = # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but @@ -656,17 +645,12 @@ proc suggestSentinel*(c: PContext) = inc(c.compilesContextId) var outputs: Suggestions = @[] # suggest everything: - var isLocal = true - var scopeN = 0 - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: isLocal = false - dec scopeN - for it in items(scope.symbols): - 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)) + for (it, scopeN, isLocal) in allSyms(c): + 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) produceOutput(outputs, c.config) diff --git a/lib/system.nim b/lib/system.nim index 57b46a937..afd8e467d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -627,7 +627,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## the sequence instead of adding them. Example: ## ## .. code-block:: Nim - ## var inputStrings : seq[string] + ## var inputStrings: seq[string] ## newSeq(inputStrings, 3) ## assert len(inputStrings) == 3 ## inputStrings[0] = "The fourth" diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 2da10e749..aae560939 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -76,7 +76,9 @@ pkg2 "nigui", "nim c -o:niguii -r src/nigui.nim" pkg2 "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg2 "nimes", "nim c src/nimes.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 "nimlsp" pkg2 "nimly", "nim c -r tests/test_readme_example.nim" diff --git a/tests/modules/mforwarded_pure_enum.nim b/tests/modules/mforwarded_pure_enum.nim new file mode 100644 index 000000000..3f03390a5 --- /dev/null +++ b/tests/modules/mforwarded_pure_enum.nim @@ -0,0 +1,3 @@ + +import mforwarded_pure_enum2 +export mforwarded_pure_enum2.PureEnum diff --git a/tests/modules/mforwarded_pure_enum2.nim b/tests/modules/mforwarded_pure_enum2.nim new file mode 100644 index 000000000..e5d5d2a71 --- /dev/null +++ b/tests/modules/mforwarded_pure_enum2.nim @@ -0,0 +1,4 @@ + +type + PureEnum* {.pure.} = enum + x, y, z diff --git a/tests/modules/tfowarded_pure_enum.nim b/tests/modules/tfowarded_pure_enum.nim new file mode 100644 index 000000000..1d2c4f342 --- /dev/null +++ b/tests/modules/tfowarded_pure_enum.nim @@ -0,0 +1,7 @@ +discard """ + output: '''z''' +""" + +import mforwarded_pure_enum as t2 + +echo z |