From 979148e863c4142a00632059f3e2a57f4ab0f960 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 17 Dec 2020 08:01:36 +0100 Subject: [PATCH] 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 --- compiler/ast.nim | 4 +- compiler/ic/bitabs.nim | 119 ++++++ compiler/ic/design.rst | 42 +++ compiler/ic/from_packed_ast.nim | 12 + compiler/ic/packed_ast.nim | 461 ++++++++++++++++++++++++ compiler/ic/to_packed_ast.nim | 90 +++++ compiler/importer.nim | 167 +++++++-- compiler/lookups.nim | 301 +++++++++++++--- compiler/main.nim | 6 +- compiler/modulegraphs.nim | 35 +- compiler/modules.nim | 10 +- compiler/plugins/locals.nim | 3 +- compiler/pragmas.nim | 4 +- compiler/rod.nim | 2 +- compiler/sem.nim | 8 +- compiler/semdata.nim | 24 +- compiler/semexprs.nim | 31 +- compiler/semgnrc.nim | 12 +- compiler/semstmts.nim | 10 +- compiler/semtypes.nim | 8 +- compiler/suggest.nim | 50 +-- lib/system.nim | 2 +- testament/important_packages.nim | 4 +- tests/modules/mforwarded_pure_enum.nim | 3 + tests/modules/mforwarded_pure_enum2.nim | 4 + tests/modules/tfowarded_pure_enum.nim | 7 + 26 files changed, 1241 insertions(+), 178 deletions(-) create mode 100644 compiler/ic/bitabs.nim create mode 100644 compiler/ic/design.rst create mode 100644 compiler/ic/from_packed_ast.nim create mode 100644 compiler/ic/packed_ast.nim create mode 100644 compiler/ic/to_packed_ast.nim create mode 100644 tests/modules/mforwarded_pure_enum.nim create mode 100644 tests/modules/mforwarded_pure_enum2.nim create mode 100644 tests/modules/tfowarded_pure_enum.nim 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.. 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.. 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..= 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.. 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.. 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