`nim c --ic:on` could not compile anything that reached `std/tempfiles`:
lib/std/tempfiles.nim(115, 38) Error: type mismatch
Expression: initRand()
Expected one of: [1] proc initRand(seed: int64)
That is `std/random`. It declares `proc initRand(): Rand` WITHOUT a `*`
and then, forty lines later, `since (1, 5, 1): export initRand`.
Two mechanisms make a symbol importable and the writer only knew one.
`sfExported` is the `*` on the declaration; `semExport` instead calls
`reexportSym`, which adds the symbol to the module's interface table and
sets no flag. `writeSymDef` decided the NIF `x` marker — "importable as a
bare identifier" — from `sfExported` alone, so a symbol exported the
second way shipped as private and its importer reported an undeclared
identifier. Nothing about this is IC-specific in the source; it is
ordinary stdlib code that classic compilation accepts.
So ask the interface too: `reexportedLocalSyms` collects the symbols a
module defines that reached its interface without the flag, and they get
the marker. Symbols that are genuinely private still do not — the
regression test's sibling case (`proc g()`, never exported) is still
rejected under both `--ic:on` and `--ic:off`.
This was found by pointing `--ic:on` at Atlas, which is now the first
sizeable third-party program it builds. `nim c --ic:off` and `--ic:on`
produce the same working binary from the same 209-module closure.
ic 41/41 (the new test is the 41st), `koch boot -d:release` equal
executables.
Running tests/ic
./bin/testament --nim:<your compiler> cat ic
The metamorphic tests are expensive, and look hung when they are not
16 of the tests carry #? metamorphic. Each has 3–4 #!STEP directives, and
every step compiles the program twice — once under nim ic, once with
nim c as the reference oracle. That is 100+ full compilations for the
category. Under --ic:on each compilation additionally fans out one backend
process per module per stage, and each of those is a compiler holding its own
module graph (~800MB peak).
A nim ic parent sitting at 0% CPU is normal. It is waiting on its
children. It is not a deadlock, and neither is a metamorphic test that occupies
the runner for many minutes. Before concluding anything is stuck, check that the
test NAME changes over a few minutes — that is the difference between slow and
hung, and it is easy to get wrong.
On a memory-constrained machine the fan-out will swap. The symptoms are exactly
the ones that read as a deadlock: several processes at 0% CPU, no output, a
different test "stuck" on every run, and the same compilation finishing in
seconds when run on its own. Check vm_stat (page-ins per second) and
sysctl vm.swapusage before looking for a bug. This was diagnosed as a
testament/nim ic interaction more than once before anyone measured.
Cap the fan-out to fit the machine — precedence documented at deps.nim's
let parallel:
--parallelBuild:N # standard flag, given meaning under IC
-d:icJobs:N # same cap, legacy define
-d:icNoParallel # serial, and non-interleaved child output
Serial output matters for a second reason: the parallel backend processes share
one stderr, so any per-process diagnostic printing (NIM_IC_BNODE_GRIND,
-d:icCanRaiseLog) interleaves and produces torn lines. Either use
-d:icNoParallel or parse defensively and count what you dropped.
Running a single test
testament r tests/ic/<file>.nim works for the ordinary tests. It does NOT work
for the metamorphic ones — the multi-step files carry several discard """
spec blocks and the single-test path rejects them with "duplicate specStart".
Those only run through cat ic.
Files matching tests/ic/*_temp.nim are ignored by git (see .gitignore) and
are scratch, not tests: several import helper modules that do not exist and fail
for that reason alone.