Sigil 0.22.10

release

A correctness and build-cache release. Optimized builds could compute wrong answers in two patterns, compiled code skipped argument-count checks, and Sigil's caches could run the wrong compiled code, ship stale native code, or throw away work they had already done. All of that is fixed, and an unchanged rebuild of a program now recompiles almost nothing.

Download 0.22.10Get started

Relock projects that make native or web builds. Code compiled by 0.22.10 needs the 0.22.10 runtime, and linking it against an older one fails with undefined reference to 'sigil_native_requires_sigil_lib_0_22_10_checked_calls'. Run sigil deps update --upgrade-sigil and rebuild. It moves the project's Sigil packages to the newest version its sigil: field allows, and it also re-resolves every other dependency, so expect those to move too. If sigil: pins a version below 0.22.10, raise it first.

Scripts, tests and the REPL could run the wrong compiled library

sigil <file>, sigil eval, sigil test and the REPL cache compiled libraries. That cache found a library by its name and trusted its timestamp, so it could hand back the wrong compiled copy: two projects that lock different versions of one library ran each other's code, and so did a scratch copy with a newer timestamp, an in-place edit that kept the timestamp, or a change to SIGIL_FEATURES. Every compiled module is now stored under what actually shaped it: its source, the compiler, the settings that change output, its dependencies, its path and the host. The new cache lives in $XDG_CACHE_HOME/sigil/compiled/; the old $XDG_CACHE_HOME/sigil/lib/<commit>+sgbN directories are no longer used and can be deleted.

Scripts and tests also follow the project's redirects now: sigil <file>, eval and test take a redirected dependency's sources from the redirect target, and sigil --redirects FILE ... sets redirects for one command.

Optimized builds computed wrong answers in two patterns

Release and web builds use the optimizer (O1 and O2), and so does bytecode compiled at those levels. Two of its passes could treat a value that came from a conditional as if it always came from one branch:

  (define (caller choose replacement)
    (let ((target (if choose (lambda (n) (+ n 1)) replacement)))
      (+ (target 7) 1)))

With choose false and a replacement that returns 41, this returned 9 instead of 42.

Both returned the wrong value silently, with no error. The optimizer now checks that a value has a single source before it relies on one. The fixes change no other generated code: the standard library and the packages compile to identical C, and so do both Flux Harmonic games for the second fix.

Builds reuse more and never ship stale code

Fixes

Known issues