Skip to content

Releases: onlyarche/rulisp

Release list

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 06:15

Added

  • :string ASCII fast path — both directions of a :string now 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 :string stays 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-boundary walks 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 &str consumer.
  • 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), runs tools/rulisp-audit.sh over each
    (Windows: the script prints SKIP, recorded as a gap in BOUNDARY §12),
    and attaches the twelve blobs, named for load-blob-crate, to a
    GitHub Release whose body is that version's CHANGELOG section.
    make check-versions holds every site that repeats the version to
    one string; docs/releasing.md is the checklist around it.
  • MSRVrust-version = "1.78" in the three crates, checked in CI
    on exactly that toolchain over the macros, the runtime, rulisp and
    the code the macros generate for wordbag.
  • Linux aarch64SBCL / 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; the linux-arm64 release asset comes with it.
  • docs.rs front door — the three macros document the full attribute
    grammar and the closed type vocabulary, every public item in rulisp
    has a doc, the crate-level example is a compiled doctest, and CI builds
    the docs with missing_docs and 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 an Err becomes); (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 :doc is text,
    never code: the loader's reader runs with *read-eval* off.
  • :rulisp-version in 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 (a style-warning) and loads anyway. Stated
    plainly, because this rule is new: a crate built with 0.4 or later that
    declares on_dump loads cleanly on a 0.3 loader with its dump hook
    silently dropped — :on-dump predates the rule and stays at :schema 1,
    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 is rulisp:rust-error itself.
    describe listed 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 macOStools/rulisp-audit.sh matched 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-data is ccl:with-pointer-to-ivector there,
    whose body runs under without-gcing — so every :string, :bytes
    and :vec argument 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-world proves 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 and load-blob-crates a downloaded release asset, as
    the pages instruct.
  • The fuzzers can fail now. m4h.thread-race and
    m4h.random-op-sequence asserted 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 from RULISP_FUZZ_SEED (a workflow_dispatch input in CI) and
    print it on failure. Verified by mutation: with half of all frees
    skipped, the race fuzzer reports the leak. New m4h.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

Choose a tag to compare

@github-actions github-actions released this 03 Sep 06:25

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: swallowed CallbackError discards the stash;
    swallow-then-fail re-signals the original condition (status 4); a panic
    in a *_free shim is caught and swallowed (new Grenade fixture handle
    whose Drop panics); a poisoned mutex reports rust-panic on the next
    lock; last_error is 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 named manifest-error (injected via c::*cc*).
  • on_dump: <fn> in rulisp::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),
    and module! rejects it at compile time too. examples/fetch converts:
    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 by make audit in CI. A
    self-test builds a library that deliberately imports signal() and
    requires the audit to reject it, so the gate cannot go inert unnoticed
    again. fetch's audit.sh is 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-name a hard
    error: two constructors on one type both derived make-<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,
    or name on 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 as asdf:program-op executables. The
    documented recipe (:no-uiop t because the distro ECL cannot satisfy
    ASDF's default static link of cmp — no libcmp.a — a prologue
    require, an explicit epilogue entry call, and the dependencies loaded
    before program-op, which the bundled ASDF 3.1.8.8 cannot build from a
    cold cache) is exercised by tests/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> with n counted per
    process, so two instances started in the same second wrote the same
    file, and copy-file rewrote 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 to dlopen.
  • A failed dlopen names the artifact the user asked for, not only the
    cache copy.
  • rulisp's load-time compiles are quiet now: on ECL, compile prints
    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.