diff --git a/compiler/typekeys.nim b/compiler/typekeys.nim index 54092eb055..6c7ca51d7b 100644 --- a/compiler/typekeys.nim +++ b/compiler/typekeys.nim @@ -47,6 +47,11 @@ type CoConsiderOwned CoDistinct CoHashTypeInsideNode + CoPrecise # produce a FRONTEND-faithful, unique key (for the + # stable NIF *name*, not hook dedup): keep distinctions + # sem makes that the backend identity collapses — e.g. + # an `int literal(x)` type carries its value so it does + # not merge with `int`. See `getTypeKey`/`typeKeyHook`. TypeLoader* = proc (t: PType) {.nimcall.} SymLoader* = proc (s: PSym) {.nimcall.} @@ -139,6 +144,20 @@ proc maybeImported(c: var Context; s: PSym; conf: ConfigRef) {.inline.} = if s != nil and {sfImportc, sfExportc} * s.flagsImpl != {}: c.symKey(s, conf) +proc emitPreciseFlags(c: var Context; t: PType; flags: set[ConsiderFlag]) {.inline.} = + ## Under CoPrecise the NIF *name* must be as fine as `types.sameType`, which + ## compares `eqTypeFlags * flags` (see `sameFlags`). Without this a + ## `proc() {.gcsafe.}` keyed identically to `proc()`, and a `ref X not nil` + ## identically to `ref X` — the loader would then merge two frontend-distinct + ## types onto one NIF name. Emitted only for the naming path (CoPrecise); the + ## `setAttachedOp` hook key (no CoPrecise) is unaffected, so its byte layout is + ## unchanged. + if CoPrecise in flags: + let ef = eqTypeFlags * t.flagsImpl + if ef != {}: + withTree c.m, "´tflags": + for f in ef: c.m.addIntLit ord(f) + proc backendTypeName(t: PType; conf: ConfigRef): string = ## Stable cross-module identity of a backend-minted (lower-stage) type: its ## serialized `@bk` NIF name (mirrors ast2nif.nifTypeName). A closure-env @@ -229,6 +248,10 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef of tyInt: withTree c.m, "i": c.m.addIntLit -1 + # An `int literal(x)` type (nImpl holds the value) must stay distinct from + # plain `int` for the NIF name, else overload resolution breaks on reload. + if CoPrecise in flags and t.nImpl != nil: + c.m.addIntLit t.nImpl.intVal maybeImported(c, t.symImpl, conf) of tyInt8: withTree c.m, "i": @@ -266,6 +289,14 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef withTree c.m, "u": c.m.addIntLit 64 maybeImported(c, t.symImpl, conf) + of tyFloat: + withTree c.m, "f": + c.m.addIntLit -1 + # A `float literal(x)` type (nImpl = nkFloatLit) must stay distinct from + # plain `float`, just like the `int literal(x)` case above. + if CoPrecise in flags and t.nImpl != nil and t.nImpl.kind in {nkFloatLit..nkFloat64Lit}: + c.m.addFloatLit t.nImpl.floatVal + maybeImported(c, t.symImpl, conf) of tyObject, tyEnum: if t.typeInstImpl != nil: # prevent against infinite recursions here, see bug #8883: @@ -381,6 +412,9 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef c.m.addIdent toNifTag(t.callConvImpl) if tfVarargs in t.flagsImpl: c.m.addIdent "´varargs" + # `.gcsafe`/`.noSideEffect` (in eqTypeFlags) distinguish proc types under + # sameType, so they must distinguish the NIF name too. + emitPreciseFlags(c, t, flags) of tyArray: withTree c.m, toNifTag(t.kind): if t.sonsImpl.len == 0: @@ -395,6 +429,9 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef c.typeKey t.sonsImpl[i], flags, conf if tfNotNil in t.flagsImpl and CoType notin flags: c.m.addIdent "´notnil" + # tfNotNil/tfVarIsPtr/tfIsOutParam (eqTypeFlags) part of sameType identity + # for ref/ptr/var/lent/sink; the hook path (no CoPrecise) keeps its layout. + emitPreciseFlags(c, t, flags) proc typeKey*(t: PType; conf: ConfigRef; tl: TypeLoader; sl: SymLoader): string = var c: Context = Context(m: createMangler(30, -1), tl: tl, sl: sl,