diff --git a/bench/orchestrator-stats/2026-05-21-iter-bugfix-print-str-fputs.json b/bench/orchestrator-stats/2026-05-21-iter-bugfix-print-str-fputs.json new file mode 100644 index 0000000..a851bc0 --- /dev/null +++ b/bench/orchestrator-stats/2026-05-21-iter-bugfix-print-str-fputs.json @@ -0,0 +1,12 @@ +{ + "iter_id": "bugfix-print-str-fputs", + "date": "2026-05-21", + "mode": "mini", + "outcome": "DONE", + "tasks_total": 1, + "tasks_completed": 1, + "reloops_per_task": { "1": 0 }, + "review_loops_spec": 0, + "review_loops_quality": 0, + "blocked_reason": null +} diff --git a/crates/ail/tests/e2e.rs b/crates/ail/tests/e2e.rs index 19e8535..153ab8d 100644 --- a/crates/ail/tests/e2e.rs +++ b/crates/ail/tests/e2e.rs @@ -2580,7 +2580,8 @@ fn ail_run_accepts_ail_source_with_same_stdout_as_ail_json() { /// wired in hs.4 — exercises checker (signature install), codegen /// (lower_app arm + IR-header declare + is_static_callee whitelist), /// runtime (str.c's `ailang_int_to_str` allocates a heap-Str slab, -/// @puts reads from the bytes pointer at offset 8), and the +/// @fputs reads from the bytes pointer at offset 8 with @stdout as +/// the FILE*), and the /// unconditional `runtime/rc.c` link (str.c's weak extern of /// `ailang_rc_alloc` resolves to the strong rc.c definition under /// every alloc strategy after the hs.4 hoist). diff --git a/crates/ail/tests/print_no_leak_pin.rs b/crates/ail/tests/print_no_leak_pin.rs index 2e202eb..62fa822 100644 --- a/crates/ail/tests/print_no_leak_pin.rs +++ b/crates/ail/tests/print_no_leak_pin.rs @@ -16,7 +16,7 @@ //! `ailang_rc_stats: allocs=1 frees=0 live=1`. The IR for the post- //! mono `ail_prelude_print__Int` body contains a `call ptr //! @ail_prelude_show__Int(...)` followed by `getelementptr +8` + -//! `@puts` + `ret i8 0` — there is no `ailang_rc_dec` on the +//! `@fputs` + `ret i8 0` — there is no `ailang_rc_dec` on the //! show-result before return. Root cause sits at codegen's //! `is_rc_heap_allocated` App-arm: it reads the callee's //! `Type::Fn.ret_mode` via `synth_callee_ret_mode` and requires diff --git a/crates/ail/tests/show_print_e2e.rs b/crates/ail/tests/show_print_e2e.rs index 7f631a8..6265465 100644 --- a/crates/ail/tests/show_print_e2e.rs +++ b/crates/ail/tests/show_print_e2e.rs @@ -56,12 +56,15 @@ fn build_and_run(fixture: &str) -> String { #[test] fn print_primitives_smoke_runs_end_to_end() { let stdout = build_and_run("show_print_smoke.ail"); - // print 42 → "42\n" (int_to_str + puts) - // print true → "true\n" (bool_to_str + puts) - // print "hello" → "hello\n" (str_clone + puts) - // print 3.14 → "3.14\n" (float_to_str / libc %g + puts) - // puts emits each Str + a trailing newline; trimmed output is - // "42\ntrue\nhello\n3.14". + // Post Gitea #29 (io/print_str → @fputs), the print path no + // longer appends an implicit newline. The fixture now emits + // each value with an explicit `(do io/print_str "\n")` after + // the print, so the raw stdout is "42\ntrue\nhello\n3.14\n" + // and `.trim()` lands at "42\ntrue\nhello\n3.14". + // print 42 → "42" (int_to_str), then explicit "\n" + // print true → "true" (bool_to_str), then explicit "\n" + // print "hello" → "hello" (str_clone), then explicit "\n" + // print 3.14 → "3.14" (float_to_str / libc %g) assert_eq!(stdout, "42\ntrue\nhello\n3.14", "got: {stdout:?}"); } diff --git a/crates/ail/tests/snapshots/hello.ll b/crates/ail/tests/snapshots/hello.ll index fe66a4c..25001ec 100644 --- a/crates/ail/tests/snapshots/hello.ll +++ b/crates/ail/tests/snapshots/hello.ll @@ -5,7 +5,8 @@ target triple = "" @.str_hello_str_0 = private unnamed_addr constant <{ i64, [15 x i8] }> <{ i64 14, [15 x i8] c"Hello, AILang.\00" }>, align 8 declare i32 @printf(ptr, ...) -declare i32 @puts(ptr) +declare i32 @fputs(ptr, ptr) +@stdout = external global ptr declare ptr @ailang_rc_alloc(i64) declare void @ailang_rc_inc(ptr) declare void @ailang_rc_dec(ptr) @@ -33,7 +34,8 @@ declare i64 @llvm.fptosi.sat.i64.f64(double) define i8 @ail_hello_main() { entry: %v1 = getelementptr inbounds i8, ptr getelementptr inbounds (<{ i64, [15 x i8] }>, ptr @.str_hello_str_0, i32 0, i32 0), i64 8 - call i32 @puts(ptr %v1) + %v2 = load ptr, ptr @stdout, align 8 + call i32 @fputs(ptr %v1, ptr %v2) ret i8 0 } diff --git a/crates/ail/tests/snapshots/list.ll b/crates/ail/tests/snapshots/list.ll index ca528d2..73cf0da 100644 --- a/crates/ail/tests/snapshots/list.ll +++ b/crates/ail/tests/snapshots/list.ll @@ -3,7 +3,8 @@ source_filename = "list.ail" target triple = "" declare i32 @printf(ptr, ...) -declare i32 @puts(ptr) +declare i32 @fputs(ptr, ptr) +@stdout = external global ptr declare ptr @ailang_rc_alloc(i64) declare void @ailang_rc_inc(ptr) declare void @ailang_rc_dec(ptr) @@ -240,7 +241,8 @@ define i8 @ail_prelude_print__Int(i64 %arg_x) { entry: %v1 = call ptr @ail_prelude_show__Int(i64 %arg_x) %v2 = getelementptr inbounds i8, ptr %v1, i64 8 - call i32 @puts(ptr %v2) + %v3 = load ptr, ptr @stdout, align 8 + call i32 @fputs(ptr %v2, ptr %v3) call void @ailang_rc_dec(ptr %v1) ret i8 0 } diff --git a/crates/ail/tests/snapshots/max3.ll b/crates/ail/tests/snapshots/max3.ll index d7882c5..d020f5d 100644 --- a/crates/ail/tests/snapshots/max3.ll +++ b/crates/ail/tests/snapshots/max3.ll @@ -3,7 +3,8 @@ source_filename = "max3.ail" target triple = "" declare i32 @printf(ptr, ...) -declare i32 @puts(ptr) +declare i32 @fputs(ptr, ptr) +@stdout = external global ptr declare ptr @ailang_rc_alloc(i64) declare void @ailang_rc_inc(ptr) declare void @ailang_rc_dec(ptr) @@ -228,7 +229,8 @@ define i8 @ail_prelude_print__Int(i64 %arg_x) { entry: %v1 = call ptr @ail_prelude_show__Int(i64 %arg_x) %v2 = getelementptr inbounds i8, ptr %v1, i64 8 - call i32 @puts(ptr %v2) + %v3 = load ptr, ptr @stdout, align 8 + call i32 @fputs(ptr %v2, ptr %v3) call void @ailang_rc_dec(ptr %v1) ret i8 0 } diff --git a/crates/ail/tests/snapshots/sum.ll b/crates/ail/tests/snapshots/sum.ll index 6518763..297620b 100644 --- a/crates/ail/tests/snapshots/sum.ll +++ b/crates/ail/tests/snapshots/sum.ll @@ -3,7 +3,8 @@ source_filename = "sum.ail" target triple = "" declare i32 @printf(ptr, ...) -declare i32 @puts(ptr) +declare i32 @fputs(ptr, ptr) +@stdout = external global ptr declare ptr @ailang_rc_alloc(i64) declare void @ailang_rc_inc(ptr) declare void @ailang_rc_dec(ptr) @@ -132,7 +133,8 @@ define i8 @ail_prelude_print__Int(i64 %arg_x) { entry: %v1 = call ptr @ail_prelude_show__Int(i64 %arg_x) %v2 = getelementptr inbounds i8, ptr %v1, i64 8 - call i32 @puts(ptr %v2) + %v3 = load ptr, ptr @stdout, align 8 + call i32 @fputs(ptr %v2, ptr %v3) call void @ailang_rc_dec(ptr %v1) ret i8 0 } diff --git a/crates/ail/tests/snapshots/ws_main.ll b/crates/ail/tests/snapshots/ws_main.ll index 84c64f4..b30876c 100644 --- a/crates/ail/tests/snapshots/ws_main.ll +++ b/crates/ail/tests/snapshots/ws_main.ll @@ -3,7 +3,8 @@ source_filename = "ws_main.ail" target triple = "" declare i32 @printf(ptr, ...) -declare i32 @puts(ptr) +declare i32 @fputs(ptr, ptr) +@stdout = external global ptr declare ptr @ailang_rc_alloc(i64) declare void @ailang_rc_inc(ptr) declare void @ailang_rc_dec(ptr) @@ -119,7 +120,8 @@ define i8 @ail_prelude_print__Int(i64 %arg_x) { entry: %v1 = call ptr @ail_prelude_show__Int(i64 %arg_x) %v2 = getelementptr inbounds i8, ptr %v1, i64 8 - call i32 @puts(ptr %v2) + %v3 = load ptr, ptr @stdout, align 8 + call i32 @fputs(ptr %v2, ptr %v3) call void @ailang_rc_dec(ptr %v1) ret i8 0 } diff --git a/crates/ailang-codegen/src/lib.rs b/crates/ailang-codegen/src/lib.rs index fdc7744..a9b9646 100644 --- a/crates/ailang-codegen/src/lib.rs +++ b/crates/ailang-codegen/src/lib.rs @@ -519,7 +519,15 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - } out.push_str("declare i32 @printf(ptr, ...)\n"); - out.push_str("declare i32 @puts(ptr)\n"); + // `io/print_str` lowers to `fputs(s, stdout)` so the runtime emits + // exactly the bytes of `s` with NO implicit trailing newline. The + // earlier `@puts(ptr)` shape de-facto turned `io/print_str` into + // `println_str` (caught empirically in the cross-model-authoring + // naming-A/B run, Gitea #29). `@stdout` is a libc global FILE*; + // POSIX exposes it as an `extern FILE *stdout` symbol, which LLVM + // IR sees as an opaque pointer to a pointer. + out.push_str("declare i32 @fputs(ptr, ptr)\n"); + out.push_str("@stdout = external global ptr\n"); // The allocator declaration name follows `alloc`. `Rc` declares // `@ailang_rc_alloc` (canonical); `Bump` declares `@bump_malloc` // (raw-alloc bench-floor), supplied by `runtime/bump.c`. @@ -2720,8 +2728,8 @@ impl<'a> Emitter<'a> { fn lower_effect_op(&mut self, op: &str, args: &[Term], tail: bool) -> Result<(String, String)> { // `musttail` requires identical caller/callee // prototypes (same return type, same param types). The MVP's - // runtime print helpers (`printf`, `puts`) return `i32`, but the - // AILang fn enclosing a `tail-do io/print_*` returns `Unit` + // runtime print helpers (`printf`, `fputs`) return `i32`, but + // the AILang fn enclosing a `tail-do io/print_*` returns `Unit` // (`i8`). `musttail` would be rejected by the LLVM verifier. // We therefore use the `tail` keyword (LLVM IR optimisation // hint, NOT a guarantee) for `tail: true` do-ops. The optimiser @@ -2744,14 +2752,23 @@ impl<'a> Emitter<'a> { )); } // `Str` values now flow as a pointer to the - // `len`-field of the packed-struct slab; @puts needs - // the bytes pointer 8 bytes further on. + // `len`-field of the packed-struct slab; @fputs needs + // the bytes pointer 8 bytes further on. Loading + // `@stdout` is mandatory: it is `extern FILE *stdout` + // in libc, an opaque pointer-to-pointer at the IR + // level, and we need the inner pointer (the FILE*) as + // the second arg to `fputs`. let bytes = self.fresh_ssa(); self.body.push_str(&format!( " {bytes} = getelementptr inbounds i8, ptr {v}, i64 8\n" )); - self.body - .push_str(&format!(" {call_kw} i32 @puts(ptr {bytes})\n")); + let fp = self.fresh_ssa(); + self.body.push_str(&format!( + " {fp} = load ptr, ptr @stdout, align 8\n" + )); + self.body.push_str(&format!( + " {call_kw} i32 @fputs(ptr {bytes}, ptr {fp})\n" + )); if tail { self.body.push_str(" ret i8 0\n"); self.block_terminated = true; @@ -4299,10 +4316,14 @@ mod tests { /// after the layout migration, the `io/print_str` /// path must `getelementptr i8` +8 onto the IR-Str pointer before - /// passing it to `@puts`, so `@puts` receives the bytes pointer - /// (skipping the `len` field) and produces correct output. + /// passing it to `@fputs`, so `@fputs` receives the bytes pointer + /// (skipping the `len` field) and produces correct output. Post + /// Gitea #29 the call lowers to `@fputs(ptr bytes, ptr fp)` where + /// `fp` is a `load ptr, ptr @stdout` — `@fputs` (unlike `@puts`) + /// does NOT append a trailing newline, so `io/print_str` is + /// byte-faithful. #[test] - fn print_str_calls_puts_with_bytes_pointer() { + fn print_str_calls_fputs_with_bytes_pointer_and_stdout() { let m = Module { schema: SCHEMA.into(), name: "t".into(), @@ -4330,11 +4351,30 @@ mod tests { let ir = emit_ir(&m).unwrap(); let body_idx = ir.find("define i8 @ail_t_main").expect("main body"); let body = &ir[body_idx..]; - let puts_idx = body.find("@puts(").expect("@puts call present"); - let before_puts = &body[..puts_idx]; + let fputs_idx = body.find("@fputs(").expect("@fputs call present"); + let before_fputs = &body[..fputs_idx]; assert!( - before_puts.contains("getelementptr inbounds i8, ptr ") && before_puts.contains(", i64 8"), - "expected `getelementptr inbounds i8, ptr , i64 8` before @puts call; ir body was:\n{body}" + before_fputs.contains("getelementptr inbounds i8, ptr ") && before_fputs.contains(", i64 8"), + "expected `getelementptr inbounds i8, ptr , i64 8` before @fputs call; ir body was:\n{body}" + ); + assert!( + before_fputs.contains("load ptr, ptr @stdout"), + "expected `load ptr, ptr @stdout` before @fputs call; ir body was:\n{body}" + ); + // Module preamble must declare both: fputs and the @stdout extern. + assert!( + ir.contains("declare i32 @fputs(ptr, ptr)"), + "expected `declare i32 @fputs(ptr, ptr)` in module preamble; ir was:\n{ir}" + ); + assert!( + ir.contains("@stdout = external global ptr"), + "expected `@stdout = external global ptr` in module preamble; ir was:\n{ir}" + ); + // After the swap there must be no `@puts` call left anywhere + // in the emitted IR — the print path is the only consumer. + assert!( + !ir.contains("@puts("), + "no @puts call should remain after the fputs swap; ir was:\n{ir}" ); } diff --git a/crates/ailang-core/tests/hash_pin.rs b/crates/ailang-core/tests/hash_pin.rs index 19207ca..14b4e31 100644 --- a/crates/ailang-core/tests/hash_pin.rs +++ b/crates/ailang-core/tests/hash_pin.rs @@ -245,14 +245,17 @@ fn ct4_migrated_fixtures_have_canonical_form_hashes() { let ord_mod = ailang_surface::load_module(&examples.join("ordering_match.ail")) .expect("examples/ordering_match.ail loads"); let main_def = ord_mod.defs.iter().find(|d| d.name() == "main").unwrap(); - // The hash reflects a corpus migration: `ordering_match.ail`'s - // body moved from `(do io/print_int x)` to `(app print x)` when - // the per-type print effect-ops were retired in favour of the - // polymorphic `print` helper. + // The hash reflects two successive corpus migrations: + // (1) the per-type-print-retirement that moved the body from + // `(do io/print_int x)` to `(app print x)`; + // (2) the io/print_str byte-faithful-print fix (Gitea #29) that + // wrapped the `(app print x)` with an explicit + // `(seq ... (do io/print_str "\n"))` newline emission, because + // io/print_str no longer adds a trailing newline. assert_eq!( def_hash(main_def), - "b65a7f834703ffb4", - "ordering_match::main canonical hash must match captured post-per-type-print-retirement value" + "8ed47b4062ce00f5", + "ordering_match::main canonical hash must match captured post-fputs-swap value" ); let dup_a_mod = ailang_surface::load_module(&examples.join("test_22b1_dup_a.ail")) diff --git a/design/contracts/str-abi.md b/design/contracts/str-abi.md index b0d0665..abb6166 100644 --- a/design/contracts/str-abi.md +++ b/design/contracts/str-abi.md @@ -60,11 +60,14 @@ Two realisations share this consumer ABI: | static-Str | string literals (`@.str_*` LLVM globals) | none | `.rodata`, packed-struct `<{ i64, [N+1 x i8] }>` | | heap-Str | runtime allocations (`int_to_str`, `float_to_str`, ...) | yes, at `payload - 8` | `malloc`'d via `ailang_rc_alloc(8 + len + 1)` | -Every consumer (`@puts`, `@strcmp`, `@ail_str_eq`, +Every consumer (`@fputs`, `@strcmp`, `@ail_str_eq`, `@ail_str_compare`) GEPs `+8` from the Str pointer to reach the bytes, regardless of realisation. The byte-comparison semantics are inherited from libc `strcmp` — locale-independent, NUL- -terminated. +terminated. The print path uses `@fputs(bytes, @stdout)` so that +`(do io/print_str s)` writes exactly the bytes of `s` with no +implicit trailing newline (Gitea #29; the earlier `@puts` lowering +appended a newline per call). The heap-Str realisation participates in standard RC (see [memory model](memory-model.md)): the diff --git a/examples/bool_to_str_drop_rc.ail b/examples/bool_to_str_drop_rc.ail index 2febb14..b26c10b 100644 --- a/examples/bool_to_str_drop_rc.ail +++ b/examples/bool_to_str_drop_rc.ail @@ -3,4 +3,4 @@ (doc "Iter 24.1: pin that `bool_to_str` participates in RC discipline. Bind the heap-Str result to `s`, consume `s` once in `io/print_str`, let the binder drop at scope close. Under --alloc=rc with AILANG_RC_STATS=1 the atexit summary must report allocs == 1 && frees == 1 && live == 0 — the heap-Str slab allocated by str_alloc inside ailang_bool_to_str rides the same rc_header + ailang_rc_dec path as int_to_str.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let s (app bool_to_str true) (do io/print_str s))))) + (body (let s (app bool_to_str true) (seq (do io/print_str s) (do io/print_str "\n")))))) diff --git a/examples/bool_to_str_smoke_false.ail b/examples/bool_to_str_smoke_false.ail index 69df911..980057e 100644 --- a/examples/bool_to_str_smoke_false.ail +++ b/examples/bool_to_str_smoke_false.ail @@ -3,4 +3,4 @@ (doc "Iter 24.1: stdout-smoke for the false branch of bool_to_str. Stdout must be `false\\n` (puts adds the newline). Companion of bool_to_str_drop_rc.ail.json (which covers the true branch and the RC-stats invariant).") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let s (app bool_to_str false) (do io/print_str s))))) + (body (let s (app bool_to_str false) (seq (do io/print_str s) (do io/print_str "\n")))))) diff --git a/examples/borrow_own_demo.ail b/examples/borrow_own_demo.ail index 67579c7..4b8dbbf 100644 --- a/examples/borrow_own_demo.ail +++ b/examples/borrow_own_demo.ail @@ -60,5 +60,5 @@ (term-ctor List Cons 3 (term-ctor List Nil)))) (seq - (app print (app list_length xs)) - (app print (app sum_list xs))))))) + (seq (app print (app list_length xs)) (do io/print_str "\n")) + (seq (app print (app sum_list xs)) (do io/print_str "\n"))))))) diff --git a/examples/compare_primitives_smoke.ail b/examples/compare_primitives_smoke.ail index decc247..7fb9852 100644 --- a/examples/compare_primitives_smoke.ail +++ b/examples/compare_primitives_smoke.ail @@ -2,31 +2,31 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (match (app compare 1 2) + (body (seq (seq (app print (match (app compare 1 2) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare 1 1) + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare 1 1) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare 2 1) + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare 2 1) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare false true) + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare false true) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare false false) + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare false false) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare true false) + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare true false) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare "a" "b") + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare "a" "b") (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (seq (app print (match (app compare "a" "a") + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (seq (app print (match (app compare "a" "a") (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3))) (app print (match (app compare "b" "a") + (case (pat-ctor GT) 3))) (do io/print_str "\n")) (seq (app print (match (app compare "b" "a") (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3)))))))))))))) + (case (pat-ctor GT) 3))) (do io/print_str "\n"))))))))))))) diff --git a/examples/eq_demo.ail b/examples/eq_demo.ail index 0c0c51e..c661997 100644 --- a/examples/eq_demo.ail +++ b/examples/eq_demo.ail @@ -47,13 +47,13 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app eq 5 5)) - (seq (app print (app eq 5 6)) - (seq (app print (app eq true false)) - (seq (app print (app eq true true)) - (seq (app print (app eq "hi" "hi")) - (seq (app print (app eq "hi" "ho")) - (seq (app print (app eq (lit-unit) (lit-unit))) - (seq (app print (app classify_str "hi")) - (seq (app print (app classify_str "ho")) - (app print (app classify_str "??")))))))))))))) + (seq (seq (app print (app eq 5 5)) (do io/print_str "\n")) + (seq (seq (app print (app eq 5 6)) (do io/print_str "\n")) + (seq (seq (app print (app eq true false)) (do io/print_str "\n")) + (seq (seq (app print (app eq true true)) (do io/print_str "\n")) + (seq (seq (app print (app eq "hi" "hi")) (do io/print_str "\n")) + (seq (seq (app print (app eq "hi" "ho")) (do io/print_str "\n")) + (seq (seq (app print (app eq (lit-unit) (lit-unit))) (do io/print_str "\n")) + (seq (seq (app print (app classify_str "hi")) (do io/print_str "\n")) + (seq (seq (app print (app classify_str "ho")) (do io/print_str "\n")) + (seq (app print (app classify_str "??")) (do io/print_str "\n")))))))))))))) diff --git a/examples/eq_ord_polymorphic.ail b/examples/eq_ord_polymorphic.ail index 4cde39f..75201b8 100644 --- a/examples/eq_ord_polymorphic.ail +++ b/examples/eq_ord_polymorphic.ail @@ -7,4 +7,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (if (app at_most 3 5) 1 0)) (seq (app print (if (app at_most true false) 1 0)) (app print (if (app at_most "abc" "abd") 1 0))))))) + (body (seq (seq (app print (if (app at_most 3 5) 1 0)) (do io/print_str "\n")) (seq (seq (app print (if (app at_most true false) 1 0)) (do io/print_str "\n")) (seq (app print (if (app at_most "abc" "abd") 1 0)) (do io/print_str "\n"))))))) diff --git a/examples/eq_ord_user_adt.ail b/examples/eq_ord_user_adt.ail index 78c8e80..ee14a6a 100644 --- a/examples/eq_ord_user_adt.ail +++ b/examples/eq_ord_user_adt.ail @@ -21,4 +21,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (if (app eq (term-ctor IntBox MkIntBox 3) (term-ctor IntBox MkIntBox 3)) 1 0)) (app print (if (app eq (term-ctor IntBox MkIntBox 3) (term-ctor IntBox MkIntBox 5)) 1 0)))))) + (body (seq (seq (app print (if (app eq (term-ctor IntBox MkIntBox 3) (term-ctor IntBox MkIntBox 3)) 1 0)) (do io/print_str "\n")) (seq (app print (if (app eq (term-ctor IntBox MkIntBox 3) (term-ctor IntBox MkIntBox 5)) 1 0)) (do io/print_str "\n")))))) diff --git a/examples/eq_primitives_smoke.ail b/examples/eq_primitives_smoke.ail index f9db923..b176594 100644 --- a/examples/eq_primitives_smoke.ail +++ b/examples/eq_primitives_smoke.ail @@ -7,4 +7,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (app to_int (app eq 7 7))) (seq (app print (app to_int (app eq 7 8))) (seq (app print (app to_int (app eq true true))) (seq (app print (app to_int (app eq true false))) (seq (app print (app to_int (app eq "hello" "hello"))) (app print (app to_int (app eq "hello" "world"))))))))))) + (body (seq (seq (app print (app to_int (app eq 7 7))) (do io/print_str "\n")) (seq (seq (app print (app to_int (app eq 7 8))) (do io/print_str "\n")) (seq (seq (app print (app to_int (app eq true true))) (do io/print_str "\n")) (seq (seq (app print (app to_int (app eq true false))) (do io/print_str "\n")) (seq (seq (app print (app to_int (app eq "hello" "hello"))) (do io/print_str "\n")) (seq (app print (app to_int (app eq "hello" "world"))) (do io/print_str "\n")))))))))) diff --git a/examples/eq_user_adt_smoke.ail b/examples/eq_user_adt_smoke.ail index 5ff108b..67bf5ee 100644 --- a/examples/eq_user_adt_smoke.ail +++ b/examples/eq_user_adt_smoke.ail @@ -23,6 +23,6 @@ (let p1 (term-ctor Point Point 1 2) (let p2 (term-ctor Point Point 1 2) (let p3 (term-ctor Point Point 1 3) - (seq (app print (app eq (lit-unit) (lit-unit))) - (seq (app print (app eq p1 p2)) - (app print (app eq p1 p3)))))))))) + (seq (seq (app print (app eq (lit-unit) (lit-unit))) (do io/print_str "\n")) + (seq (seq (app print (app eq p1 p2)) (do io/print_str "\n")) + (seq (app print (app eq p1 p3)) (do io/print_str "\n")))))))))) diff --git a/examples/escape_local_demo.ail b/examples/escape_local_demo.ail index 05abe40..a766135 100644 --- a/examples/escape_local_demo.ail +++ b/examples/escape_local_demo.ail @@ -51,7 +51,7 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app peek 0)) - (seq (app print (app peek 123)) - (seq (app print (app count 5)) - (app print (app count 0)))))))) + (seq (seq (app print (app peek 0)) (do io/print_str "\n")) + (seq (seq (app print (app peek 123)) (do io/print_str "\n")) + (seq (seq (app print (app count 5)) (do io/print_str "\n")) + (seq (app print (app count 0)) (do io/print_str "\n")))))))) diff --git a/examples/float_compare_smoke.ail b/examples/float_compare_smoke.ail index 30e49b3..e5be927 100644 --- a/examples/float_compare_smoke.ail +++ b/examples/float_compare_smoke.ail @@ -4,6 +4,6 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app float_eq 1.5 1.5)) - (seq (app print (app float_lt 1.0 2.0)) - (app print (app float_eq 1.0 0.0))))))) + (seq (seq (app print (app float_eq 1.5 1.5)) (do io/print_str "\n")) + (seq (seq (app print (app float_lt 1.0 2.0)) (do io/print_str "\n")) + (seq (app print (app float_eq 1.0 0.0)) (do io/print_str "\n"))))))) diff --git a/examples/float_to_str_smoke.ail b/examples/float_to_str_smoke.ail index 33aecdc..af4337c 100644 --- a/examples/float_to_str_smoke.ail +++ b/examples/float_to_str_smoke.ail @@ -3,4 +3,4 @@ (doc "Iter hs.4: smoke pin for the float_to_str heap-Str builtin. `do io/print_str(float_to_str(3.5))` builds through the whole pipeline (checker resolves float_to_str:(Float)->Str, codegen lowers to call ptr @ailang_float_to_str(double 3.5), runtime/str.c's ailang_float_to_str snprintfs into a heap-Str slab via libc %g plus a `.0`-fallback for finite whole-valued doubles whose %g output lacks `.`/`e`/`E` (Gitea #7), io/print_str's @puts prints from the bytes pointer at offset 8). The exact rendering of 3.5 is libc-target-dependent; on the dev target's clang+glibc with default C locale it is the three bytes `3.5` (the fallback does not fire because `.` is already present). Single-value smoke; NaN / +Inf / -Inf edge cases are deferred per the hs.4 plan.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (do io/print_str (app float_to_str 3.5))))) + (body (seq (do io/print_str (app float_to_str 3.5)) (do io/print_str "\n"))))) diff --git a/examples/floats.ail b/examples/floats.ail index ea3887c..0eddfd0 100644 --- a/examples/floats.ail +++ b/examples/floats.ail @@ -2,4 +2,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (app + 1.5 2.5)) (seq (app print (app int_to_float 42)) (app print (app neg 1.5))))))) + (body (seq (seq (app print (app + 1.5 2.5)) (do io/print_str "\n")) (seq (seq (app print (app int_to_float 42)) (do io/print_str "\n")) (seq (app print (app neg 1.5)) (do io/print_str "\n"))))))) diff --git a/examples/heap_str_repeated_print_borrow.ail b/examples/heap_str_repeated_print_borrow.ail index 551cdd3..789a5db 100644 --- a/examples/heap_str_repeated_print_borrow.ail +++ b/examples/heap_str_repeated_print_borrow.ail @@ -3,4 +3,4 @@ (doc "Iter eob.1: heap-Str passed to io/print_str twice in sequence. Under the new rule (Term::Do args = Borrow), the second call no longer triggers use-after-consume; the linearity check accepts the program. Under --alloc=rc + AILANG_RC_STATS=1 the slab allocates once and is freed once at scope close (allocs == 1, frees == 1, live == 0).") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let s (app int_to_str 42) (seq (do io/print_str s) (do io/print_str s)))))) + (body (let s (app int_to_str 42) (seq (seq (do io/print_str s) (do io/print_str "\n")) (seq (do io/print_str s) (do io/print_str "\n"))))))) diff --git a/examples/int_to_print_int_borrow.ail b/examples/int_to_print_int_borrow.ail index e9be9cc..4e5635a 100644 --- a/examples/int_to_print_int_borrow.ail +++ b/examples/int_to_print_int_borrow.ail @@ -3,4 +3,4 @@ (doc "Iter eob.1 / rpe.1: primitive Int passed to the polymorphic `print` helper (Show Int). The rule that Term::Do args are Borrow has no observable RC effect here because Int is unboxed; the test pins that no spurious bookkeeping appears (allocs == 0, frees == 0, live == 0).") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let n 7 (app print n))))) + (body (let n 7 (seq (app print n) (do io/print_str "\n")))))) diff --git a/examples/int_to_str_drop_rc.ail b/examples/int_to_str_drop_rc.ail index 6752f4a..e6eda35 100644 --- a/examples/int_to_str_drop_rc.ail +++ b/examples/int_to_str_drop_rc.ail @@ -3,4 +3,4 @@ (doc "Iter hs.4: pin that `int_to_str` participates in RC discipline. Bind the heap-Str result to `s`, consume `s` once in `io/print_str`, let the binder drop at scope close. Under --alloc=rc with AILANG_RC_STATS=1 the atexit summary must report allocs == frees && live == 0 — the heap-Str slab allocated by str_alloc inside ailang_int_to_str rides the same rc_header + ailang_rc_dec path as any other RC value.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let s (app int_to_str 42) (do io/print_str s))))) + (body (let s (app int_to_str 42) (seq (do io/print_str s) (do io/print_str "\n")))))) diff --git a/examples/int_to_str_smoke.ail b/examples/int_to_str_smoke.ail index 0638a49..140e41d 100644 --- a/examples/int_to_str_smoke.ail +++ b/examples/int_to_str_smoke.ail @@ -3,4 +3,4 @@ (doc "Iter hs.4: smoke pin for the int_to_str heap-Str builtin. `do io/print_str(int_to_str(42))` builds through the whole pipeline (checker resolves int_to_str:(Int)->Str, codegen lowers to call ptr @ailang_int_to_str(i64 42), runtime/str.c's ailang_int_to_str snprintfs into a heap-Str slab, io/print_str's @puts prints from the bytes pointer at offset 8). Single-value smoke; the spec's broader edge-case sweep (0, -1, i64::MAX, i64::MIN) is deferred to a follow-up tidy iter per the hs.4 plan.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (do io/print_str (app int_to_str 42))))) + (body (seq (do io/print_str (app int_to_str 42)) (do io/print_str "\n"))))) diff --git a/examples/list_map.ail b/examples/list_map.ail index 17ad346..6aa40ce 100644 --- a/examples/list_map.ail +++ b/examples/list_map.ail @@ -16,7 +16,7 @@ (params xs) (body (match xs (case (pat-ctor Nil) (lit-unit)) - (case (pat-ctor Cons h t) (seq (app print h) (app print_list t)))))) + (case (pat-ctor Cons h t) (seq (seq (app print h) (do io/print_str "\n")) (app print_list t)))))) (fn main (doc "Build [1,2,3], double each, print result.") (type (fn-type (params) (ret (con Unit)) (effects IO))) diff --git a/examples/list_map_poly.ail b/examples/list_map_poly.ail index 60eb6ba..eb413ec 100644 --- a/examples/list_map_poly.ail +++ b/examples/list_map_poly.ail @@ -46,7 +46,7 @@ (lit-unit)) (case (pat-ctor Cons h t) (seq - (app print h) + (seq (app print h) (do io/print_str "\n")) (tail-app print_list t)))))) (fn main diff --git a/examples/lit_pat.ail b/examples/lit_pat.ail index 0f6188b..d43c6e0 100644 --- a/examples/lit_pat.ail +++ b/examples/lit_pat.ail @@ -46,9 +46,9 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app classify 0)) - (seq (app print (app classify 1)) - (seq (app print (app classify 17)) - (seq (app print (app categorize_first (term-ctor IntList Nil))) - (seq (app print (app categorize_first (term-ctor IntList Cons 0 (term-ctor IntList Nil)))) - (app print (app categorize_first (term-ctor IntList Cons 7 (term-ctor IntList Nil)))))))))))) + (seq (seq (app print (app classify 0)) (do io/print_str "\n")) + (seq (seq (app print (app classify 1)) (do io/print_str "\n")) + (seq (seq (app print (app classify 17)) (do io/print_str "\n")) + (seq (seq (app print (app categorize_first (term-ctor IntList Nil))) (do io/print_str "\n")) + (seq (seq (app print (app categorize_first (term-ctor IntList Cons 0 (term-ctor IntList Nil)))) (do io/print_str "\n")) + (seq (app print (app categorize_first (term-ctor IntList Cons 7 (term-ctor IntList Nil)))) (do io/print_str "\n")))))))))) diff --git a/examples/local_rec_as_value_capture.ail b/examples/local_rec_as_value_capture.ail index 4f4b76d..f4087a1 100644 --- a/examples/local_rec_as_value_capture.ail +++ b/examples/local_rec_as_value_capture.ail @@ -39,5 +39,5 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app run_with_base 10)) - (app print (app run_with_base 100)))))) + (seq (seq (app print (app run_with_base 10)) (do io/print_str "\n")) + (seq (app print (app run_with_base 100)) (do io/print_str "\n")))))) diff --git a/examples/local_rec_capture.ail b/examples/local_rec_capture.ail index b431eab..cf4aa4f 100644 --- a/examples/local_rec_capture.ail +++ b/examples/local_rec_capture.ail @@ -30,6 +30,6 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app sum_below 1)) - (seq (app print (app sum_below 5)) - (app print (app sum_below 10))))))) + (seq (seq (app print (app sum_below 1)) (do io/print_str "\n")) + (seq (seq (app print (app sum_below 5)) (do io/print_str "\n")) + (seq (app print (app sum_below 10)) (do io/print_str "\n"))))))) diff --git a/examples/local_rec_demo.ail b/examples/local_rec_demo.ail index 1501d5e..d9ec084 100644 --- a/examples/local_rec_demo.ail +++ b/examples/local_rec_demo.ail @@ -21,6 +21,6 @@ 1 (app * n (app fact (app - n 1))))) (in - (seq (app print (app fact 1)) - (seq (app print (app fact 3)) - (app print (app fact 5))))))))) + (seq (seq (app print (app fact 1)) (do io/print_str "\n")) + (seq (seq (app print (app fact 3)) (do io/print_str "\n")) + (seq (app print (app fact 5)) (do io/print_str "\n"))))))))) diff --git a/examples/local_rec_let_capture.ail b/examples/local_rec_let_capture.ail index b3c5ba4..91f40b5 100644 --- a/examples/local_rec_let_capture.ail +++ b/examples/local_rec_let_capture.ail @@ -43,6 +43,6 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app count_below 0)) - (seq (app print (app count_below 5)) - (app print (app count_below 15))))))) + (seq (seq (app print (app count_below 0)) (do io/print_str "\n")) + (seq (seq (app print (app count_below 5)) (do io/print_str "\n")) + (seq (app print (app count_below 15)) (do io/print_str "\n"))))))) diff --git a/examples/local_rec_match_capture.ail b/examples/local_rec_match_capture.ail index b8f0a52..7dfe100 100644 --- a/examples/local_rec_match_capture.ail +++ b/examples/local_rec_match_capture.ail @@ -54,6 +54,6 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app count_below (term-ctor Pair MkPair 10 0))) - (seq (app print (app count_below (term-ctor Pair MkPair 10 5))) - (app print (app count_below (term-ctor Pair MkPair 10 15)))))))) + (seq (seq (app print (app count_below (term-ctor Pair MkPair 10 0))) (do io/print_str "\n")) + (seq (seq (app print (app count_below (term-ctor Pair MkPair 10 5))) (do io/print_str "\n")) + (seq (app print (app count_below (term-ctor Pair MkPair 10 15))) (do io/print_str "\n"))))))) diff --git a/examples/maybe_int.ail b/examples/maybe_int.ail index 72171c2..bcf1ca7 100644 --- a/examples/maybe_int.ail +++ b/examples/maybe_int.ail @@ -14,4 +14,4 @@ (doc "Print or_else for both arms; expected 7 then 99.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (app or_else (term-ctor Maybe Some 7) 99)) (app print (app or_else (term-ctor Maybe None) 99)))))) + (body (seq (seq (app print (app or_else (term-ctor Maybe Some 7) 99)) (do io/print_str "\n")) (seq (app print (app or_else (term-ctor Maybe None) 99)) (do io/print_str "\n")))))) diff --git a/examples/nested_let_rec.ail b/examples/nested_let_rec.ail index 0adf855..473c51a 100644 --- a/examples/nested_let_rec.ail +++ b/examples/nested_let_rec.ail @@ -48,6 +48,6 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app nested_sum 1)) - (seq (app print (app nested_sum 3)) - (app print (app nested_sum 5))))))) + (seq (seq (app print (app nested_sum 1)) (do io/print_str "\n")) + (seq (seq (app print (app nested_sum 3)) (do io/print_str "\n")) + (seq (app print (app nested_sum 5)) (do io/print_str "\n"))))))) diff --git a/examples/nullary_app_smoke.ail b/examples/nullary_app_smoke.ail index cc51e67..a7d9231 100644 --- a/examples/nullary_app_smoke.ail +++ b/examples/nullary_app_smoke.ail @@ -3,7 +3,7 @@ (doc "Nullary user function — no params, prints a fixed line.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (do io/print_str "hello"))) + (body (seq (do io/print_str "hello") (do io/print_str "\n")))) (fn main (doc "Calls the nullary `greet` as `(app greet)` — the zero-arg surface form accepted under Gitea #12.") diff --git a/examples/ordering_match.ail b/examples/ordering_match.ail index 265300f..74569a8 100644 --- a/examples/ordering_match.ail +++ b/examples/ordering_match.ail @@ -3,7 +3,7 @@ (doc "Iter 23.1 fixture: pattern-match on prelude Ordering ctor without explicit prelude import.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (app print (match (term-ctor prelude.Ordering LT) + (body (seq (app print (match (term-ctor prelude.Ordering LT) (case (pat-ctor LT) 1) (case (pat-ctor EQ) 2) - (case (pat-ctor GT) 3)))))) + (case (pat-ctor GT) 3))) (do io/print_str "\n"))))) diff --git a/examples/ordering_match.prose.txt b/examples/ordering_match.prose.txt index 6cc36c5..2366d50 100644 --- a/examples/ordering_match.prose.txt +++ b/examples/ordering_match.prose.txt @@ -7,5 +7,6 @@ fn main() -> Unit with IO { LT => 1, EQ => 2, GT => 3 - }) + }); + do io/print_str("\n") } diff --git a/examples/poly_id.ail b/examples/poly_id.ail index f242daf..5fa3dbf 100644 --- a/examples/poly_id.ail +++ b/examples/poly_id.ail @@ -6,4 +6,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (app id 42)) (app print (app id true)))))) + (body (seq (seq (app print (app id 42)) (do io/print_str "\n")) (seq (app print (app id true)) (do io/print_str "\n")))))) diff --git a/examples/poly_rec_capture.ail b/examples/poly_rec_capture.ail index 471a125..0b0c37c 100644 --- a/examples/poly_rec_capture.ail +++ b/examples/poly_rec_capture.ail @@ -53,5 +53,5 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app apply_n_times 5 0 succ)) - (app print (app apply_n_times 4 false flip)))))) + (seq (seq (app print (app apply_n_times 5 0 succ)) (do io/print_str "\n")) + (seq (app print (app apply_n_times 4 false flip)) (do io/print_str "\n")))))) diff --git a/examples/print_float_whole_smoke.ail b/examples/print_float_whole_smoke.ail index 2bb775a..7d7da1c 100644 --- a/examples/print_float_whole_smoke.ail +++ b/examples/print_float_whole_smoke.ail @@ -3,4 +3,4 @@ (doc "RED pin for Gitea #7: a whole-valued Float (here, the literal 2.0) routed through polymorphic `print` (which monomorphises to Show Float.show -> float_to_str -> runtime/str.c::ailang_float_to_str -> libc snprintf %g) must render with a `.0` suffix, matching the surface printer's write_float_lit fallback (crates/ailang-surface/src/print.rs lines 624-637). The desired stdout for this fixture is the three bytes `2.0\\n`. Pre-fix, libc %g strips trailing zeros and renders `2\\n` — Int-shaped output for a Float value — which violates the surface-runtime round-trip-on-lex symmetry the language relies on.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (app print 2.0)))) + (body (seq (app print 2.0) (do io/print_str "\n"))))) diff --git a/examples/show_print_smoke.ail b/examples/show_print_smoke.ail index 4dfe1b1..9db3d0f 100644 --- a/examples/show_print_smoke.ail +++ b/examples/show_print_smoke.ail @@ -2,4 +2,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (seq (seq (app print 42) (app print true)) (app print "hello")) (app print 3.14))))) + (body (seq (seq (seq (seq (app print 42) (do io/print_str "\n")) (seq (app print true) (do io/print_str "\n"))) (seq (app print "hello") (do io/print_str "\n"))) (app print 3.14))))) diff --git a/examples/show_user_adt_with_label.ail b/examples/show_user_adt_with_label.ail index 4387144..add6da2 100644 --- a/examples/show_user_adt_with_label.ail +++ b/examples/show_user_adt_with_label.ail @@ -10,4 +10,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (app print (term-ctor Item MkItem 42))))) + (body (seq (app print (term-ctor Item MkItem 42)) (do io/print_str "\n"))))) diff --git a/examples/sort.ail b/examples/sort.ail index 44828a9..2a01855 100644 --- a/examples/sort.ail +++ b/examples/sort.ail @@ -22,7 +22,7 @@ (params xs) (body (match xs (case (pat-ctor Nil) (lit-unit)) - (case (pat-ctor Cons h t) (seq (app print h) (tail-app print_list t)))))) + (case (pat-ctor Cons h t) (seq (seq (app print h) (do io/print_str "\n")) (tail-app print_list t)))))) (fn main (doc "Sort and print [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].") (type (fn-type (params) (ret (con Unit)) (effects IO))) diff --git a/examples/std_either_demo.ail b/examples/std_either_demo.ail index d9255b0..dd13bce 100644 --- a/examples/std_either_demo.ail +++ b/examples/std_either_demo.ail @@ -18,14 +18,14 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_either.from_right 0 (term-ctor std_either.Either Right 42))) - (seq (app print (app std_either.from_right 99 (term-ctor std_either.Either Left 7))) - (seq (app print (app std_either.is_left (term-ctor std_either.Either Left 7))) - (seq (app print (app std_either.is_right (term-ctor std_either.Either Right 42))) - (seq (app print (app std_either.from_right 0 + (seq (seq (app print (app std_either.from_right 0 (term-ctor std_either.Either Right 42))) (do io/print_str "\n")) + (seq (seq (app print (app std_either.from_right 99 (term-ctor std_either.Either Left 7))) (do io/print_str "\n")) + (seq (seq (app print (app std_either.is_left (term-ctor std_either.Either Left 7))) (do io/print_str "\n")) + (seq (seq (app print (app std_either.is_right (term-ctor std_either.Either Right 42))) (do io/print_str "\n")) + (seq (seq (app print (app std_either.from_right 0 (app std_either.map_right inc - (term-ctor std_either.Either Right 41)))) - (seq (app print (app std_either.either inc inc - (term-ctor std_either.Either Left 5))) - (app print (app std_either.either inc inc - (term-ctor std_either.Either Right 100)))))))))))) + (term-ctor std_either.Either Right 41)))) (do io/print_str "\n")) + (seq (seq (app print (app std_either.either inc inc + (term-ctor std_either.Either Left 5))) (do io/print_str "\n")) + (seq (app print (app std_either.either inc inc + (term-ctor std_either.Either Right 100))) (do io/print_str "\n"))))))))))) diff --git a/examples/std_either_list_demo.ail b/examples/std_either_list_demo.ail index b38c797..ea068e2 100644 --- a/examples/std_either_list_demo.ail +++ b/examples/std_either_list_demo.ail @@ -24,7 +24,7 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_list.length (app std_either_list.lefts (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil))))))))) - (seq (app print (app std_list.length (app std_either_list.rights (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil))))))))) - (seq (app print (app std_list.length (app std_pair.fst (app std_either_list.partition_eithers (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil)))))))))) - (app print (app std_list.length (app std_pair.snd (app std_either_list.partition_eithers (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil)))))))))))))))) + (seq (seq (app print (app std_list.length (app std_either_list.lefts (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil))))))))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_either_list.rights (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil))))))))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_pair.fst (app std_either_list.partition_eithers (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil)))))))))) (do io/print_str "\n")) + (seq (app print (app std_list.length (app std_pair.snd (app std_either_list.partition_eithers (term-ctor std_list.List Cons (term-ctor std_either.Either Left 1) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 10) (term-ctor std_list.List Cons (term-ctor std_either.Either Left 2) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 20) (term-ctor std_list.List Cons (term-ctor std_either.Either Right 30) (term-ctor std_list.List Nil)))))))))) (do io/print_str "\n")))))))) diff --git a/examples/std_list_demo.ail b/examples/std_list_demo.ail index 45091cb..695a027 100644 --- a/examples/std_list_demo.ail +++ b/examples/std_list_demo.ail @@ -48,14 +48,14 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_list.length xs)) - (seq (app print (app std_list.is_empty xs)) - (seq (app print (app std_list.is_empty (term-ctor std_list.List Nil))) - (seq (app print (app std_maybe.from_maybe -1 (app std_list.head xs))) - (seq (app print (app std_list.length (app std_maybe.from_maybe xs (app std_list.tail xs)))) - (seq (app print (app std_list.length (app std_list.append xs xs))) - (seq (app print (app std_maybe.from_maybe -1 (app std_list.head (app std_list.reverse xs)))) - (seq (app print (app std_maybe.from_maybe -1 (app std_list.head (app std_list.map double xs)))) - (seq (app print (app std_list.length (app std_list.filter is_even xs))) - (seq (app print (app std_list.fold_left add 0 xs)) - (app print (app std_list.fold_right add 0 xs))))))))))))))) + (seq (seq (app print (app std_list.length xs)) (do io/print_str "\n")) + (seq (seq (app print (app std_list.is_empty xs)) (do io/print_str "\n")) + (seq (seq (app print (app std_list.is_empty (term-ctor std_list.List Nil))) (do io/print_str "\n")) + (seq (seq (app print (app std_maybe.from_maybe -1 (app std_list.head xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_maybe.from_maybe xs (app std_list.tail xs)))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_list.append xs xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_maybe.from_maybe -1 (app std_list.head (app std_list.reverse xs)))) (do io/print_str "\n")) + (seq (seq (app print (app std_maybe.from_maybe -1 (app std_list.head (app std_list.map double xs)))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_list.filter is_even xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.fold_left add 0 xs)) (do io/print_str "\n")) + (seq (app print (app std_list.fold_right add 0 xs)) (do io/print_str "\n"))))))))))))))) diff --git a/examples/std_list_more_demo.ail b/examples/std_list_more_demo.ail index 01d8f7c..a251055 100644 --- a/examples/std_list_more_demo.ail +++ b/examples/std_list_more_demo.ail @@ -24,9 +24,9 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_list.length (app std_list.take 0 xs))) - (seq (app print (app std_list.length (app std_list.take 3 xs))) - (seq (app print (app std_list.length (app std_list.take 100 xs))) - (seq (app print (app std_list.length (app std_list.drop 0 xs))) - (seq (app print (app std_list.length (app std_list.drop 2 xs))) - (app print (app std_list.length (app std_list.drop 100 xs))))))))))) + (seq (seq (app print (app std_list.length (app std_list.take 0 xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_list.take 3 xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_list.take 100 xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_list.drop 0 xs))) (do io/print_str "\n")) + (seq (seq (app print (app std_list.length (app std_list.drop 2 xs))) (do io/print_str "\n")) + (seq (app print (app std_list.length (app std_list.drop 100 xs))) (do io/print_str "\n")))))))))) diff --git a/examples/std_list_stress.ail b/examples/std_list_stress.ail index 834c21d..80c8929 100644 --- a/examples/std_list_stress.ail +++ b/examples/std_list_stress.ail @@ -37,5 +37,5 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_list.fold_left add 0 (app build 1000))) - (app print (app std_list.fold_right add 0 (app build 1000))))))) + (seq (seq (app print (app std_list.fold_left add 0 (app build 1000))) (do io/print_str "\n")) + (seq (app print (app std_list.fold_right add 0 (app build 1000))) (do io/print_str "\n")))))) diff --git a/examples/std_maybe_demo.ail b/examples/std_maybe_demo.ail index 2623a17..7811cc9 100644 --- a/examples/std_maybe_demo.ail +++ b/examples/std_maybe_demo.ail @@ -23,8 +23,8 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_maybe.from_maybe 99 (term-ctor std_maybe.Maybe Just 7))) - (seq (app print (app std_maybe.from_maybe 99 (term-ctor std_maybe.Maybe Nothing))) - (seq (app print (app std_maybe.is_some (term-ctor std_maybe.Maybe Just 5))) - (seq (app print (app std_maybe.is_none (term-ctor std_maybe.Maybe Nothing))) - (app print (app std_maybe.from_maybe 0 (app std_maybe.map_maybe inc (term-ctor std_maybe.Maybe Just 41))))))))))) + (seq (seq (app print (app std_maybe.from_maybe 99 (term-ctor std_maybe.Maybe Just 7))) (do io/print_str "\n")) + (seq (seq (app print (app std_maybe.from_maybe 99 (term-ctor std_maybe.Maybe Nothing))) (do io/print_str "\n")) + (seq (seq (app print (app std_maybe.is_some (term-ctor std_maybe.Maybe Just 5))) (do io/print_str "\n")) + (seq (seq (app print (app std_maybe.is_none (term-ctor std_maybe.Maybe Nothing))) (do io/print_str "\n")) + (seq (app print (app std_maybe.from_maybe 0 (app std_maybe.map_maybe inc (term-ctor std_maybe.Maybe Just 41)))) (do io/print_str "\n"))))))))) diff --git a/examples/std_pair_demo.ail b/examples/std_pair_demo.ail index f10e8e9..cd6171e 100644 --- a/examples/std_pair_demo.ail +++ b/examples/std_pair_demo.ail @@ -24,9 +24,9 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app std_pair.fst (term-ctor std_pair.Pair MkPair 7 9))) - (seq (app print (app std_pair.snd (term-ctor std_pair.Pair MkPair 7 9))) - (seq (app print (app std_pair.fst (app std_pair.swap (term-ctor std_pair.Pair MkPair 7 9)))) - (seq (app print (app std_pair.snd (app std_pair.swap (term-ctor std_pair.Pair MkPair 7 9)))) - (seq (app print (app std_pair.fst (app std_pair.map_first inc (term-ctor std_pair.Pair MkPair 7 9)))) - (app print (app std_pair.snd (app std_pair.map_second double (term-ctor std_pair.Pair MkPair 7 9)))))))))))) + (seq (seq (app print (app std_pair.fst (term-ctor std_pair.Pair MkPair 7 9))) (do io/print_str "\n")) + (seq (seq (app print (app std_pair.snd (term-ctor std_pair.Pair MkPair 7 9))) (do io/print_str "\n")) + (seq (seq (app print (app std_pair.fst (app std_pair.swap (term-ctor std_pair.Pair MkPair 7 9)))) (do io/print_str "\n")) + (seq (seq (app print (app std_pair.snd (app std_pair.swap (term-ctor std_pair.Pair MkPair 7 9)))) (do io/print_str "\n")) + (seq (seq (app print (app std_pair.fst (app std_pair.map_first inc (term-ctor std_pair.Pair MkPair 7 9)))) (do io/print_str "\n")) + (seq (app print (app std_pair.snd (app std_pair.map_second double (term-ctor std_pair.Pair MkPair 7 9)))) (do io/print_str "\n")))))))))) diff --git a/examples/str_clone_cross_realisation.ail b/examples/str_clone_cross_realisation.ail index 16c4172..33c454e 100644 --- a/examples/str_clone_cross_realisation.ail +++ b/examples/str_clone_cross_realisation.ail @@ -3,4 +3,4 @@ (doc "Iter 24.1: cross-realisation invariant for str_clone. Clones a heap-Str (produced by int_to_str 42) AND a static-Str (literal `abc`); both clones produce fresh heap-Str slabs that print their input bytes. Pins the uniform-consumer-ABI claim — str_clone only reads the len-field at offset 0 plus the bytes plus the NUL; it never consults the (absent for static-Str) rc_header. RC stats under --alloc=rc: allocs == 3 (int_to_str output + two str_clone outputs), frees == 3, live == 0.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let src_heap (app int_to_str 42) (let c1 (app str_clone src_heap) (let c2 (app str_clone "abc") (let _a (do io/print_str c1) (do io/print_str c2)))))))) + (body (let src_heap (app int_to_str 42) (let c1 (app str_clone src_heap) (let c2 (app str_clone "abc") (let _a (seq (do io/print_str c1) (do io/print_str "\n")) (seq (do io/print_str c2) (do io/print_str "\n"))))))))) diff --git a/examples/str_clone_drop_rc.ail b/examples/str_clone_drop_rc.ail index 2d1208f..fb0fb21 100644 --- a/examples/str_clone_drop_rc.ail +++ b/examples/str_clone_drop_rc.ail @@ -3,4 +3,4 @@ (doc "Iter 24.1: pin that `str_clone` participates in RC discipline AND emits correct bytes. Input is a static-Str literal; the clone produces a fresh heap-Str slab whose payload is byte-equal to the input. RC stats under --alloc=rc with AILANG_RC_STATS=1: allocs == 1 (the clone), frees == 1 (let-binder drop at scope close), live == 0.") (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (let s (app str_clone "hello") (do io/print_str s))))) + (body (let s (app str_clone "hello") (seq (do io/print_str s) (do io/print_str "\n")))))) diff --git a/examples/str_field_in_adt_heap.ail b/examples/str_field_in_adt_heap.ail index beb8039..9788885 100644 --- a/examples/str_field_in_adt_heap.ail +++ b/examples/str_field_in_adt_heap.ail @@ -7,4 +7,4 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body (let b (term-ctor Boxed Box (app int_to_str 42)) (match b - (case (pat-ctor Box s) (do io/print_str s))))))) + (case (pat-ctor Box s) (seq (do io/print_str s) (do io/print_str "\n")))))))) diff --git a/examples/test_22b3_coherence_two_instances.ail b/examples/test_22b3_coherence_two_instances.ail index 41e43fa..6765e83 100644 --- a/examples/test_22b3_coherence_two_instances.ail +++ b/examples/test_22b3_coherence_two_instances.ail @@ -16,4 +16,4 @@ (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) - (body (seq (app print (app r 0)) (app print (app r true)))))) + (body (seq (seq (app print (app r 0)) (do io/print_str "\n")) (seq (app print (app r true)) (do io/print_str "\n")))))) diff --git a/examples/unreachable_demo.ail b/examples/unreachable_demo.ail index a73dc90..f99457f 100644 --- a/examples/unreachable_demo.ail +++ b/examples/unreachable_demo.ail @@ -31,5 +31,5 @@ (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body - (seq (app print (app safe_div 8 2)) - (app print (app safe_div 15 3)))))) + (seq (seq (app print (app safe_div 8 2)) (do io/print_str "\n")) + (seq (app print (app safe_div 15 3)) (do io/print_str "\n")))))) diff --git a/runtime/str.c b/runtime/str.c index c2baacc..72a2529 100644 --- a/runtime/str.c +++ b/runtime/str.c @@ -71,7 +71,7 @@ int ail_str_compare(const char *a, const char *b) { * payload-8 payload (returned) * * Calls `ailang_rc_alloc(8 + len + 1)`. The +8 is the len field; - * the +1 is the trailing NUL byte that keeps `@strcmp` and `@puts` + * the +1 is the trailing NUL byte that keeps `@strcmp` and `@fputs` * happy under the shared consumer ABI. Writes `len` into the first * 8 bytes of the payload and returns the payload pointer; the * caller is responsible for filling bytes [8 .. 8 + len) and the