//ww:run-exit 42 // selfhost_smoke — end-to-end smoke for the selfhost patterns (migrated // from selfhost/test/smoke.ww, the 990_selfhost probe fixture). // // Exercises the patterns the real ww-side compiler port will use: // - bump arena allocator (mem.ww shape) // - error idiom (T | str) // - struct of fn pointers + ctx pointer (the io.stream-style // polymorphism we use instead of interfaces) // - byte-level scanning that mirrors the hot path inside lex.ww // - strconv round-trip via the real stdlib // // `main` returns 42 when every check passes, 1..N on failure // indicating which probe broke. The compiler-fixture corpus asserts 42. // // Note: only stack-local mutable state. Top-level `let` mutation // requires a writable .data segment in w6l, which is a separate // task; until then we exercise polymorphism via ctx pointers, which // is what the real port wants anyway. package test; import os; import strconv; import ascii; // --- bump arena --------------------------------------------------------- type arena = struct { buf: *u8, off: u64, cap: u64, }; // In-place init. Returning a 24-byte struct by value isn't yet // supported in w6c (SysV requires a hidden return-slot pointer for // structs >16 bytes), so we initialize through a pointer like the // real compiler does today. fn arena_init(a: *arena, buf: *u8, cap: u64) void = { a.buf = buf; a.off = 0u64; a.cap = cap; }; fn arena_alloc(a: *arena, n: u64) *u8 = { if (n > a.cap - a.off) { return nil; }; let p: *u8 = a.buf + a.off; a.off += n; return p; }; // --- (i32 | str) error idiom ------------------------------------------- fn checked_div(num: i32, den: i32) (i32 | str) = { if (den == 0) { return "div by zero"; }; return num / den; }; // --- struct-of-fn-pointer polymorphism --------------------------------- // // A trivial "writer" abstraction: a function pointer plus a context. // This mirrors how io.stream / Plan 9 Bio work. The ctx pointer lets // the implementation own its own state without a global. type counter = struct { n: i32, }; type writer = struct { ctx: *void, emit: fn(ctx: *void, b: u8) void, }; fn count_emit(ctx: *void, b: u8) void = { let c: *counter = ctx: *counter; c.n += 1; }; // --- size/align/offset typed-builtin fixtures (#42) -------------------- type point = struct { x: i32, y: i32, }; // Mixed-alignment struct: i8 lays at 0, then i64 needs to skip to // offset 8 (the i64's natural align). Probe asserts both ends. type mixalign = struct { tag: i8, val: i64, }; // --- byte scanner like lex.ww's hot path ------------------------------- fn count_digits(s: str) i32 = { let i: i32 = 0; let n: i32 = 0; for (i < s.len) { let c: u8 = s[i]; if (c >= 48u8) { if (c <= 57u8) { n += 1; }; }; i += 1; }; return n; }; // --- entry -------------------------------------------------------------- export fn main() i32 = { // Probe 1 — arena hands out distinct pointers, refuses oversize. let buf: [256]u8; let a: arena; arena_init(&a, buf.ptr, 256u64); let p1: *u8 = arena_alloc(&a, 32u64); let p2: *u8 = arena_alloc(&a, 32u64); if (p1 == nil) { return 1; }; if (p2 == nil) { return 2; }; if (p1 == p2) { return 3; }; let p3: *u8 = arena_alloc(&a, 1024u64); if (p3 != nil) { return 4; }; // Probe 2 — error union both ways. let r_ok: (i32 | str) = checked_div(84, 2); let r_bad: (i32 | str) = checked_div(1, 0); let acc: i32 = 0; match (r_ok) { case let v: i32 => acc = v; case let e: str => return 5; }; if (acc != 42) { return 6; }; match (r_bad) { case let v: i32 => return 7; case let e: str => acc = e.len: i32; }; if (acc != 11) { return 8; }; // len("div by zero") == 11 // Probe 3 — struct-of-fn-pointer dispatch via ctx pointer. let c: counter = counter { n = 0 }; let w: writer = writer { ctx = (&c): *void, emit = count_emit }; w.emit(w.ctx, 65u8); w.emit(w.ctx, 66u8); w.emit(w.ctx, 67u8); if (c.n != 3) { return 9; }; // Probe 4 — byte scan over a literal. let dn: i32 = count_digits("ww123abc"); if (dn != 3) { return 10; }; // Probe 5 — strconv round-trip via the real stdlib. let s: str = strconv.i64tos(4242i64, strconv.base.DEC); if (s.len != 4) { return 11; }; if (s.ptr[0] != 52u8) { return 12; }; // '4' if (s.ptr[3] != 50u8) { return 13; }; // '2' // Probe 6 — ascii classifications (rune-taking, Hare-shaped). if (!ascii.isdigit(53)) { return 14; }; // '5' if (ascii.isdigit(65)) { return 15; }; // 'A' is not a digit if (!ascii.isalpha(122)) { return 16; }; // 'z' if (!ascii.isxdigit(70)) { return 17; }; // 'F' if (ascii.isxdigit(71)) { return 18; }; // 'G' is not hex if (ascii.tolower(65) != 97) { return 19; }; // 'A' -> 'a' if (ascii.toupper(122) != 90) { return 20; }; // 'z' -> 'Z' // Probe 7 — file open/read via the new os APIs. /proc/self/cmdline // always exists on Linux, no write side, and is non-empty. let path: str = "/proc/self/cmdline"; // Use raw os.open here (returns i32 with -errno) for the same // reason as os.read below: the retired 990 probes compiled // smoke.ww standalone (no `use` expansion), so cross-module type // references like `os.oserror` and `os.flag` kept off the hot // path. RDONLY is 0; the literal keeps the historical shape. let fd: i32 = os.open(path, 0, 0i32); if (fd < 0) { return 21; }; let rbuf: [128]u8; // Use raw os.read here (single syscall, plain i64) instead of // os.readall, keeping the retired standalone-compile shape. let n: i64 = os.read(fd, rbuf.ptr, 128u64); os.close(fd); if (n <= 0i64) { return 22; }; // Probe 8 — size(T) / align(T) / offset(e.f) typed-builtin folds // (#42). Each call folds to an N_INTLIT at check time; cgen // materialises the literal as a plain `MOVQ $N, AX`. Mirrors // cstage cmd/wcc/check.c:907-960 byte-for-byte on this corpus. if (size(str) != 24) { return 23; }; // str IS []u8: {ptr,len,cap} 24B (#1/Phase 3) if (size(i64) != 8) { return 24; }; if (size(i32) != 4) { return 25; }; if (align(i64) != 8) { return 26; }; if (align(i32) != 4) { return 27; }; // Initialize struct locals explicitly so the cgen path doesn't // drift from cstage on bare `let X: T;` zero-init (pre-existing // wwstage divergence outside #42). let pt: point = point { x = 0, y = 0 }; if (offset(pt.x) != 0) { return 28; }; if (offset(pt.y) != 4) { return 29; }; let mx: mixalign = mixalign { tag = 0i8, val = 0i64 }; if (offset(mx.tag) != 0) { return 30; }; if (offset(mx.val) != 8) { return 31; }; // align-padded to 8 // Width breadth: smallest prim, ptr, slice, struct (8B + padded), // covering astsize's TPTR/TSLICE/TNAME-resolve-to-struct arms. if (size(i8) != 1) { return 32; }; if (align(i8) != 1) { return 33; }; if (size(*i32) != 8) { return 34; }; if (size([]i32) != 24) { return 35; }; if (size(point) != 8) { return 36; }; if (size(mixalign) != 16) { return 37; }; return 42; };