mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
Nine `PNode`-typed codegen procs become `AnyNode`, all pure readers: `bodyCanRaise` (the consumer of the `canRaise` work), `isAssignedImmediately`, `branchHasTooBigRange`, `hasNoInit`, `reifiedOpenArray`, `skipTrivialIndirections`, `isSimpleExpr`, `isConstClosure` and `fewCmps`. The signatures are the small part. A migrated proc that nothing calls with a `BNode` is not even type-checked, so the substance is `grindPredicates`: every one of them runs on BOTH spellings of the SAME node, at EVERY node of every graded body, inside the walk `grindLockstep` was already doing. 67_721 nodes on the reference target, 0 disagreements. Two things had to be gated, and neither by widening a guard until the run went green. The tolerated `(ht . <sym>)` type difference is benign for the vocabulary check and NOT benign for a predicate that reads `typ` — and because the predicates recurse, one excused node poisons every ancestor's answer too. `grindLockstep` now reports whether a subtree is free of it, and only clean subtrees are graded. `isAssignedImmediately` and `fewCmps` hand `n.typ` to `getSize`/`mapType`, which are total only over types the C backend can lay out. Asked at an arbitrary node they meet a `tyGenericParam` or a `tyAnything` and abort — that is a question with no answer in either spelling, not a disagreement between them. Both are graded FROM THE PARENT, at the position production calls them from. Declarative subtrees are skipped for the same reason, along the boundary `bodyCanRaise` already draws. Both exclusions are counted and printed beside the graded count, so a run that grades nothing cannot pass for a run that grades everything. `tools/icgrind` versions the grind target, because two ways of silently getting no coverage turned up while writing it: the main module's routines are never graded, and `ast2nif` defers only `nkStmtList` bodies, so a one-line `proc f(x: int): int = case x ...` is invisible to the oracle. Shapes added for `branchHasTooBigRange` and `fewCmps` produced exactly zero coverage until both were found by counting rather than assumed. Verified: 215/215 byte-identical `.c` against HEAD on the default path; sabotaging `isAtom` and sabotaging the parent-driven node selection each make the grinder fail on the first body it reaches. Recorded rather than papered over: `isConstClosure` is graded only on its false side — the whole closure contains one `nkClosure` node. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR