iter str-concat: heap-Str concatenation primitive in four-site lockstep
Closes fieldtest-form-a friction finding #4. `str_concat : (borrow Str, borrow Str) -> Str` ships in the four-site-lockstep pattern established by `str_clone` / `int_to_str` / `bool_to_str` (iter 24.1). The LLM-natural Show-MyType body `(app str_concat "label=" (app int_to_str x))` now parses, checks, builds, and runs end-to-end. Sites touched (lockstep): - runtime/str.c — `ailang_str_concat(a, b)` slab-allocates and memcpys both source payloads into a new heap-Str. - ailang-check/src/builtins.rs — `env.globals.insert("str_concat", Fn { 2x Str borrow, ret Str own, effects [] })` + `list()` row + `install_str_concat_signature` unit test. - ailang-codegen/src/lib.rs — `declare ptr @ailang_str_concat(ptr, ptr)` extern + `lower_app` arm after str_clone + `is_builtin_callable` extension + IR-pin unit test `str_concat_emits_call_to_ailang_str_concat`. - examples/show_user_adt_with_label.ail (new) + crates/ail/tests/ str_concat_e2e.rs (new) — corpus fixture exercising the LLM-natural Show body shape + E2E pin asserting check + build + run produce `Item 42\n`. Lockstep collision repaired: examples/bug_unbound_in_instance_method.ail used `str_concat` as its UNBOUND name (because that was the literal fieldtester repro). Renamed to `format_label` (LLM-author-realistic helper name that will never become a builtin) and updated the pin test `crates/ail/tests/unbound_in_instance_method_pin.rs` accordingly, preserving the regression guard's intent (instance-method-body walked through unbound-var check). DESIGN.md amended: new §"Heap-Str primitives" subsection between the milestone-24 Show-backer enumeration and the existing `Primitive output goes through ...` paragraph, cataloguing all five heap-Str primitives (`int_to_str`, `bool_to_str`, `float_to_str`, `str_clone`, `str_concat`) with signatures, iter origins, and the user-visible-vs-prelude-internal distinction. Show-backer block unchanged. IR snapshots regenerated (hello, list, max3, sum, ws_main) to absorb the new `declare ptr @ailang_str_concat(ptr, ptr)` line in the unconditional extern header — same upkeep pattern as hs.4 which regenerated the same 5 snapshots for the same reason (unconditional declares dead-stripped by clang -O2 when unused). Tests: 559 + 3 = 562 green (E2E pin + builtin-signature test + IR-pin test). Zero re-loops across all 7 tasks.
This commit is contained in:
@@ -184,3 +184,30 @@ char *ailang_str_clone(const char *src) {
|
||||
payload[8 + len] = '\0';
|
||||
return payload;
|
||||
}
|
||||
|
||||
/*
|
||||
* `ailang_str_concat(a, b)` — heap-Str concatenation primitive
|
||||
* shipped in iter str-concat (2026-05-13). Reads `len` headers from
|
||||
* both source Str payloads (offset 0 of each), allocates a fresh
|
||||
* heap-Str slab sized for the combined bytes via `str_alloc`,
|
||||
* memcpys both payloads in order, NUL-terminates, returns the new
|
||||
* payload pointer. Like `str_clone`, this works uniformly on static-
|
||||
* Str and heap-Str inputs because the consumer ABI is identical
|
||||
* between realisations (see DESIGN.md §"Heap-Str primitives").
|
||||
*
|
||||
* Used by Show bodies that want labelled output (e.g.
|
||||
* `"Item " ++ int_to_str n`) and by any caller that needs to
|
||||
* combine two existing Str values into a new owned Str.
|
||||
*
|
||||
* Returns the payload pointer of a fresh heap-Str slab. The rc
|
||||
* header is initialised to 1 by `str_alloc`.
|
||||
*/
|
||||
char *ailang_str_concat(const char *a, const char *b) {
|
||||
uint64_t la = *(const uint64_t *)a;
|
||||
uint64_t lb = *(const uint64_t *)b;
|
||||
char *payload = str_alloc(la + lb);
|
||||
memcpy(payload + 8, a + 8, la);
|
||||
memcpy(payload + 8 + la, b + 8, lb);
|
||||
payload[8 + la + lb] = '\0';
|
||||
return payload;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user