mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-06 07:19:09 +00:00
IC: bugfixes
This commit is contained in:
@@ -149,6 +149,17 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
# as properties are accessed and trigger lazy loading.
|
||||
backendEnsureMutable(t)
|
||||
|
||||
# Bare type-class keywords used as a typedesc without arguments (e.g. `array`,
|
||||
# `range`, `distinct` passed to `signatureHash`) have no children, so the
|
||||
# structural branches below would index a non-existent `elementType`. Hash them
|
||||
# by kind (+ sym for an extra, stable distinction) — enough for a stable,
|
||||
# distinct identity. (`seq`/`openArray`/`tuple` already fall through the empty
|
||||
# `else` loop unharmed; this covers the branches that index `elementType`.)
|
||||
if t.kind in {tyArray, tyRange, tyDistinct} and not t.hasElementType:
|
||||
c &= char(t.kind)
|
||||
if t.sym != nil: c.hashSym(t.sym)
|
||||
return
|
||||
|
||||
case t.kind
|
||||
of tyGenericInvocation:
|
||||
for a in t.kids:
|
||||
|
||||
@@ -1955,7 +1955,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
if regs[rb].node.kind != nkSym:
|
||||
stackTrace(c, tos, pc, "node is not a symbol")
|
||||
else:
|
||||
regs[ra].node.strVal = $sigHash(regs[rb].node.sym, c.config)
|
||||
let shSym = regs[rb].node.sym
|
||||
# When `signatureHash` is applied to a type (e.g. a `T: typedesc`/generic
|
||||
# param), hash the *type* it denotes, not the parameter symbol. Hashing the
|
||||
# symbol routes through `hashNonProc`, which mixes in `s.disamb` — a
|
||||
# per-module instantiation counter. Under incremental compilation the
|
||||
# registering module and a consuming module instantiate the surrounding
|
||||
# generic separately, get different `disamb`s, and produce different
|
||||
# hashes for the same type (nim-serialization's auto-serialization lookup
|
||||
# missed because of this). Hashing the underlying type via `hashType` is
|
||||
# type-identity based and stable across the NIF boundary.
|
||||
let shTyp = shSym.typ
|
||||
if shTyp != nil and shTyp.kind == tyTypeDesc and shTyp.hasElementType:
|
||||
regs[ra].node.strVal = $hashType(shTyp.elementType, c.config)
|
||||
else:
|
||||
regs[ra].node.strVal = $sigHash(shSym, c.config)
|
||||
of opcSlurp:
|
||||
decodeB(rkNode)
|
||||
createStr regs[ra]
|
||||
|
||||
2
koch.nim
2
koch.nim
@@ -617,7 +617,7 @@ proc runIcTestFile(inp: string) =
|
||||
# on a sibling helper (`timp` -> `myimp`, `tcompiletimeglobal` -> `mctglobal`),
|
||||
# which exercises the NIF import/load path the single-file tests do not.
|
||||
const icSuite = ["thallo", "tconverter", "timp", "tmiscs", "tparseutils",
|
||||
"tcompiletimeglobal"]
|
||||
"tcompiletimeglobal", "tsighashstable"]
|
||||
|
||||
proc icTest(args: string) =
|
||||
temp("")
|
||||
|
||||
48
tests/ic/msighashstable.nim
Normal file
48
tests/ic/msighashstable.nim
Normal file
@@ -0,0 +1,48 @@
|
||||
# Helper module for tsighashstable.nim (not a test itself; no `discard`).
|
||||
#
|
||||
# Models nim-serialization's auto-serialization registry: a flavor records which
|
||||
# types it auto-serializes in a `std/macrocache` keyed by `signatureHash(T)`,
|
||||
# computed through a generic `{.compileTime.}` func. The registration happens
|
||||
# here (at this module's compile time); the lookup happens in the importer.
|
||||
#
|
||||
# Under `nim ic` the two modules are compiled separately, so the generic
|
||||
# `getSig[T]` is instantiated independently on each side. `signatureHash` must
|
||||
# therefore hash the *type* `T` denotes, not the generic parameter symbol — the
|
||||
# latter mixes in a per-module `disamb` counter and diverges across the NIF
|
||||
# boundary, making the lookup miss.
|
||||
|
||||
import std/[macrocache, macros, typetraits]
|
||||
|
||||
type DefaultFlavor* = object
|
||||
|
||||
macro calcSig*(T: typed): untyped =
|
||||
doAssert(T.typeKind == ntyTypeDesc)
|
||||
result = newLit(signatureHash(T))
|
||||
|
||||
func getSig*(F: type DefaultFlavor, T: distinct type): string {.compileTime.} =
|
||||
calcSig(T)
|
||||
|
||||
func getTable*(F: type DefaultFlavor): CacheTable {.compileTime.} =
|
||||
CacheTable("nsrzStableTable" & typetraits.name(F))
|
||||
|
||||
func setAuto*(F: type DefaultFlavor, T: distinct type) {.compileTime.} =
|
||||
let sig = F.getSig(T)
|
||||
let table = F.getTable()
|
||||
if not table.hasKey(sig):
|
||||
table[sig] = newLit(1)
|
||||
|
||||
func getAuto*(F: type DefaultFlavor, T: distinct type): bool {.compileTime.} =
|
||||
let sig = F.getSig(T)
|
||||
let table = F.getTable()
|
||||
table.hasKey(sig)
|
||||
|
||||
template autoCheck*(F: distinct type, T: distinct type, body) =
|
||||
when not F.getAuto(T):
|
||||
{.error: "auto serialization not enabled for `" & typetraits.name(T) & "`".}
|
||||
else:
|
||||
body
|
||||
|
||||
static:
|
||||
setAuto(DefaultFlavor, string)
|
||||
setAuto(DefaultFlavor, SomeInteger)
|
||||
setAuto(DefaultFlavor, seq)
|
||||
33
tests/ic/tsighashstable.nim
Normal file
33
tests/ic/tsighashstable.nim
Normal file
@@ -0,0 +1,33 @@
|
||||
discard """
|
||||
output: '''ok string
|
||||
ok int
|
||||
ok seq'''
|
||||
"""
|
||||
|
||||
# Regression test: `signatureHash(T)` must be stable across the NIF boundary so
|
||||
# that a macrocache keyed by it (nim-serialization's auto-serialization registry)
|
||||
# can be populated in one module and queried from another under `nim ic`.
|
||||
#
|
||||
# Before the fix, `signatureHash` hashed the generic *parameter symbol* (whose
|
||||
# `disamb` is a per-module instantiation counter) instead of the type it denotes.
|
||||
# The registering module (msighashstable) and this importer instantiated the
|
||||
# generic `getSig[T]` separately, got different `disamb`s, and the lookups for
|
||||
# `string`/`SomeInteger` missed -> `{.error: auto serialization not enabled.}`.
|
||||
|
||||
import msighashstable
|
||||
|
||||
proc writeStr(F: type DefaultFlavor, v: string) =
|
||||
autoCheck(F, string):
|
||||
echo "ok string"
|
||||
|
||||
proc writeInt[T: SomeInteger](F: type DefaultFlavor, v: T) =
|
||||
autoCheck(F, SomeInteger):
|
||||
echo "ok int"
|
||||
|
||||
proc writeSeq[T](F: type DefaultFlavor, v: seq[T]) =
|
||||
autoCheck(F, seq):
|
||||
echo "ok seq"
|
||||
|
||||
writeStr(DefaultFlavor, "hi")
|
||||
writeInt(DefaultFlavor, 42)
|
||||
writeSeq(DefaultFlavor, @[1, 2, 3])
|
||||
Reference in New Issue
Block a user