Append three new symbols to runtime/str.c: a private str_alloc(uint64_t)
slab helper that allocates [rc_header | len | bytes... | NUL] via
ailang_rc_alloc and writes the len prefix, plus two extern formatters
ailang_int_to_str(int64_t) and ailang_float_to_str(double) that compose
str_alloc with snprintf("%lld") / snprintf("%g"). Defensive abort() on
truncation; 64-byte stack buffer comfortably oversized for either
formatter.
The extern declaration of ailang_rc_alloc carries __attribute__((weak)).
First regression sweep without it surfaced 11 e2e link failures under
--alloc=gc and --alloc=bump: public T-visible symbols (int_to_str /
float_to_str) pull their transitive callees (rc_alloc) into every
binary's symbol set, but rc.c is not linked under gc/bump in hs.3.
The weak attribute makes the cross-allocator link safe in isolation;
under rc the strong definition wins as usual. Retained after hs.4
(when rc.c becomes unconditionally linked) as a no-op that keeps
str.c link-safe in isolation.
No IR-side caller wired yet — hs.4 lands the IR-header declare lines,
the int_to_str/float_to_str codegen lowering, the checker install,
and the unconditional rc.c link.
cargo test --workspace + cross_lang.py + compile_check.py + check.py
all green on re-sweep.
5.4 KiB
iter hs.3 — Heap-Str runtime additions
Date: 2026-05-12
Started from: 187d04814d
Status: DONE
Tasks completed: 2 of 2
Summary
Third iter of the heap-Str ABI milestone. Single-file additive change
to runtime/str.c: three new symbols (private static char *str_alloc(uint64_t) slab helper plus two extern formatters
ailang_int_to_str(int64_t) and ailang_float_to_str(double)), the
supporting <stdint.h> / <stdio.h> / <stdlib.h> includes, and an
extern void *ailang_rc_alloc(size_t) declaration. No IR-side caller
is wired yet — hs.4 will land codegen + checker + linker work that
makes the formatters live builtins.
One deviation from the plan: the extern declaration of
ailang_rc_alloc was promoted from plain-external to
__attribute__((weak)). The plan's assertion that the new symbols
sit "present-but-unreferenced" across all --alloc strategies turned
out to be wrong at the link layer — ailang_int_to_str /
ailang_float_to_str are publicly-visible T symbols, so the linker
preserves them and their transitive callees (including
ailang_rc_alloc) in every binary regardless of program-level usage.
Under --alloc=gc and --alloc=bump the build pipeline does not
link rc.c, so the unresolved-symbol error fires at link time and
took out 11 e2e tests on the first regression sweep. The weak
attribute makes the reference link-safe in isolation: under gc/bump
it resolves to NULL (never dereferenced — no IR caller exists yet);
under rc the strong definition wins as usual. The attribute is
retained after hs.4 unconditionally links rc.c (where it becomes a
no-op) to keep runtime/str.c link-safe in isolation, matching the
spec's "single-file abstraction" framing of the runtime split.
Acceptance verified: cargo test --workspace (zero failures across
the full test surface), bench/cross_lang.py (25/25 metrics stable,
exit 0), bench/compile_check.py (24/24 metrics stable, exit 0),
bench/check.py (zero regressed, exit 0; same five
latency.explicit_at_rc.* improvements outside tolerance that
audit-cma and audit-ms have already documented as
not-coupled-to-milestone — baseline left untouched per established
practice). clang -c -O2 -Wall -Werror on runtime/str.c standalone
also exits 0 with no warnings.
Per-task notes
- iter hs.3.1: appended three
#includelines (stdint.h,stdio.h,stdlib.h), one weakextern void *ailang_rc_allocdeclaration, and three new function bodies (str_alloc,ailang_int_to_str,ailang_float_to_str) toruntime/str.c. Compile-check viaclang -c -O2 -Wall -Werrorgreen; nm symbol-table inspection: 4 publicTsymbols (ail_str_eq,ail_str_compare,ailang_int_to_str,ailang_float_to_str),str_allocabsent at-O2(inlined into both callers — only visible ast str_allocat-O0; static-linkage invariant preserved),ailang_rc_allocshown asw(weak undefined). - iter hs.3.2: ran the four regression gates. First-pass sweep
surfaced the 11-test linker failure under
--alloc=gc/bumpcaused by the plan's "rc_alloc reference is harmless if unreferenced" assumption; weak-attribute repair re-ran clean. All four gates green on the re-sweep.
Concerns
- The plan's Task 1 Step 7 nm-output expected-line block ("
U ailang_rc_alloc" and "0000000000000000 t str_alloc") is inaccurate at the optimisation level the plan itself specifies (-O2): the grep regex does not matchailang_rc_alloc, andclang -O2inlines the 2-linestr_allochelper into both callers so not str_allocsymbol survives the compilation unit. Both deviations are-O2consequences, not bugs — the semantically meaningful invariants (str_allocis static and absent fromT;ailang_rc_allocis a non-Tundefined reference) both hold. A future plan author writing nm-output checks should inspect at-O0for symbol presence and at-O2for what actually ships, or relax the expected text from literal-match to invariant-check (e.g. "noT str_alloc", "ailang_rc_allocisUorw"). - The plan's core "no build-pipeline change in hs.3" boundary held, but the supporting claim "symbols are present in every binary regardless of allocator choice" was false at the link layer. The weak-attribute repair makes it true at the cost of one extra attribute and a long-form doc-comment explaining the cross- allocator linking story. A future plan that defers a build- pipeline change should explicitly call out cross-translation-unit symbol dependencies as a gating concern.
Known debt
- hs.4 will wire
ailang_int_to_str/ailang_float_to_strfrom the IR side and unconditionally linkrc.c; at that point the__attribute__((weak))on theailang_rc_allocdeclaration becomes a no-op (strong definitions always satisfy weak references). The attribute is intentionally retained — see the doc-comment inruntime/str.c— to keep the translation unit link-safe in isolation, matching the parent spec's framing ofruntime/str.cas the heap-Str ABI's single point of contact.
Files touched
runtime/str.c— three new functions appended afterail_str_compare, plus three new#includelines and one weakexterndeclaration after the existing include block. Existingail_str_eq/ail_str_comparebodies and the file's top comment block untouched.
Stats
bench/orchestrator-stats/2026-05-12-iter-hs.3.json