iter embedding-abi-m2.1 (DONE 5-9): swarm-safe ctx ABI complete + sanitiser-verified

Completes Embedding ABI — M2.1 (Tasks 1-4 committed c9a84b3 as the
known-good subset; this commit lands Tasks 5-9 from the re-dispatch
against the Boss-corrected plan).

- T5: swarm.c per-thread-ctx scalar-swarm capability demo (no
  SHARED_CTX) + rc_accounting_shared_ctx_is_flagged_by_tsan added to
  the item-1 harness driver (de-globalisation negative-control teeth,
  where a shared ctx genuinely races on ctx->alloc_count).
- T6: M1 host.c migrated to the ctx ABI; embed_e2e green (s==25
  through the ctx-threaded forwarder).
- T7: DESIGN.md current-state update (provisional-until-M3 narrowed
  to the value/record layout; Decision-10 per-thread-ctx note);
  docs_honesty-pinned sentence + (con Int) snippet preserved verbatim.
- T8: regression gate — workspace green; null-ctx fallback
  byte-behaviour preserved.
- T9: bench-trio — compile_check/cross_lang clean; check.py
  tracked-P2 noise only, causally exonerated (zero codegen/runtime
  diff vs HEAD), NO baseline ratified.

Boss-verified directly (not on report trust): docs_honesty_pin 5/0,
design_schema_drift 8/0, embed_export_hash_stable 1/0,
embed_export_gate 6/0, embed_e2e 1/0, embed_staticlib_alloc_guard
2/0, print_no_leak_pin 1/0 + e2e rc_stats 4/0; the tsan coherent-stop
proven by hand — per-ctx clean (exit 0, 0 warnings), shared-ctx a
real ThreadSanitizer data race at rc.c:157/:208 (exit 66).

Known debt (journal + roadmap): DESIGN.md '## Embedding ABI (M1)'
header still says (M1) while the body mirrors M2 — out of Task 7's
scope-limited regions by design; post-audit doc-honesty tidy.

Milestone-close audit next.
This commit is contained in:
2026-05-18 17:31:50 +02:00
parent c9a84b33b3
commit fbeeadeba5
8 changed files with 349 additions and 118 deletions
+15 -5
View File
@@ -1526,7 +1526,10 @@ acyclicity invariant.
`(con T)` is treated as `(own T)`. Existing JSON hashes
stay bit-identical until the fixture is intentionally updated.
- **Does not commit to atomic refcounts.** AILang is
single-threaded; refcounts are non-atomic.
single-threaded; refcounts are non-atomic. The embedding ABI's
per-thread `ailang_ctx_t` keeps this correct under a host swarm:
each thread's allocations are private to its ctx, no RC cell
crosses a thread, so non-atomic refcounts stay sound.
- **Does not introduce regions.** Regions were considered and
rejected; see "Why not other memory models" above.
@@ -2279,10 +2282,17 @@ parameter and the return type must be `Int` (lowered `i64`) or
`Float` (lowered `double`), and the fn's effect set must be empty.
These are enforced at `ail check` (`export-non-scalar-signature`,
`export-has-effects`) — an effectful or non-scalar export *fails to
typecheck*. M1 ships no runtime lifecycle API: the per-thread
context parameter that M2 threads through every exported call will
change this C signature; do not treat the M1 signature as frozen.
The value/record layout freeze is M3.
typecheck*. Every exported entrypoint takes a mandatory leading `ailang_ctx_t*`
(M2): a per-thread embedding context created by `ailang_ctx_new()`
and released by `ailang_ctx_free()`, owned by the calling thread for
its lifetime. The host links one `ailang_ctx_t` per OS worker thread
and the runtime accounts RC alloc/free into it (no shared mutable
runtime state — the swarm artefact is data-race-free, sanitiser-
verified). The staticlib swarm artefact is **RC-only**:
`ail build --emit=staticlib` rejects `--alloc=gc`/`--alloc=bump`
(the shared Boehm collector is not swarm-safe). Only the value/record
layout remains provisional until M3; the ctx-threaded C signature is
the M2 shape.
Export parameters are written **bare**: a scalar type carries no
`own`/`borrow` mode (modes apply only to heap-shaped types, which the