/* * 960_opaque_decl_run — runtime proof that the abstract `opaque` type * (#108 sub-fold a) EXISTS and is usable behind indirection: `*opaque` * is a usable 8-byte pointer and `[]opaque` is a usable 24-byte slice * header (.len/.ptr). The name resolves via the type-name resolver * (lookup_builtin / tinfofornode's N_TNAME chain), mirroring uintptr/ * size; `opaque` itself is unsized (size=align=SIZE_UNDEFINED), so it is * only ever materialised through a pointer or slice, never as a bare * value. A green row IS the proof the name binds: without the resolver * arm every `*opaque` / `[]opaque` row fails to compile ("unknown type * 'opaque'"). * * NOTE — this fold (a) deliberately does NOT test bare `let x: opaque`, * `size(opaque)`, an `opaque` (bare) struct field, `[N]opaque`, or * `[]opaque` INDEXING: those are the use-restriction GUARDS of sub-fold * (b). Every row here stays behind indirection. * * Table-driven like 957_size_type_run / 952_floats_run: each row is a * self-contained ww program; the C-side cstage `ww build -I lib` * compiles it, we run the binary and assert the exit code. cstage-only * by design (mirrors 951/952/957/969): per-program wwstage byte-id is * the 990-997 gates' job, and the wwstage resolver arm is twinned for * rule-10 symmetry but stays dead on the opaque-free selfhost corpus. */ #include #include #include #include #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return 1; } struct row { const char *src; int want_exit; }; static const struct row rows[] = { /* `*opaque` is a usable 8-byte pointer: round-trip a real *i32 * through *opaque and back, then deref. Proves the type binds and * carries pointer width. */ { "package main;\n" "export fn main() i32 = {\n" " let n: i32 = 42;\n" " let pn: *i32 = &n;\n" " let po: *opaque = pn: *opaque;\n" " let back: *i32 = po: *i32;\n" " return *back;\n" "};\n", 42 }, /* `[]opaque` is a usable 24-byte slice header: reinterpret a real * []i32 as []opaque, read .len, and round-trip .ptr (a *opaque) * back to *i32 + deref. The []opaque is never indexed (guard b). * (Uses a[0:4] + a store-to-var deref to steer clear of an * unrelated, pre-existing cgen bug — inline deref of a sub-slice * .ptr inside a comparison; see report. The opaque path itself is * exercised in full.) */ { "package main;\n" "export fn main() i32 = {\n" " let a: [4]i32;\n" " a[0] = 11; a[1] = 22; a[2] = 33; a[3] = 44;\n" " let s: []i32 = a[0:4];\n" " let so: []opaque = s: []opaque;\n" " let n: i32 = so.len: i32;\n" " if (n != 4) { return 1; };\n" " let pp: *opaque = so.ptr;\n" " let pi: *i32 = pp: *i32;\n" " let v: i32 = *pi;\n" " if (v != 11) { return 2; };\n" " return n - 1;\n" "};\n", 3 }, /* `*opaque` as a struct field (behind indirection): set from a cast * pointer, read back, deref + add a sibling scalar field. */ { "package main;\n" "type handle = struct { p: *opaque, tag: i32 };\n" "export fn main() i32 = {\n" " let n: i32 = 99;\n" " let h: handle = handle{ p = (&n): *opaque, tag = 7 };\n" " let pi: *i32 = h.p: *i32;\n" " return *pi + h.tag;\n" "};\n", 106 }, { NULL, 0 } }; int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; char absbin[1024]; if (bin[0] != '/') { snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { char src[64]; snprintf(src, sizeof src, "/tmp/wwopq_%d_%d.ww", getpid(), i); FILE *f = fopen(src, "wb"); if (f == NULL) { fail++; continue; } fputs(rows[i].src, f); fclose(f); char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwopq_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); char cmd[2048]; snprintf(cmd, sizeof cmd, "cd %s && %s/ww build -I %s/lib %s", tmpdir, bin, cwd, src); if (runwait(cmd) != 0) { fprintf(stderr, "row %d: build failed\n src: %s\n", i, rows[i].src); fail++; unlink(src); rmdir(tmpdir); continue; } char outbin[128]; const char *base = strrchr(src, '/'); base = base ? base + 1 : src; snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); char *dot = strrchr(outbin, '.'); if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; int got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n", i, got, rows[i].want_exit, rows[i].src); fail++; } unlink(src); unlink(outbin); rmdir(tmpdir); } if (fail) { fprintf(stderr, "%d/%d opaque tests failed\n", fail, n); return 1; } printf("opaque: %d/%d ok\n", n, n); return 0; }