From fa66510f7a66f53c229d29728c9e76ba4ada5a0b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 10 Jul 2026 14:07:35 +0200 Subject: [PATCH] IC: bugfix (#25982) --- compiler/sem.nim | 8 ++++++++ koch.nim | 2 +- tests/ic/mtraitparam.nim | 34 ++++++++++++++++++++++++++++++++++ tests/ic/ttraitparam.nim | 14 ++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/ic/mtraitparam.nim create mode 100644 tests/ic/ttraitparam.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index ab6542f9d6..a689e2626f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -288,6 +288,14 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = result = copySym(result) result.ast = n.sym.ast put(c.p, n.sym, result) + if result.state == Sealed: + # the symbol was loaded from another module's NIF cache (e.g. a param + # symbol spliced out of an imported proc type by a `typed` macro) and is + # therefore immutable; the caller re-owns it and assigns its type/flags, + # so hand back a fresh, mutable copy owned by the current module instead. + let fresh = copySym(result, c.idgen) + fresh.ast = result.ast + result = fresh # when there is a nested proc inside a template, semtmpl # will assign a wrong owner during the first pass over the # template; we must fix it here: see #909 diff --git a/koch.nim b/koch.nim index 9d63618e67..1220550681 100644 --- a/koch.nim +++ b/koch.nim @@ -619,7 +619,7 @@ proc runIcTestFile(inp: string) = const icSuite = ["thallo", "tconverter", "timp", "tmiscs", "tparseutils", "tcompiletimeglobal", "tsighashstable", "tpureenum", "tgenericoffer", "tconverterreexport", "ttypeoffer", "ttransitiveoffer", - "tmodsymref", "tmethupref", "temit"] + "tmodsymref", "tmethupref", "temit", "ttraitparam"] proc icTest(args: string) = temp("") diff --git a/tests/ic/mtraitparam.nim b/tests/ic/mtraitparam.nim new file mode 100644 index 0000000000..b18fbb1e5f --- /dev/null +++ b/tests/ic/mtraitparam.nim @@ -0,0 +1,34 @@ +import std/macros + +type + Chunk* = ref object + x*: int32 + ChunkTrait* = distinct tuple[ + loaded: proc(self: pointer, chunk: Chunk) + ] + +macro makeVTable*(traitType: typedesc): untyped = + ## Splice the param symbols out of an imported proc type into a fresh proc + ## type nested in a fresh tuple type. Under `nim ic` the imported trait is + ## loaded from a NIF cache, so its param symbols are `Sealed`; re-owning them + ## in `semProcTypeNode` used to trip `ast.nim` `s.state != Sealed`. + var t = traitType.getTypeInst[1].getTypeImpl + if t.kind == nnkDistinctTy: t = t[0] + let formalParams = t[0][1][0] + var bridgeParams = nnkFormalParams.newTree(formalParams[0].copyNimTree) + bridgeParams.add nnkIdentDefs.newTree(ident"p", ident"pointer", newEmptyNode()) + for j in 2 ..< formalParams.len: + bridgeParams.add formalParams[j].copyNimTree + let vtType = nnkTupleTy.newTree( + nnkIdentDefs.newTree(ident"m0", + nnkProcTy.newTree(bridgeParams, nnkPragma.newTree(ident"nimcall")), + newEmptyNode())) + let vtName = genSym(nskType, "VT") + let vtVar = genSym(nskVar, "vt") + result = nnkStmtList.newTree( + nnkTypeSection.newTree( + nnkTypeDef.newTree(vtName, newEmptyNode(), vtType)), + nnkVarSection.newTree( + nnkIdentDefs.newTree( + nnkPragmaExpr.newTree(vtVar, nnkPragma.newTree(ident"used")), + vtName, newEmptyNode()))) diff --git a/tests/ic/ttraitparam.nim b/tests/ic/ttraitparam.nim new file mode 100644 index 0000000000..35a81d39cb --- /dev/null +++ b/tests/ic/ttraitparam.nim @@ -0,0 +1,14 @@ +discard """ +output: '''ok''' +""" + +# Regression test: a `typed` macro in an imported module splices param symbols +# out of an imported proc type into a freshly semchecked proc type. Under +# `nim ic` those param symbols are loaded `Sealed` from the NIF cache; reusing +# them in `newSymG`/`semProcTypeNode` used to fail `ast.nim` `s.state != Sealed`. + +import mtraitparam + +makeVTable(ChunkTrait) + +echo "ok"