cmd+rt+selfhost+test: graduate alloc to (*T | nomem) / ([]T | nomem)

Per Hare convention, alloc is a typed builtin that returns a tagged
union carrying nomem as the OOM variant. Callers spell their policy:
`alloc(T)!` aborts on OOM (the old behavior), `alloc(T)?` propagates
when the enclosing fn already returns nomem.

cstage: check builds TY_TAGGED{*T | nomem} (or {[]T | nomem}); cgen
emits AX=tag, DX=ptr per the general tagged-return ABI (the (*T|!void)
nullable-ptr fold gated in ea76ee4 keeps this clean). wwstage cgalloc
mirrors. rt/alloc.s zeroes AX on syscall error so the builtin's null
check sees a clean 0 instead of mmap's -errno leaking through as a
poisoned pointer.

Migration: 3 `!` sites in test/wcc/700_e2e.c, 1 `!` site in
rt/ensure.ww (preserves the pre-existing sizeof bug tracked by #27),
1 `?` site in selfhost/test/tagged_ptr_ret.ww (allocbox exercises
real `?` propagation against a (*T | nomem) return).

130/130 tests green, 994_w6c_ww + 995_self_rebuild stage byte-identity
preserved. Follow-ups #31 (wwstage checkletassign leniency), #32
(wwstage slice-form gap), #33 (tagged_ptr_ret.ww make-test wiring).
This commit is contained in:
2026-05-19 20:25:14 +09:00
parent d27411d833
commit 61705fb39e
9 changed files with 204 additions and 41 deletions

View File

@@ -13,10 +13,19 @@
// Two match arms cover both runtime outcomes — success unwrap (tag=0,
// ptr payload in DX) and error propagation (tag=1) — exercising the
// same AX/DX ABI both stages must agree on.
//
// Task #30: the `alloc` builtin itself now returns `(*T | nomem)`. The
// allocbox arm below propagates the builtin's tagged return through
// the enclosing fn via `?` against a same-shape `(*point | nomem)`
// — matching the team-lead spec's "exercise `alloc(T)?` against a
// real function returning `(T | nomem)`".
package test;
package main;
import fmt;
import os;
type point = struct { x: i32, y: i32 };
fn alloc1(fail: i64) (*u8 | nomem) = {
if (fail != 0i64) { let e: nomem; return e; };
@@ -29,6 +38,11 @@ fn caller(fail: i64) (*u8 | nomem) = {
return p;
};
fn allocbox() (*point | nomem) = {
let p: *point = alloc(point { x = 3, y = 4 })?;
return p;
};
export fn main() i32 = {
let rc: i32 = 0;
match (caller(0i64)) {
@@ -50,5 +64,15 @@ export fn main() i32 = {
fmt.println("nomem as expected");
};
};
match (allocbox()) {
case let p: *point => {
fmt.println("allocbox ok");
if (p.x * p.x + p.y * p.y != 25) { rc = 4; };
};
case nomem => {
fmt.println("allocbox unexpected nomem");
rc = 5;
};
};
return rc;
};