iter 23.2.1: runtime/str.c with ail_str_eq + unconditional link

This commit is contained in:
2026-05-10 21:49:45 +02:00
parent c6c3c21013
commit cc2d6944c1
2 changed files with 91 additions and 0 deletions
+65
View File
@@ -1970,6 +1970,37 @@ fn locate_rc_runtime() -> Result<PathBuf> {
)
}
/// Locate the workspace-root `runtime/str.c` file relative to the
/// `ail` binary, mirroring [`locate_rc_runtime`]. Iter 23.2:
/// `runtime/str.c` houses Str-runtime primitives (`ail_str_eq`,
/// later `ail_str_compare`) and is linked unconditionally for every
/// alloc strategy — `@ail_str_eq` is declared in the emitted IR
/// header (codegen) without an alloc-strategy guard, so the symbol
/// must always be resolvable at link time.
fn locate_str_runtime() -> Result<PathBuf> {
let candidates = [
std::env::current_exe().ok(),
std::env::current_dir().ok(),
];
for start in candidates.iter().flatten() {
let mut cur: &Path = start.as_path();
loop {
let candidate = cur.join("runtime").join("str.c");
if candidate.exists() {
return Ok(candidate);
}
match cur.parent() {
Some(p) => cur = p,
None => break,
}
}
}
anyhow::bail!(
"could not locate `runtime/str.c` (linked unconditionally). \
Run `ail` from inside the AILang workspace."
)
}
/// Iter 9b: shared build helper for `Cmd::Build` and `Cmd::Run`.
/// Loads the workspace, runs the typechecker, emits IR, and links via
/// clang. On typecheck failure, prints diagnostics to stderr and exits
@@ -2060,6 +2091,29 @@ fn build_to(
let out_bin = out.unwrap_or_else(|| Path::new(".").join(&ws.entry).with_extension(""));
let mut clang = std::process::Command::new("clang");
clang.arg(opt).arg("-o").arg(&out_bin).arg(&ll_path);
// Iter 23.2: compile and link `runtime/str.c` unconditionally —
// `@ail_str_eq` is emitted in the IR header without an alloc-strategy
// guard (it backs the prelude `eq__Str` mono symbol). The .o is
// cached at <tmpdir>/str.o per build invocation; if a program never
// monomorphises `eq__Str`, clang -O2 dead-strips the symbol so the
// linker overhead is fixed and negligible.
let str_src = locate_str_runtime()?;
let str_obj = tmpdir.join("str.o");
let cstatus = std::process::Command::new("clang")
.arg("-O2")
.arg("-c")
.arg(&str_src)
.arg("-o")
.arg(&str_obj)
.status()
.context("compiling runtime/str.c")?;
if !cstatus.success() {
anyhow::bail!(
"clang failed compiling str.c (status {})",
cstatus
);
}
clang.arg(&str_obj);
match alloc {
ailang_codegen::AllocStrategy::Gc => {
// Boehm conservative GC (Decision 9 / Iter 14f). The lowered
@@ -2139,6 +2193,17 @@ fn build_to(
mod tests {
use super::compose_merge_prose_prompt;
#[test]
fn locate_str_runtime_finds_workspace_str_c() {
let p = super::locate_str_runtime().expect("locate runtime/str.c");
assert!(
p.ends_with("runtime/str.c"),
"expected path ending in runtime/str.c, got {}",
p.display()
);
assert!(p.exists(), "located path must exist on disk: {}", p.display());
}
/// The composed prompt must contain all three payloads
/// byte-for-byte — the LLM has to see exactly what the user wrote,
/// the original module's Form-A bytes, and the embedded spec, with
+26
View File
@@ -0,0 +1,26 @@
/* AILang Str runtime primitives.
*
* Provides ABI-stable string operations against the existing
* constant-Str layout (NUL-terminated byte buffer, passed as
* `ptr` in LLVM IR). Lives separately from runtime/rc.c so that
* a future heap-Str ABI milestone can swap the implementations
* without touching the RC runtime.
*
* Linked unconditionally by `ail build` (Gc, Bump, Rc) so that
* any program that monomorphises `eq__Str` (auto-loaded from
* the prelude) finds the symbol. Programs that never call
* `eq` on Str leave @ail_str_eq as a dead-stripped no-op at
* link time under clang -O2.
*/
#include <stdbool.h>
#include <string.h>
/* Byte-equality on NUL-terminated strings. Mirrors strcmp(...) == 0
* but exposed under a stable AILang-namespaced ABI so the codegen
* caller does not assume libc's strcmp shape. Heap-Str ABI milestone
* will replace the body with length-prefixed comparison without
* changing the signature. */
bool ail_str_eq(const char *a, const char *b) {
return strcmp(a, b) == 0;
}