From 4c4b8ef9660e69ca92a6f85d21ec54e978b870cf Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 2 Jul 2026 12:10:40 +0200 Subject: [PATCH] Make Nimble compile again --- compiler/ast2nif.nim | 14 ++++++++++++-- compiler/astdef.nim | 11 ++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compiler/ast2nif.nim b/compiler/ast2nif.nim index 6fb138692d..72375a775e 100644 --- a/compiler/ast2nif.nim +++ b/compiler/ast2nif.nim @@ -2808,8 +2808,18 @@ proc materializeLazyBody*(c: var DecodeContext; node: PNode) = node.typField = real.typField node.flags = real.flags -forceLazyBodyHook = proc (n: PNode) {.nimcall.} = - if loaderCtx != nil: materializeLazyBody(loaderCtx[], n) +forceLazyBodyHook = proc (n: PNode) {.nimcall, raises: [], tags: [], gcsafe.} = + # `len` (the sole caller path) MUST stay effect-free, so this hook is typed + # `raises: []`. The underlying `loadNode` chain infers `raises: [KeyError]` + # (index/sym Table lookups), but materialization only ever runs for a body + # DEFERRED during THIS load — the buffer/index is present by construction, so a + # KeyError here means a corrupt cache: a fatal bug, not a recoverable error. + # Treat it as effect-free (a `Defect`-like invariant) via a scoped cast. + if loaderCtx != nil: + {.cast(raises: []).}: + {.cast(tags: []).}: + {.cast(gcsafe).}: + materializeLazyBody(loaderCtx[], n) proc loadSymFromIndexEntry(c: var DecodeContext; module: FileIndex; nifName: string; entry: NifIndexEntry; thisModule: string): PSym = diff --git a/compiler/astdef.nim b/compiler/astdef.nim index 8817e3b15c..bd593577a7 100644 --- a/compiler/astdef.nim +++ b/compiler/astdef.nim @@ -906,11 +906,20 @@ const defaultOffset* = -1 -var forceLazyBodyHook*: proc (n: PNode) {.nimcall.} +var forceLazyBodyHook*: proc (n: PNode) {.nimcall, raises: [], tags: [], gcsafe.} ## Set by the IC loader (ast2nif). When a node carries `nfLazyBody`, any access ## to its children through `len` materializes the deferred routine body in place. ## `safeLen` delegates to `len`, so it is covered transitively; a lazy body is ## never a leaf kind, so the `{nkNone..nkNilLit}` short-circuit never hides it. + ## + ## The type MUST be effect-free (`raises: []`/`tags: []`): `len` is a fundamental + ## `PNode` accessor that the whole compiler — and every compiler-as-library + ## consumer (nimble, nimsuggest, ...) — assumes cannot raise. An unannotated + ## `proc` var defaults to `raises: [Exception]`, so the indirect call tainted + ## `len`/`safeLen`/`items` with `Exception`, breaking any iterator/`{.raises.}` + ## over a `PNode` (e.g. nimble's `extract {.raises: [CatchableError].}`). + ## Materialization is a pure in-memory buffer transform; a corrupt buffer is a + ## `Defect` (`raiseAssert`), which is outside exception tracking. proc len*(n: PNode): int {.inline.} = if nfLazyBody in n.flags and forceLazyBodyHook != nil: