Sigil 0.19.2

release

A correctness and speed release. Calling a native procedure with the wrong number of arguments now raises an arity error instead of reading whatever happened to be on the stack, native builds work on OpenBSD, and a native build refuses to link against a runtime older than the compiler that produced it rather than producing a binary that misbehaves later. Sigil also starts about three times faster.

Download 0.19.2Get started

Existing projects build without changes, with two exceptions worth reading below: code that over-applied a native procedure now raises, and a native build whose lockfile pins an older sigil-lib than the compiler will now refuse to link.

Calling a native with the wrong number of arguments now raises

(car '(1 2) 3 4) returned 1. (car) returned a type error about something you did not write. (apply car '()) crashed the process with a segmentation fault. Each of those took a different path into the same native procedure, and only one of the four checked how many arguments it was given.

Natives that document an argument count now have it enforced on every path, so all four spellings raise the same arity error naming what was expected and what arrived. The crash was the visible case: a native reading argument one when it was passed none read an uninitialised stack slot, which segfaulted in one build and returned a plausible wrong answer in another.

This can surface existing bugs. If your code passes a native more arguments than it takes, that call used to succeed quietly and now raises. The extra arguments were always ignored; what changed is that Sigil says so.

Variadic natives are unaffected: (apply + (make-list 1000 1)) and a 5000 argument string-append work exactly as before.

Native builds work on OpenBSD

A natively compiled Sigil program died on OpenBSD at the first thing it did inside with-async. No error, no output, nothing in the log. The binary built cleanly, installed cleanly and answered --version correctly, so every check short of running it passed.

OpenBSD refuses a system call made while the stack pointer is in memory the kernel was not told is a stack. Sigil's coroutines allocated their stacks on the heap, so the first system call inside any fiber killed the process. Fiber stacks are now mapped with MAP_STACK on OpenBSD, and nothing changes on other platforms.

If you disabled the native backend on OpenBSD because of this, you can turn it back on.

A native build refuses a runtime older than the compiler

Building natively with one Sigil version while your lockfile pins an older sigil-lib produced a binary that ran, mostly. Calls the newer compiler emitted by name were absent from the older runtime's table, and the lookup failure returned an undefined value rather than raising. Programs that guarded the call saw garbled output. Programs that did not carried the undefined value forward.

The native link now compares what the generated code calls against what the linked runtime provides and refuses when they disagree, naming the procedures that are missing. A native build under the 0.19.2 toolchain with sigil-lib pinned at 0.19.0 or earlier will now stop. Run sigil deps update and it proceeds. Bytecode builds are unaffected, because a bytecode bundle carries the runtime it was compiled against.

The check adds about a second to a native link. Development and test link paths do not perform it yet, because they link no generated code for it to read.

Sigil starts about three times faster

sigil --version took roughly 500ms and now takes roughly 180ms. Garbage collection got faster by a wider margin: a collection heavy workload runs about six times fewer instructions.

Two diagnostic environment variables were being read once per object during collection, and reading an environment variable scans the whole environment. Sigil's speed therefore depended on how many environment variables your shell had, with no way to notice: the same program was measurably slower under a large environment than a small one. The values are now read once per process.

Setting SIGIL_GC_CRASH_DEBUG or SIGIL_GC_TRACE_FREE after the process has started no longer takes effect. Set them before starting the process, as before.