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:
@@ -1991,6 +1991,36 @@ Routing through `print` replaces the ad-hoc `io/print_int|bool|float`
|
||||
idiom for new code; retiring the per-type effect-ops is queued as a
|
||||
P2 follow-up.
|
||||
|
||||
### Heap-Str primitives
|
||||
|
||||
The runtime ships a small family of operations that produce or
|
||||
transform heap-allocated `Str` values uniformly across static-Str
|
||||
and heap-Str inputs (the consumer ABI is identical between
|
||||
realisations — see §"Str ABI"). All take their input(s) by `borrow`
|
||||
and return an owned `Str`. Each is registered as a builtin in
|
||||
`crates/ailang-check/src/builtins.rs`, lowered inline in
|
||||
`crates/ailang-codegen/src/lib.rs::lower_app` to a `call ptr @ailang_<name>`,
|
||||
and backed by a `runtime/str.c` C helper.
|
||||
|
||||
- `int_to_str : (borrow Int) -> Str` (iter 24.1) — decimal rendering
|
||||
of an `Int`. Backs `Show Int` in the prelude.
|
||||
- `bool_to_str : (borrow Bool) -> Str` (iter 24.1) — `"true"`/`"false"`.
|
||||
Backs `Show Bool` in the prelude.
|
||||
- `float_to_str : (borrow Float) -> Str` (iter 24.1, type-installed;
|
||||
codegen ships in a future iter per roadmap). Will back `Show Float`.
|
||||
- `str_clone : (borrow Str) -> Str` (iter 24.1) — allocates a fresh
|
||||
heap-Str copy of the input's bytes. Backs `Show Str` in the prelude.
|
||||
- `str_concat : (borrow Str, borrow Str) -> Str` (iter str-concat,
|
||||
2026-05-13) — combines two `Str` values into a single owned `Str`.
|
||||
General-purpose; commonly used in Show bodies for labelled output
|
||||
(`(app str_concat "label=" (app int_to_str x))`).
|
||||
|
||||
The four Show-backers above are not directly observable to the
|
||||
LLM-author writing a `Show <T>` instance — the prelude's instance
|
||||
bodies dispatch into them. `str_concat` IS directly observable
|
||||
because the LLM-author calls it explicitly when authoring an
|
||||
instance body that wants to combine fragments.
|
||||
|
||||
Primitive output goes through `io/print_int` / `io/print_bool` /
|
||||
`io/print_str` directly.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user