/* * 961_opaque_guards — C-stage-only guards for the unsized opaque type. * * opaque is legal behind indirection (*opaque is 8 bytes and []opaque is a * 24-byte header), but it cannot appear by value or be indexed through a * slice. The WW frontend currently accepts the eight programs below because * it does not validate those construction and binding sites. That polarity * cannot be represented by the both-stage fixture protocol, so this carrier * requires each program to fail with the C-stage driver. * * Both-stage size/align rejections, including nested aggregates, are owned by * the six r961_neg_* fixtures. The sized pointer and slice controls are * owned by r961_pos_ptr_opaque and r961_pos_slice_opaque. */ #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 *label; const char *src; }; static const struct row rows[] = { { "neg_bare_local", "package main;\n" "export fn main() i32 = {\n" "\tlet x: opaque;\n" "\treturn 0;\n" "};\n" }, { "neg_param", "package main;\n" "fn f(x: opaque) i32 = { return 0; };\n" "export fn main() i32 = { return 0; };\n" }, /* * A declaration isolates the return-type guard. A function body returning * 0 would add an unrelated untyped_int-to-opaque rejection. */ { "neg_return", "package main;\n" "fn f() opaque;\n" "export fn main() i32 = { return 0; };\n" }, { "neg_struct_field", "package main;\n" "type S = struct { x: opaque };\n" "export fn main() i32 = { return 0; };\n" }, { "neg_array_elem", "package main;\n" "export fn main() i32 = {\n" "\tlet a: [4]opaque;\n" "\treturn 0;\n" "};\n" }, /* * The cast keeps this row on the slice-index guard rather than creating * a bare opaque local. */ { "neg_slice_index", "package main;\n" "export fn main() i32 = {\n" "\tlet s: []opaque;\n" "\tlet v: i32 = s[0]: i32;\n" "\treturn v;\n" "};\n" }, { "neg_tuple_member", "package main;\n" "export fn main() i32 = {\n" "\tlet t: (opaque, i32);\n" "\treturn 0;\n" "};\n" }, { "neg_tagged_variant", "package main;\n" "export fn main() i32 = {\n" "\tlet x: (opaque | i32);\n" "\treturn 0;\n" "};\n" }, }; static int run_row(const char *driver, const struct row *r, int i) { char tmpdir[64] = "/tmp/wcopg_XXXXXX"; if (mkdtemp(tmpdir) == NULL) { perror("opaque-guard: mkdtemp"); return -1; } char src[128], outbin[128], sepwork[160], rmcmd[192], cmd[2048]; snprintf(src, sizeof src, "%s/wcopg_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wcopg_%d_%d", tmpdir, getpid(), i); snprintf(sepwork, sizeof sepwork, "%s.sepwork", outbin); snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", sepwork); int result = -1; FILE *f = fopen(src, "wb"); if (!f) goto cleanup; fputs(r->src, f); fclose(f); snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>&1", driver, outbin, src); int rc = runwait(cmd); int bad = (rc == 0); if (bad) { fprintf(stderr, "opaque-guard[%s]: build unexpectedly succeeded\n", r->label); } result = bad ? -1 : 0; cleanup: { int cleanbad = 0; if (unlink(src) != 0 && errno != ENOENT) cleanbad = 1; if (unlink(outbin) != 0 && errno != ENOENT) cleanbad = 1; if (runwait(rmcmd) != 0) cleanbad = 1; if (rmdir(tmpdir) != 0) cleanbad = 1; if (cleanbad) { fprintf(stderr, "opaque-guard[%s]: temporary cleanup failed\n", r->label); result = -1; } } return result; } int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char absbin[512]; if (bin[0] != '/') { char cwd[256]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } char cdrv[640]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); int n = (int)(sizeof rows / sizeof rows[0]); int fail = 0; for (int i = 0; i < n; i++) { if (run_row(cdrv, &rows[i], i) != 0) fail++; } if (fail) { fprintf(stderr, "opaque-guards: %d/%d row(s) failed\n", fail, n); return 1; } printf("opaque-guards: %d/%d ok\n", n, n); return 0; }