Releases: onlyarche/rulisp
Releases · onlyarche/rulisp
Release list
v0.5.0
Added
:stringASCII fast path — both directions of a:stringnow take
a typed check-and-store loop while the text is ASCII and hand the same
bytes to babel at the first char/byte ≥ 128, after a peek at the first
one so text that is non-ASCII from the start (CJK, say) allocates
nothing extra. Host-neutral, no wire change, the copy contract of
BOUNDARY §4 untouched — zero-copy:stringstays refused. A 64 KiB
ASCII round trip drops from ~1 ms to ~0.32 ms on the benchmark host
(docs/benchmarks.md); non-ASCII text costs what it did. New
v05.utf8-fastpath-boundarywalks the seam (empty, DEL, code 128,
interior NUL, Latin-1 range, 4-byte chars first/last, fill-pointer and
base strings) through wordbag's echo on all three hosts, and the bench
gains an rx "count over 1 MiB" row: a real&strconsumer.- Releases with audited assets — every
v*tag builds the four
examples in the release profile on each required host (Linux x86-64,
macOS arm64, Windows x86-64), runstools/rulisp-audit.shover each
(Windows: the script prints SKIP, recorded as a gap in BOUNDARY §12),
and attaches the twelve blobs, named forload-blob-crate, to a
GitHub Release whose body is that version's CHANGELOG section.
make check-versionsholds every site that repeats the version to
one string; docs/releasing.md is the checklist around it. - MSRV —
rust-version = "1.78"in the three crates, checked in CI
on exactly that toolchain over the macros, the runtime,rulispand
the code the macros generate forwordbag. - Linux aarch64 —
SBCL / Linux aarch64 (best-effort)on GitHub's
arm runners: the loader's aarch64 paths (the target check, the blob
suffix) execute for the first time. Promotion follows the written
procedure; thelinux-arm64release asset comes with it. - docs.rs front door — the three macros document the full attribute
grammar and the closed type vocabulary, every public item inrulisp
has a doc, the crate-level example is a compiled doctest, and CI builds
the docs withmissing_docsand broken links as errors. - Docstrings,
describe, and:doc— the REPL front door. Every
generated function and handle class now has a docstring synthesized
from the manifest (the Lisp call shape, the Rust signature, and which
condition anErrbecomes);(describe crate)prints where the crate
came from, its generation, versions, dump hook, handle classes and every
export's signature. A glue crate's///comments travel too: the macros
put them in the manifest as:doc(an enhancement key — older loaders
ignore it) and they lead the docstring.#.inside a:docis text,
never code: the loader's reader runs with*read-eval*off. :rulisp-versionin the manifest, and the key-class rule (BOUNDARY
§11): a load-bearing manifest key raises:schema; an enhancement key
rides the ignore-unknown-keys rule. Every crate now records the rulisp
it was built with, and a loader whose major.minor is older signals
rulisp-version-skew(astyle-warning) and loads anyway. Stated
plainly, because this rule is new: a crate built with 0.4 or later that
declareson_dumploads cleanly on a 0.3 loader with its dump hook
silently dropped —:on-dumppredates the rule and stays at:schema1,
since a retroactive bump would refuse every newer crate on the 0.4.0
loaders that support it fully. Upgrade the loader.- docs/stability.md — what is stable and what 1.0 will promise: the
four versioned surfaces (Rust API, the 32-symbol Lisp API, the manifest
schema, the C ABI) and what breaks each; semver and deprecation policy;
host support defined as the required CI matrix with a written
promotion/demotion procedure; the manifest key-class rule; the 1.0 exit
criteria; the Quicklisp prerequisites. CI now proves the system loads
with no cargo on PATH.
Fixed
- Docstrings named a symbol that does not exist — for a
Result<_, Error>export the synthesized "Signals:" line said
<crate>:rust-error; the condition isrulisp:rust-erroritself.
describelisted a///-documented export by its first doc line
instead of its call shape; it now prints the call shape for every
export. Both surfaced when the docs audit compared usage.md's
transcript with the loader's real output. - Audit false positive on macOS —
tools/rulisp-audit.shmatched an
inner_signal(_dispatch_semaphore_signal, imported by every macOS
artifact) as a signal import; only a leading underscore is Mach-O's now,
and the self-test checks the pattern both ways. - On CCL, lending a buffer to Rust stopped the world. CFFI's
with-pointer-to-vector-dataisccl:with-pointer-to-ivectorthere,
whose body runs underwithout-gcing— so every:string,:bytes
and:vecargument suspended garbage collection for every thread for
the whole export, callbacks included (measured: 0 collections
completed on another thread during a 600 ms borrow; ~30 unpinned). The
loader now pins only long enough to memcpy into a heap buffer on CCL
and lends the copy. SBCL keeps the zero-copy borrow; ECL's pin never
inhibited GC.v05.pin-does-not-stop-the-worldproves it on all three.
Changed
- Docs claim audit — every capability, host and performance claim in
README and the docs pages (371 of them) now has a citation in
docs/claims.md or was corrected: the handle bullet (a second free is
refused, a racing free is deferred), "Scope (v0.1)" and the stale
"non-SBCL image dump" out-list, the quickstart's error-class rule and
its ~50 ns figure, usage.md's:target-check story and its Quicklisp
mention, benchmarks.md's 16× comparison, installation.md's
cargo-not-found and Windows cache-path rows. One support table (README
§Status) equals the required CI matrix; benchmarks.md carries SBCL,
CCL and ECL columns. CI now also loads rulisp from Quicklisp's
local-projects andload-blob-crates a downloaded release asset, as
the pages instruct. - The fuzzers can fail now.
m4h.thread-raceand
m4h.random-op-sequenceasserted only "no unexpected condition"; a
leaked in-flight count or a free that never reached Rust passed. Both
now reconcile every generation's live-handle and live-allocation
counters at the end (captured as wrapper closures, so generations
reloaded mid-run reconcile against their own library copy), take their
seed fromRULISP_FUZZ_SEED(aworkflow_dispatchinput in CI) and
print it on failure. Verified by mutation: with half of all frees
skipped, the race fuzzer reports the leak. Newm4h.reload-under-load
proves BOUNDARY §4's cross-reload claim — strings round-tripped by four
threads across three live reloads are released through their own
generation's dealloc; every generation ends at zero. - The fetch suite runs in CI (SBCL/Linux and CCL/Linux required; macOS
best-effort) — the 23 tests BOUNDARY §12 cites had only ever run on the
maintainer's machine. Its first run on CCL fixed two host assumptions in
the tests themselves.
v0.4.0
Added
- BOUNDARY.md §12: the conformance table. Every normative claim in
§1–§11 (103 rows) classified as compile error, runtime check, test,
documented-UB-by-design, or gap — with verified citations. The two
remaining gaps are scheduled v0.4 items and say so in the table. - Eight new conformance tests (
:rulisp-v04), closing every prose-only
claim the sweep found: swallowedCallbackErrordiscards the stash;
swallow-then-fail re-signals the original condition (status 4); a panic
in a*_freeshim is caught and swallowed (newGrenadefixture handle
whoseDroppanics); a poisoned mutex reportsrust-panicon the next
lock;last_erroris thread-local AND per-library (concurrent failures
in wordbag and rx never cross); out-params survive an ERR return
untouched (FFI-level sentinel test); ECL's missing-C-compiler failure is
the namedmanifest-error(injected viac::*cc*). on_dump: <fn>inrulisp::module!— a declared, zero-arg shutdown
export the loader auto-registers as an image-dump hook (new wire-additive
manifest key(:on-dump "symbol"); ABI 1 intact). Hooks run in load
order; a failing hook is warned and skipped — a dump is never wedged by
its own cleanup. The manifest refuses a declaration whose function takes
parameters or returns a value (the call goes through a fixed signature),
andmodule!rejects it at compile time too.examples/fetchconverts:
its hook quiesces every live tokio runtime, and the suite proves the
whole story by dumping an image with a request in flight and restoring
it (the two tests the v0.3 risk table promised).- docs/benchmarks.md — a dated, release-profile baseline with the
method; every number quoted in this changelog traces to it. - The ECL CI job is required (was best-effort): 24 consecutive green
runs since the 0.2 trampoline fix, now including the program-op
deployment smoke. The workflow documents the demotion procedure. tools/rulisp-audit.sh— BOUNDARY §7 as a command for any glue
crate (signal-disposition imports on the artifact, tokio signal/process
features,block_on), run over every example bymake auditin CI. A
self-test builds a library that deliberately importssignal()and
requires the audit to reject it, so the gate cannot go inert unnoticed
again. fetch'saudit.shis now a wrapper adding its OpenSSL rule.#[rulisp(constructor, name = "…")]— an explicit Lisp name for a
constructor. Load-bearing since 0.3 made duplicate:lisp-namea hard
error: two constructors on one type both derivedmake-<type>, so a
second constructor was inexpressible. The attribute grammar is now
strict — a misspelled key, a non-string name, a name with reader syntax,
ornameon a non-constructor are compile errors (previously unknown
keys were silently ignored).- ECL deployment story (docs/distribution.md Pattern B′): ECL has no
image dump; applications ship asasdf:program-opexecutables. The
documented recipe (:no-uiop tbecause the distro ECL cannot satisfy
ASDF's default static link ofcmp— nolibcmp.a— a prologue
require, an explicit epilogue entry call, and the dependencies loaded
beforeprogram-op, which the bundled ASDF 3.1.8.8 cannot build from a
cold cache) is exercised bytests/ecl-program, a minimal consumer the
ECL CI job builds and runs (make test-ecl-program). - The dump/restore test (m7) now really runs on CCL — and Windows —
instead of passing vacuously off SBCL, with a new assertion: a pre-dump
handle GC'd after restore must not make a foreign call.
Fixed
- Two processes sharing one crate cache could crash each other. The
cache copy was named<crate>-c<n>-<universal-time>withncounted per
process, so two instances started in the same second wrote the same
file, andcopy-filerewrote a library the other process had already
mapped — a segmentation fault (found by the ECL deployment verification,
reproduced 5/5). Copies now carry a per-process tag (pid, or a random
tag where the host has none), recomputed on image restore; the sweep
deletes this process's older generations immediately and other
processes' copies only after an hour, so it can no longer unlink a copy
another process is about todlopen. - A failed
dlopennames the artifact the user asked for, not only the
cache copy. - rulisp's load-time compiles are quiet now: on ECL,
compileprints
per-function notes, so a deployed program printed dozens of lines every
time it loaded a crate. - The loader now rejects
(:option :bool)in a hand-written manifest
(option-inner); previously only the macro refused to emit it.