Fil-C on Nix

Filnix wiki

nanofil/docs/filc-tests.md

Upstream Fil-C's test suite

Fil-C's own tests (filc/tests in the Fil-C repository, run there by filc/run-tests) are about 7,300 directories, each a small program with a manifest that says what must happen: exit 0, a Fil-C trap (exit 42), a crash, a compile or link failure, and which strings the output must or must not contain. tests/filctests.py builds them with nanofil and checks them as run-tests does:

git clone https://github.com/mbrock/fil-c ~/fil-c   # at lib/filc-staging.json's coreRev
FILCC=/tmp/filcc/bin/clang SWIPL=/tmp/swipl/bin/swipl \
  nanofil/tests/filctests.py ~/fil-c -j 24           # about 12 minutes on swa

Only tests whose sources are all C run (nanofil compiles nothing else). filcc preprocesses each .c, nanofil compiles it, as assembles it, and filcc links, with filc/tests/utils.c and the manifest's libraries. run-tests runs each program four ways (GC rage mode, scribbling and verifying, stop-the-world, release); this runs only the first, FUGC_RAGE_MODE=1. --cc filcc builds the same tests with filcc, which shows which failures belong to the environment: five do (two static links that the Nix glibc toolchain refuses, a -la library path, a symver script, a packed union clang refuses at -O2).

Results

Staging at fil-c b24cd9552c09 (deluge 7b9ca2bdbb21), November 2026, with --modes all (a test passes only if it passes in all four modes):

tests
in filc/tests 7,325
skipped: C++, assembly or LLVM IR sources 1,302
skipped: another platform (only-on-platform) 506
skipped: skip in the manifest 4
run 5,491
pass 4,011
refused by nanofil 1,387
trap with a different message 38
link error 40
spurious trap 6
wrong output or exit status 6
over the time limit when scribbling 2
diagnostic differs 1

No test that must trap ran to completion. Every test whose manifest expects a Fil-C trap or a crash trapped or crashed under nanofil, or was refused at compile time.

Safety triage

Filip Pizlo's triage for this suite: what matters is a program that crashes (a signal, not a Fil-C panic) or that exits normally where the suite demands a panic. A different panic where one is due does not. filctests.py reports these as crash and missed trap. With --modes all, all four of run-tests' ways to run (GC rage mode, scribbling and verifying, stop-the-world, release), there are:

Refused (1,387)

nanofil refuses C it does not lower, with a diagnostic (reject.sh). Nearly all are SIMD:

Traps with a different message (38)

The program traps where it must, but the message differs from filcc's, so output-includes fails. Most (vibemove, zmemmove_union, cloningfail2) are copies that nanofil hands to the runtime's zmemmove, which reports "cannot read source pointer ..." where filcc's inline check reports "cannot read pointer ...". Others report a narrower check first (uaf: "cannot write pointer to free object" for filcc's "cannot access"), and stackdumpbug wants a file and line in the stack trace, which nanofil does not emit.

Spurious traps (6)

Wrong output or exit status (6)

Diagnostic differs (1)

initmisalignedptrfail refuses the misaligned pointer initializer as filcc does, but the message does not name the object.

Fixed by this suite

The first run passed 3,747; these fixes add 261 (in all four modes):

Next

In order of tests gained per change:

  1. Report memmove traps with filcc's messages, by checking both ranges inline before calling zmemmove, as filcc does (about 30 tests).
  2. The intrinsics of Link errors (38 tests, IR operations).
  3. Copying capabilities with the words of integer-typed members, if the campaign wants filcc's union and struct transport (ub.md).
  4. __int128 (35 tests) and real musttail.

The SIMD inline asm and intrinsics (about 1,300 tests) are out of scope for now.