test: migrate Fam13 misc checker/coercion value tests to @test (#5-C6)
Final fold-2 chunk. The 12 Fam13 single-file value drivers move from test/wcc/*_run.c into in-language @test row-tables under test/lang/: - value rows -> test/lang/*_test.ww (12 files) - reject rows -> runww //ww:error carriers (13, dual-stage non-vacuous) - nullable abort rows -> runww //ww:run-exit 1 carriers (3) - 953_globalslice_arg -> _runonly (cs!=ww checker divergence, #28) - 788 value_not_type_neg + 953_arrlit_slice reject_assign kept as slim rc-only .c pins (divergent-diag dual-reject, mutation-gated); 788 #29 Coverage parity verified row-by-row vs each retired driver; advisor- ratified carve taxonomy; two-round reviewed. Floor ratchet follows.
This commit is contained in:
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* 782_fieldfn_leaf_collide_run — project #211 close, the cgen sibling of
|
||||
* #208 (the checker fix).
|
||||
*
|
||||
* SHAPE: a value-receiver fn-pointer FIELD call `s.pull(...)` whose leaf
|
||||
* name `pull` COLLIDES with a same-module GLOBAL fn `pull` of a DIFFERENT
|
||||
* register shape — the field returns a tagged `(i64 | sentinel)` (2-word
|
||||
* AX=tag/DX=word0 ABI), the global returns a scalar `i64` (1-word AX).
|
||||
* cstage resolves the call result from the CALLEE's own type (the field's
|
||||
* fn type, cmd/wcc/check.c:1433/1490 `n->type = u->ret`), so it reads the
|
||||
* tagged 2-word return correctly. PRE-#211 wwstage cgen re-derived the
|
||||
* source shape by NAME (rhstaggedabicall → fnretlookupmod over the leaf,
|
||||
* with the receiver VARIABLE name as the "module"), mis-bound the scalar
|
||||
* global, and widened a 1-word AX into the tagged slot — a silent cs≠ww
|
||||
* miscompile (wrong runtime + divergent .s).
|
||||
*
|
||||
* THE FIX (#211, align wwstage UP to cstage, structural): rhstaggedabicall
|
||||
* (selfhost/cmd/wcc/cgenutil.ww) reads the checker-stamped result type off
|
||||
* the N_CALL node (`src.type_`, which check.ww's N_CALL stamps to the
|
||||
* callee fn-type's ret in both the SK_FN and the fn-VALUE/field paths)
|
||||
* instead of the leaf-name lookup. Mirrors harec selecting by interned
|
||||
* type id, not name (ref/harec/src/types.c:714). #211 was MASKED until
|
||||
* #208 landed: pre-#208 the wwstage checker rejected this shape ("is/as:
|
||||
* operand is not a tagged union") before cgen ran, so the cgen path was
|
||||
* unreachable.
|
||||
*
|
||||
* row | what it pins
|
||||
* ---------------------+----------------------------------------------
|
||||
* field_vs_global_leaf | s.pull(&s,5) routes to the FIELD impl
|
||||
* | (srcpull → 105), NOT the same-named global
|
||||
* | `pull` (→ 14). `r is i64` / `r as i64` prove
|
||||
* | the result is the tagged field type. The
|
||||
* | global stays live (g = pull(3) = 14) so the
|
||||
* | collision is real, not dead-code-elided.
|
||||
*
|
||||
* Graduated to STAGE_CS | STAGE_WW + byte_id on #211 close: builds + runs
|
||||
* on both stages (exit 42) and asserts cs.s == ww.s (rule-10). A red means
|
||||
* #211 regressed — wwstage re-derived the call-result shape by leaf name
|
||||
* again, or the cstage call-result resolution broke.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define STAGE_CS 1
|
||||
#define STAGE_WW 2
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit;
|
||||
int stage_mask;
|
||||
int byte_id; /* assert cs.s == ww.s */
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "field_vs_global_leaf",
|
||||
"package main;\n"
|
||||
"type sentinel = void;\n"
|
||||
"fn pull(x: i64) i64 = {\n"
|
||||
" return x + 11i64;\n"
|
||||
"};\n"
|
||||
"type src = struct {\n"
|
||||
" pull: fn(s: *src, k: i64) (i64 | sentinel),\n"
|
||||
"};\n"
|
||||
"fn srcpull(s: *src, k: i64) (i64 | sentinel) = {\n"
|
||||
" return k + 100i64;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: src;\n"
|
||||
" s.pull = srcpull;\n"
|
||||
" let g: i64 = pull(3i64);\n"
|
||||
" let r = s.pull(&s, 5i64);\n"
|
||||
" let out: i64 = -1i64;\n"
|
||||
" if (r is i64) { out = r as i64; };\n"
|
||||
" if (out == 105i64 && g == 14i64) { return 42; };\n"
|
||||
" return 1i32;\n"
|
||||
"};\n",
|
||||
42,
|
||||
STAGE_CS | STAGE_WW, 1 },
|
||||
};
|
||||
|
||||
static int
|
||||
write_source(const char *path, const char *src)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Per-row tmpdir cleanup. ww_ww writes intermediates next to the source
|
||||
* (filed task #15); cstage ww does too. Sweep then rmdir. */
|
||||
static void
|
||||
cleanup_tmp(const char *tmpdir, const char *base)
|
||||
{
|
||||
char p[1024];
|
||||
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
|
||||
if (system(p)) {} /* best-effort */
|
||||
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
|
||||
rmdir(tmpdir);
|
||||
}
|
||||
|
||||
static int
|
||||
build_via_driver(const char *driver, const char *tmpdir, const char *src)
|
||||
{
|
||||
char cmd[2048];
|
||||
/* #93 sep layout: emit asm to <src-stem>.sepwork/__root.s; pin
|
||||
* WW_PKGCACHE under tmpdir so out/.pkgcache is untouched. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
|
||||
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
|
||||
tmpdir, src, tmpdir, driver);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
/* run_row — build via driver, run the binary, return exit (or -1 on
|
||||
* build failure). */
|
||||
static int
|
||||
run_row(const char *driver, const struct row *r, int seq)
|
||||
{
|
||||
char tmpdir[256], src[512], base[64], outbin[768];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/ffl_%d_d_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main782");
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
|
||||
mkdir(tmpdir, 0755);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
|
||||
int rc;
|
||||
if (build_via_driver(driver, tmpdir, src) == 0) {
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||
rc = runwait(outbin);
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
|
||||
* ww_ww writing intermediates next to the source doesn't clobber the
|
||||
* cstage .s (CLAUDE.md rule 14 phase split). Mirror of 783's helper. */
|
||||
static int
|
||||
asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
|
||||
int seq)
|
||||
{
|
||||
char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512];
|
||||
snprintf(tdc, sizeof tdc, "/tmp/ffl_%d_c_%d", getpid(), seq);
|
||||
snprintf(tdw, sizeof tdw, "/tmp/ffl_%d_w_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main782");
|
||||
mkdir(tdc, 0755);
|
||||
mkdir(tdw, 0755);
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
|
||||
int rc = -1;
|
||||
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
|
||||
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
|
||||
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tdw, base);
|
||||
if (write_source(src, r->src) != 0) goto out;
|
||||
if (build_via_driver(wdrv, tdw, src) != 0) goto out;
|
||||
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base);
|
||||
|
||||
FILE *fc = fopen(cs, "rb");
|
||||
FILE *fw = fopen(ws, "rb");
|
||||
if (fc && fw) {
|
||||
rc = 0;
|
||||
for (;;) {
|
||||
int a = fgetc(fc);
|
||||
int b = fgetc(fw);
|
||||
if (a != b) { rc = -1; break; }
|
||||
if (a == EOF) break;
|
||||
}
|
||||
}
|
||||
if (fc) fclose(fc);
|
||||
if (fw) fclose(fw);
|
||||
out:
|
||||
cleanup_tmp(tdc, base);
|
||||
cleanup_tmp(tdw, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640], wdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0, seq = 0;
|
||||
int wwpresent = (access(wdrv, X_OK) == 0);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
|
||||
if (r->stage_mask & STAGE_CS) {
|
||||
total++;
|
||||
int got = run_row(cdrv, r, seq++);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr, "fieldfn_leaf_collide[cs][%s]: exit=%d want=%d\n",
|
||||
r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (wwpresent && (r->stage_mask & STAGE_WW)) {
|
||||
total++;
|
||||
int got = run_row(wdrv, r, seq++);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr, "fieldfn_leaf_collide[ww][%s]: exit=%d want=%d\n",
|
||||
r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
if (r->byte_id) {
|
||||
total++;
|
||||
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
|
||||
fprintf(stderr, "fieldfn_leaf_collide[byte-id][%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "fieldfn_leaf_collide: %d/%d checks failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("fieldfn_leaf_collide: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,52 +1,26 @@
|
||||
/*
|
||||
* 788_type_value_shadow_run — project #225 close. A value binding that
|
||||
* shadows a same-named TYPE must NOT hide that type in type-annotation
|
||||
* or cast position. ww keeps type and value namespaces separate;
|
||||
* wwstage already did, cstage did not.
|
||||
* 788_type_value_shadow_run -- SLIMMED to the one irreducible asymmetric row.
|
||||
*
|
||||
* Pre-fix cstage rejected valid Hare code that wwstage + Hare accept:
|
||||
* The #225 type/value-namespace corpus migrated to fold-2 homes (#5-C6): the
|
||||
* value rows (param_and_cast, cast_local) -> test/lang/type_value_shadow_test.ww
|
||||
* (@test + T2 byte-id).
|
||||
*
|
||||
* type off = i64;
|
||||
* fn f(off: off) i64 = { return off: off; };
|
||||
* value_not_type_neg CANNOT move to a runww //ww:error carrier: both stages
|
||||
* REJECT a value name in type position with no same-named type, but with
|
||||
* DIVERGENT diagnostics -- cstage emits a clean "unknown type 'q'" while wwstage
|
||||
* trips an internal "asserttyped: cast" guard (filed task #29: a checker-quality
|
||||
* cs!=ww, wwstage to align DOWN to cstage's clean diagnostic per rule 10). No
|
||||
* honest shared substring exists, so the #20-non-vacuity ERROR carrier (which
|
||||
* demands the SAME substring in both stages) is inexpressible. A build-reject
|
||||
* also emits no .s, so there is no byte-id surface (ken). It stays a slim C pin
|
||||
* asserting rc!=0 on BOTH stages.
|
||||
*
|
||||
* cmd/wcc/check.c resolve_typename resolved the type name via the
|
||||
* kind-blind scope_lookup_prefer, which returns the first NAME match
|
||||
* of ANY kind (innermost→outermost). The param `off` (SK_PARAM, inner
|
||||
* scope) shadowed the global `type off` (SK_TYPE, scope 0): the inner
|
||||
* value won, resolve_typename saw a non-SK_TYPE → nil → "unknown type
|
||||
* 'off'". The type binding was present; the lookup just couldn't see
|
||||
* past the shadowing value.
|
||||
*
|
||||
* Fix (additive, cstage only — aligning UP to wwstage per rule 10):
|
||||
* a kind-filtered scope_lookup_type (cmd/wcc/sym.c) that skips every
|
||||
* non-SK_TYPE Sym and keeps scanning, returning the innermost SK_TYPE
|
||||
* of that name. resolve_typename calls it instead of scope_lookup_prefer.
|
||||
* Both type-annotation (param type) and cast-target (`expr: T`, the
|
||||
* N_CAST rhs) route through resolve_type→N_TNAME→resolve_typename, so
|
||||
* the single swap covers both positions.
|
||||
*
|
||||
* ROW POLARITY: both rows build + run on BOTH stages and assert
|
||||
* cs.s == ww.s (rule-10). The fix is byte-id-NEUTRAL by construction —
|
||||
* the new branch fires only on the pre-fix "unknown type" error path,
|
||||
* which no passing corpus reaches.
|
||||
* param_and_cast — the canonical repro: a param named `off` shadows
|
||||
* `type off`, used both as the param's own type and
|
||||
* as a cast target inside the body.
|
||||
* cast_local — a `let` value shadows `type t`, then `v: t` casts
|
||||
* through the shadowed type name in the same scope.
|
||||
* value_not_type_neg — the inverse guard: a value name with NO
|
||||
* same-named type in any scope must still be rejected
|
||||
* in type position. Proves the kind filter only looks
|
||||
* PAST a value to a real type, never promotes the
|
||||
* value itself. Both stages must fail the build.
|
||||
*
|
||||
* Not covered (not constructible): a fn-name shadowing a type — fns and
|
||||
* types share the flat global scope and dedup on (name, mod), so a
|
||||
* same-named global `fn`/`type` pair is a "duplicate fn" error, never a
|
||||
* shadow. SK_PARAM + SK_VAR are the only value kinds that can shadow.
|
||||
*
|
||||
* GATE: must stay GREEN. Red on cstage means the kind filter regressed;
|
||||
* red on byte-id means the two stages diverged on the shadow path.
|
||||
* Non-vacuity self-check (the pin CAN go RED): the SAME name `q` is also built
|
||||
* as a real TYPE in `valid_src`, which BOTH stages must ACCEPT (rc==0). If the
|
||||
* harness reported reject for everything, that control would fail; it proves the
|
||||
* pin discriminates accept-vs-reject, not "all builds error". Mutation-checked:
|
||||
* giving `value_not_type_neg` a real `type q = i32;` flips both stages to accept
|
||||
* -> the reject leg goes RED (verified).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -63,150 +37,47 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define STAGE_CS 1
|
||||
#define STAGE_WW 2
|
||||
/* value_not_type_neg: `q` is a VALUE (SK_VAR) in type/cast position with no
|
||||
* same-named type in any scope -- both stages reject (cstage "unknown type
|
||||
* 'q'", wwstage asserttyped:cast, #29). */
|
||||
static const char *reject_src =
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let q: i32 = 3;\n"
|
||||
" return q: q;\n"
|
||||
"};\n";
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit;
|
||||
int stage_mask;
|
||||
int byte_id;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "param_and_cast",
|
||||
"package main;\n"
|
||||
"type off = i64;\n"
|
||||
"fn f(off: off) i64 = { return off: off; };\n"
|
||||
"export fn main() i32 = { return f(5): i32; };\n",
|
||||
5, STAGE_CS | STAGE_WW, 1 },
|
||||
|
||||
{ "cast_local",
|
||||
"package main;\n"
|
||||
"type t = i32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t: i32 = 7;\n"
|
||||
" return t: t;\n"
|
||||
"};\n",
|
||||
7, STAGE_CS | STAGE_WW, 1 },
|
||||
|
||||
/* NEG: a value in type position with NO same-named type must still
|
||||
* error. Guards the inverse of the fix — scope_lookup_type must
|
||||
* never PROMOTE the value `q` (SK_VAR) into a type, only look PAST
|
||||
* a value to a real type that is genuinely there. want_exit -1 =
|
||||
* build fails on both stages (cstage "unknown type 'q'"). */
|
||||
{ "value_not_type_neg",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let q: i32 = 3;\n"
|
||||
" return q: q;\n"
|
||||
"};\n",
|
||||
-1, STAGE_CS | STAGE_WW, 0 },
|
||||
};
|
||||
|
||||
static void
|
||||
cleanup_tmp(const char *tmpdir, const char *base)
|
||||
{
|
||||
char p[1024];
|
||||
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
|
||||
/* #93: the sep scratch dir + the tmpdir-pinned pkgcache. */
|
||||
snprintf(p, sizeof p, "rm -rf %s/%s.sepwork %s/pkgc", tmpdir, base, tmpdir);
|
||||
if (system(p)) {} /* best-effort */
|
||||
rmdir(tmpdir);
|
||||
}
|
||||
/* Vacuity control: the SAME name `q` bound as a real TYPE -- both stages accept.
|
||||
* Proves the pin distinguishes accept from reject. */
|
||||
static const char *valid_src =
|
||||
"package main;\n"
|
||||
"type q = i32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return 3: q;\n"
|
||||
"};\n";
|
||||
|
||||
/* build_only -- write `src`, build via `driver` (no run). Returns the build
|
||||
* exit code (0 = accepted, nonzero = rejected). */
|
||||
static int
|
||||
write_source(const char *path, const char *src)
|
||||
build_only(const char *driver, const char *src, int seq)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
char tmpdir[96], srcp[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tvs_%d_%d", getpid(), seq);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir);
|
||||
snprintf(outbin, sizeof outbin, "%s/c", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(srcp, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
build_via_driver(const char *driver, const char *tmpdir, const char *src)
|
||||
{
|
||||
char cmd[2048];
|
||||
/* #93 sep layout: `--sep -o <src-stem>` relocates artifacts to
|
||||
* <stem>.sepwork/; WW_PKGCACHE is pinned under tmpdir so the shared
|
||||
* out/.pkgcache is untouched. The value_not_type_neg row still fails:
|
||||
* a checker error makes w6c return nonzero regardless of placement. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && b='%s'; WW_PKGCACHE=%s/pkgc timeout 180 %s build --sep "
|
||||
"-o \"${b%%.ww}\" \"$b\" 2>/dev/null",
|
||||
tmpdir, src, tmpdir, driver);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
/* run_row — build via driver, run the binary, return exit (or -1 on
|
||||
* build failure). */
|
||||
static int
|
||||
run_row(const char *driver, const struct row *r, int seq)
|
||||
{
|
||||
char tmpdir[256], src[512], base[64], outbin[768];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tvs_%d_d_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main788");
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
|
||||
mkdir(tmpdir, 0755);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
|
||||
int rc;
|
||||
if (build_via_driver(driver, tmpdir, src) == 0) {
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||
rc = runwait(outbin);
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
|
||||
* ww_ww writing intermediates next to the source doesn't clobber the
|
||||
* cstage .s (CLAUDE.md rule 14 phase split). */
|
||||
static int
|
||||
asm_byte_identical(const char *cdrv, const char *wdrv, const struct row *r,
|
||||
int seq)
|
||||
{
|
||||
char src[512], tdc[256], tdw[256], base[64], cs[512], ws[512];
|
||||
snprintf(tdc, sizeof tdc, "/tmp/tvs_%d_c_%d", getpid(), seq);
|
||||
snprintf(tdw, sizeof tdw, "/tmp/tvs_%d_w_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main788");
|
||||
mkdir(tdc, 0755);
|
||||
mkdir(tdw, 0755);
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tdc, base);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
|
||||
int rc = -1;
|
||||
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
|
||||
snprintf(cs, sizeof cs, "%s/%s.sepwork/__root.s", tdc, base);
|
||||
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tdw, base);
|
||||
if (write_source(src, r->src) != 0) goto out;
|
||||
if (build_via_driver(wdrv, tdw, src) != 0) goto out;
|
||||
snprintf(ws, sizeof ws, "%s/%s.sepwork/__root.s", tdw, base);
|
||||
|
||||
FILE *fc = fopen(cs, "rb");
|
||||
FILE *fw = fopen(ws, "rb");
|
||||
if (fc && fw) {
|
||||
rc = 0;
|
||||
for (;;) {
|
||||
int a = fgetc(fc);
|
||||
int b = fgetc(fw);
|
||||
if (a != b) { rc = -1; break; }
|
||||
if (a == EOF) break;
|
||||
}
|
||||
}
|
||||
if (fc) fclose(fc);
|
||||
if (fw) fclose(fw);
|
||||
out:
|
||||
cleanup_tmp(tdc, base);
|
||||
cleanup_tmp(tdw, base);
|
||||
return rc;
|
||||
snprintf(cmd, sizeof cmd, "timeout 20 %s build -o %s %s >/dev/null 2>&1",
|
||||
driver, outbin, srcp);
|
||||
int brc = runwait(cmd);
|
||||
runwait(rmcmd);
|
||||
return brc;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -214,58 +85,45 @@ main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
char absbin[512];
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640], wdrv[640];
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0, seq = 0;
|
||||
int wwpresent = (access(wdrv, X_OK) == 0);
|
||||
int fail = 0, seq = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
/* reject leg: both stages must REJECT (rc!=0). */
|
||||
if (build_only(cdrv, reject_src, seq++) == 0) {
|
||||
fprintf(stderr, "type_value_shadow[cs]: value_not_type_neg ACCEPTED (want reject)\n");
|
||||
fail++;
|
||||
}
|
||||
if (wwpresent && build_only(wdrv, reject_src, seq++) == 0) {
|
||||
fprintf(stderr, "type_value_shadow[ww]: value_not_type_neg ACCEPTED (want reject)\n");
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (r->stage_mask & STAGE_CS) {
|
||||
total++;
|
||||
int got = run_row(cdrv, r, seq++);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr, "type_value_shadow[cs][%s]: exit=%d want=%d\n",
|
||||
r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (wwpresent && (r->stage_mask & STAGE_WW)) {
|
||||
total++;
|
||||
int got = run_row(wdrv, r, seq++);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr, "type_value_shadow[ww][%s]: exit=%d want=%d\n",
|
||||
r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
if (r->byte_id) {
|
||||
total++;
|
||||
if (asm_byte_identical(cdrv, wdrv, r, seq++) != 0) {
|
||||
fprintf(stderr, "type_value_shadow[byte-id][%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* vacuity control: both stages must ACCEPT (rc==0). */
|
||||
if (build_only(cdrv, valid_src, seq++) != 0) {
|
||||
fprintf(stderr, "type_value_shadow[cs]: vacuity control REJECTED (pin may be vacuous)\n");
|
||||
fail++;
|
||||
}
|
||||
if (wwpresent && build_only(wdrv, valid_src, seq++) != 0) {
|
||||
fprintf(stderr, "type_value_shadow[ww]: vacuity control REJECTED (pin may be vacuous)\n");
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "type_value_shadow: %d/%d checks failed\n", fail, total);
|
||||
fprintf(stderr, "type_value_shadow: %d checks failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("type_value_shadow: %d/%d ok\n", total, total);
|
||||
printf("type_value_shadow: value_not_type_neg pin ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
* 813_arrlit_infer_elem_run — #103 + #108 (+ #104 + the m8_slice
|
||||
* acceptance gap): an INFERRED let defaults its untyped-int to `int`
|
||||
* (8B machine word) on BOTH stages, byte-identically and at runtime.
|
||||
*
|
||||
* THE BUG (cstage was the truncating side — #263 polarity):
|
||||
* cstage type_default(TY_UNTYPED_INT) returned ty_i32 (4B). For a
|
||||
* scalar `let x = <v>` and an inferred array `let a = [<v>, ...]`
|
||||
* (no annotation) the literal defaulted to i32 — silently TRUNCATING
|
||||
* any value > 2^31 (5000000000 → 705032704) and striding arrays at 4.
|
||||
* wwstage kept the element as raw untyped_int (size 0), which sized
|
||||
* INCONSISTENTLY across cgen consumers: the array STORE strode the 8
|
||||
* sentinel (so the value width was right) but letslotsize under-
|
||||
* allocated the frame (SEGV) and cgindex strode the READ at 1 — so
|
||||
* `let a = [..]` SEGV'd on wwstage and the scalar ran correct only by
|
||||
* the 8B-store accident. The two stages were each wrong differently.
|
||||
*
|
||||
* THE FIX (one root, both stages, FUSE):
|
||||
* - cstage type.c type_default(TY_UNTYPED_INT) i32 -> int; the empty
|
||||
* arrlit fallback check.c ty_i32 -> ty_int (symmetric, count-0
|
||||
* neutral). int = machine word = 8B (Go-style; MEMORY
|
||||
* project_int_machine_word_derived_limits) — Hare lowers a flexible
|
||||
* iconst to `int`, never a fixed i32 (ref/harec/src/types.c:835).
|
||||
* - wwstage check.ww exprtype N_ARRLIT: default the inferred
|
||||
* element's UNTYPED flavor to its concrete type (untyped_int->int,
|
||||
* _float->f64, _str->str, _rune->rune, _bool->bool), the empty-elt
|
||||
* fallback "i32"->"int", and stamp the synthesized N_TARRAY's
|
||||
* .type_ — so slotsize / elemsizeofc / letslotsize all read its
|
||||
* real [N]int size (32 for [4]int), routing through the type table
|
||||
* (rule-13) instead of the nil/0/sentinel fallbacks. SSoT — no
|
||||
* letslotsize special-case.
|
||||
*
|
||||
* POLARITY TEETH: the wide rows use 5000000000 ( > 2^31). A small-value
|
||||
* row would pass both stages by luck (gate-blind); only a > 2^31 value
|
||||
* distinguishes the truncating i32 default from the correct int.
|
||||
*
|
||||
* COVERAGE — the two direct repros plus the FIVE corpus movers ken's v2
|
||||
* re-census named (each converges 0/0 byte-id + run-correct, NONE
|
||||
* both-wrong): m2_while (#108 scalar, alias-bool loop), m8_range1/2
|
||||
* (#104 for-range elem over alias / 2-level-alias array), m8_slice1/2
|
||||
* (#103 SEGV + slice-init acceptance over alias / 2-level-alias slice).
|
||||
* Plus the annotated controls [4]i32 (stride-4) and [4]int (stride-8),
|
||||
* which the fix must leave byte-identical — it touches ONLY the inferred
|
||||
* untyped default, never an explicit element type.
|
||||
*
|
||||
* row | shape | want
|
||||
* -----------------+---------------------------------------------+-----
|
||||
* arr_wide | let a=[5e9,2,3,4]; a[0]==5e9 && a[3]==4 | 0
|
||||
* scalar_wide | let x=5e9; x==5e9 (#108 teeth) | 0
|
||||
* arr_small | let a=[10,20,30,40]; a[2] | 30
|
||||
* m2_while | #108 scalar via alias-bool loop counter | 0
|
||||
* m8_range1 | #104 for-range elem over alias [4]int | 0
|
||||
* m8_range2 | #104 for-range elem over 2-level-alias | 0
|
||||
* m8_slice1 | #103 inferred a + slice s:sl=a[1:3] | 0
|
||||
* m8_slice2 | #103 + 2-level-alias slice + re-slice | 0
|
||||
* ctrl_i32 | let a:[4]i32=[1,2,3,4]; a[3] (stride-4) | 4
|
||||
* ctrl_int | let a:[4]int=[1,2,3,4]; a[3] (stride-8) | 4
|
||||
*
|
||||
* EACH ROW CARRIES THREE DIMENSIONS (684/802 model):
|
||||
* (a) cstage `ww build` + run, asserting the exit — pins the converged
|
||||
* asm is runtime-correct (real values, no truncation/SEGV).
|
||||
* (b) wwstage `ww_ww build` + run (gated on w6c_ww) — pins the ww SEGV
|
||||
* is gone and the value round-trips.
|
||||
* (c) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge (rule-10).
|
||||
*
|
||||
* GATE POLARITY: must stay GREEN. A wrong exit means the inferred
|
||||
* default regressed to a truncating/under-strided type; a byte-id FAIL
|
||||
* means the stages diverged.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* #103 array, > 2^31 element — truncation + frame teeth. */
|
||||
{ "arr_wide",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a = [5000000000, 2, 3, 4];\n"
|
||||
"\tif (a[0] != 5000000000) { return 1; };\n"
|
||||
"\tif (a[3] != 4) { return 2; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* #108 scalar, > 2^31 — the cstage general truncation teeth. */
|
||||
{ "scalar_wide",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet x = 5000000000;\n"
|
||||
"\tif (x != 5000000000) { return 1; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* basic inferred array — frame + stride sanity (mutation: a wrong
|
||||
* stride/slot reads a neighbouring word, not 30). */
|
||||
{ "arr_small",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a = [10, 20, 30, 40];\n"
|
||||
"\treturn a[2]: i32;\n"
|
||||
"};\n",
|
||||
30 },
|
||||
|
||||
/* corpus mover m2_while (#108 scalar via an alias-bool loop). */
|
||||
{ "m2_while",
|
||||
"package main;\n"
|
||||
"type myb = bool;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet b: myb = true;\n"
|
||||
"\tlet i = 0;\n"
|
||||
"\tfor (b) {\n"
|
||||
"\t\ti = i + 1;\n"
|
||||
"\t\tif (i >= 3) { b = false; };\n"
|
||||
"\t};\n"
|
||||
"\tif (i != 3) { return 1; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* corpus mover m8_range1 (#104 for-range elem over alias array). */
|
||||
{ "m8_range1",
|
||||
"package main;\n"
|
||||
"type arr = [4]int;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a: arr = [1, 2, 3, 4];\n"
|
||||
"\tlet sum = 0;\n"
|
||||
"\tfor (let x .. a) {\n"
|
||||
"\t\tsum = sum + x;\n"
|
||||
"\t};\n"
|
||||
"\tif (sum != 10) { return 1; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* corpus mover m8_range2 (#104 for-range elem over 2-level alias). */
|
||||
{ "m8_range2",
|
||||
"package main;\n"
|
||||
"type arr = [4]int;\n"
|
||||
"type arr2 = arr;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a: arr2 = [1, 2, 3, 4];\n"
|
||||
"\tlet sum = 0;\n"
|
||||
"\tfor (let x .. a) {\n"
|
||||
"\t\tsum = sum + x;\n"
|
||||
"\t};\n"
|
||||
"\tif (sum != 10) { return 1; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* corpus mover m8_slice1 (#103 inferred array + alias-slice init). */
|
||||
{ "m8_slice1",
|
||||
"package main;\n"
|
||||
"type sl = []int;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a = [10, 20, 30, 40];\n"
|
||||
"\tlet s: sl = a[1:3];\n"
|
||||
"\tif (s.len != 2) { return 1; };\n"
|
||||
"\tif (s[0] != 20) { return 2; };\n"
|
||||
"\tif (s[1] != 30) { return 3; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* corpus mover m8_slice2 (#103 + 2-level-alias slice + re-slice). */
|
||||
{ "m8_slice2",
|
||||
"package main;\n"
|
||||
"type sl = []int;\n"
|
||||
"type sl2 = sl;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a = [10, 20, 30, 40];\n"
|
||||
"\tlet s: sl2 = a[1:3];\n"
|
||||
"\tif (s.len != 2) { return 1; };\n"
|
||||
"\tif (s[0] != 20) { return 2; };\n"
|
||||
"\tlet t = s[0:1];\n"
|
||||
"\tif (t[0] != 20) { return 3; };\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* CONTROL: annotated [4]i32 — must stay i32/stride-4, byte-id,
|
||||
* UNTOUCHED by the inferred-default fix. */
|
||||
{ "ctrl_i32",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a: [4]i32 = [1, 2, 3, 4];\n"
|
||||
"\treturn a[3];\n"
|
||||
"};\n",
|
||||
4 },
|
||||
|
||||
/* CONTROL: annotated [4]int — int/stride-8, byte-id. */
|
||||
{ "ctrl_int",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a: [4]int = [1, 2, 3, 4];\n"
|
||||
"\treturn a[3]: i32;\n"
|
||||
"};\n",
|
||||
4 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/aie_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/aie_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/aie_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return got;
|
||||
}
|
||||
|
||||
/* asm_byte_identical — w6c vs w6c_ww .s for the same source must match. */
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[64], cs[72], ws[72], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/aie_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/aie_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/aie_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *fc = fopen(cs, "rb");
|
||||
FILE *fw = fopen(ws, "rb");
|
||||
int rc = 0;
|
||||
if (!fc || !fw) {
|
||||
rc = -1;
|
||||
} else {
|
||||
for (;;) {
|
||||
int a = fgetc(fc);
|
||||
int b = fgetc(fw);
|
||||
if (a != b) { rc = -1; break; }
|
||||
if (a == EOF) break;
|
||||
}
|
||||
}
|
||||
if (fc) fclose(fc);
|
||||
if (fw) fclose(fw);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1100], wdrv[1100];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr, "arrlit_infer_elem: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"arrlit_infer_elem[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0)
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"arrlit_infer_elem: %d/%d fixtures failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("arrlit_infer_elem: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* 911_continue_run — runtime + byte-id net for #138: `continue` in a
|
||||
* loop with a post-step (3-clause C-style `for (init; cond; post)` OR
|
||||
* Hare-range `for (let x .. xs)` with its implicit `i+=1`) must run
|
||||
* the post-step BEFORE re-testing the cond / bound. Pre-fix the
|
||||
* continue-target was the loop top, which SKIPPED the post-step → the
|
||||
* value that triggered continue never advanced → infinite loop.
|
||||
*
|
||||
* Pre-fix BOTH STAGES emitted identical buggy asm (`JMP loop_top` for
|
||||
* continue, post-step JMP'd over) — cs==ww byte-id held → 990-997 gate
|
||||
* was BLIND to the miscompile. Found by impl-strconv-fold2 during the
|
||||
* fold-3 decimal.ha port: leftshift_newdigits uses `for (let i: u32 =
|
||||
* 0u32; i < n; i += 1u32) { ... else if (d.digits[i]==p5[i]) continue;
|
||||
* ... }`; pre-fix that path infinite-looped at the first equal digit.
|
||||
*
|
||||
* Fix (both stages): allocate a dedicated `post` (3-clause) / `rpost`
|
||||
* (range) label; continue jumps to that label; the label emits before
|
||||
* the post-step; fall-through naturally hits it too. For 1-clause `for
|
||||
* (cond)` with no post-step, the cont-target stays = loop-top
|
||||
* (unchanged from pre-#138, byte-id preserved on the corpus's
|
||||
* 1-clause shape).
|
||||
*
|
||||
* Rows cover the 3 shapes the fix targets PLUS a 1-clause regression
|
||||
* row asserting the byte-id surface for the 1-clause path is
|
||||
* unchanged. cstage `ww build` + run for runtime; w6c vs w6c_ww `.s`
|
||||
* cmp for rule-10 byte-id.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* The lead's repro: 3-clause for with continue. Pre-fix infinite
|
||||
* loop on i=2; post-fix count=4 (i=0,1,3,4 all increment count). */
|
||||
{ "for3_skip_one",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let count: i32 = 0;\n"
|
||||
" for (let i: i32 = 0; i < 5; i += 1) {\n"
|
||||
" if (i == 2) { continue; };\n"
|
||||
" count += 1;\n"
|
||||
" };\n"
|
||||
" return count;\n"
|
||||
"};\n", 4 },
|
||||
/* Two skips: continue must advance i correctly each time. count
|
||||
* over i=0,2,4 only (skip 1 and 3); 3 hits × 10 = 30. */
|
||||
{ "for3_skip_two",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let c: i32 = 0;\n"
|
||||
" for (let i: i32 = 0; i < 5; i += 1) {\n"
|
||||
" if (i == 1) { continue; };\n"
|
||||
" if (i == 3) { continue; };\n"
|
||||
" c += 10;\n"
|
||||
" };\n"
|
||||
" return c;\n"
|
||||
"};\n", 30 },
|
||||
/* Hare-range form `for (let x .. xs)`. Continue must run the
|
||||
* implicit `i+=1` increment. Pre-fix infinite-loop on first
|
||||
* matching element; post-fix sum over non-skipped values. xs =
|
||||
* [10, 20, 30, 40, 50]; skip 30; sum = 10+20+40+50 = 120. */
|
||||
{ "range_skip",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [5]i32 = [10, 20, 30, 40, 50];\n"
|
||||
" let sum: i32 = 0;\n"
|
||||
" for (let v .. xs) {\n"
|
||||
" if (v == 30) { continue; };\n"
|
||||
" sum += v;\n"
|
||||
" };\n"
|
||||
" return sum;\n"
|
||||
"};\n", 120 },
|
||||
/* 1-clause `for (cond)` with continue. No post-step, so cont-
|
||||
* target stays = loop-top. Pre-#138 emission must be byte-id
|
||||
* preserved — this is the bootstrap shape. Manual post-step
|
||||
* inside the body. count over i=0,1,3,4 (skip i=2) → 4. */
|
||||
{ "for1_continue_byteid",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let count: i32 = 0;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 5) {\n"
|
||||
" let cur: i32 = i;\n"
|
||||
" i += 1;\n"
|
||||
" if (cur == 2) { continue; };\n"
|
||||
" count += 1;\n"
|
||||
" };\n"
|
||||
" return count;\n"
|
||||
"};\n", 4 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "continue: w6c_ww missing — cannot run the "
|
||||
"cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwcont_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
char rmcmd[128];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128];
|
||||
snprintf(src, sizeof src, "%s/wwcont_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { runwait(rmcmd); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char outbin[128];
|
||||
snprintf(outbin, sizeof outbin, "%s/wwcont_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
/* Build with 5s timeout — pre-fix the buggy rows infinite-
|
||||
* looped; post-fix all rows must exit cleanly under it. */
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* timeout wrapper: pre-fix bug = infinite loop; want clean
|
||||
* exit. exit 124 = timeout/hang. */
|
||||
char rcmd[256];
|
||||
snprintf(rcmd, sizeof rcmd, "timeout 5 %s", outbin);
|
||||
int got = runwait(rcmd);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d "
|
||||
"(124 = timeout / pre-fix infinite loop)\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwcont_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwcont_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d continue tests failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("continue: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
* 937_forrange_fieldbase_run — #70: by-value for-range over a NON-IDENT
|
||||
* slice/str base (field chain, indexed element, call result). Pre-#70
|
||||
* the range header stored cgexpr's AX — the DATA POINTER (slice cgexpr
|
||||
* leaves AX=ptr, BX=len, CX=cap) — into the single bound temp, and the
|
||||
* per-iteration code had no non-ident base arm, so the bound reload
|
||||
* doubled as the base: i was compared against the POINTER and the loop
|
||||
* walked off the end (regex.finish, SEGV on the first non-empty
|
||||
* charsets; empty slices coincidentally exited on ptr==0 — latent since
|
||||
* fold 1, BYTE-ID both stages, so the 989 M_ID gate held on
|
||||
* both-wrong-identical). Now: bound = len, base ptr spilled to its own
|
||||
* .rgb slot. Deref bases (*p — the #11 family, cgexpr does NOT deliver
|
||||
* the header) and non-ident ARRAY bases stay LOUD.
|
||||
*
|
||||
* row | shape | want
|
||||
* -------------------+----------------------------------------+-----
|
||||
* field_base | h.xs ([]i64) value + ptr root | 0
|
||||
* field_base_24B | p.names ([]str) — the finish() shape | 0
|
||||
* indexed_base | m[0] ([][]i64 element) | 0
|
||||
* call_base | mk() ([]i64 call result) | 0
|
||||
* empty_field | zero header field exits cleanly | 0
|
||||
* eval_once | base reassigned INSIDE the loop — |
|
||||
* | header captured once (a per-iteration |
|
||||
* | re-read of the field would mis-sum) | 0
|
||||
* reject_deref | *q base stays loud (#11) | err
|
||||
* reject_arr_field | h.arr ([3]i64 field) stays loud | err
|
||||
*
|
||||
* Every K_RUN row also asserts cstage/wwstage asm byte-id. NNN<950,
|
||||
* self-contained (/tmp, no imports).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define K_RUN 0 /* build+run both drivers, exit==want, + cs==ww byte-id */
|
||||
#define K_BUILDERR 1 /* build must FAIL with experr on BOTH drivers (rule 7) */
|
||||
|
||||
struct row { const char *label; const char *src; int want;
|
||||
int kind; const char *experr; };
|
||||
|
||||
/* errlog_has — a BUILDERR row must fail WITH its diagnostic; any other
|
||||
* failure (parse error, crash) is a vacuous reject (940 precedent). */
|
||||
static int
|
||||
errlog_has(const char *path, const char *needle)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
char buf[8192];
|
||||
size_t got = fread(buf, 1, sizeof buf - 1, f);
|
||||
fclose(f);
|
||||
buf[got] = '\0';
|
||||
return strstr(buf, needle) != NULL;
|
||||
}
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "field_base",
|
||||
"package main;\n"
|
||||
"type holder = struct { xs: []i64, name: str };\n"
|
||||
"fn mk() []i64 = {\n"
|
||||
" let xs: []i64;\n"
|
||||
" append(xs, 7);\n"
|
||||
" append(xs, 8);\n"
|
||||
" return xs;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let h: holder = holder { xs = mk(), name = \"h\" };\n"
|
||||
" let s1: i64 = 0;\n"
|
||||
" for (let v .. h.xs) { s1 += v; };\n"
|
||||
" if (s1 != 15) { return 1; };\n"
|
||||
" let p: *holder = &h;\n"
|
||||
" let s2: i64 = 0;\n"
|
||||
" for (let v .. p.xs) { s2 += v; };\n"
|
||||
" if (s2 != 15) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* 24B str-header elements through a ptr field — regex.finish's
|
||||
* exact shape (the live SEGV repro). */
|
||||
{ "field_base_24B",
|
||||
"package main;\n"
|
||||
"type bag = struct { names: []str, n: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let names: []str;\n"
|
||||
" append(names, \"ab\");\n"
|
||||
" append(names, \"cde\");\n"
|
||||
" let b: bag = bag { names = names, n = 2 };\n"
|
||||
" let p: *bag = &b;\n"
|
||||
" let total: i64 = 0;\n"
|
||||
" for (let nm .. p.names) {\n"
|
||||
" total += (len(nm): i64);\n"
|
||||
" };\n"
|
||||
" if (total != 5) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* indexed element base — the task-#57 shape, graduated by #70 */
|
||||
{ "indexed_base",
|
||||
"package main;\n"
|
||||
"fn mk() []i64 = {\n"
|
||||
" let xs: []i64;\n"
|
||||
" append(xs, 7);\n"
|
||||
" append(xs, 8);\n"
|
||||
" return xs;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let m: [][]i64;\n"
|
||||
" append(m, mk());\n"
|
||||
" let s: i64 = 0;\n"
|
||||
" for (let v .. m[0]) { s += v; };\n"
|
||||
" if (s != 15) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "call_base",
|
||||
"package main;\n"
|
||||
"fn mk() []i64 = {\n"
|
||||
" let xs: []i64;\n"
|
||||
" append(xs, 7);\n"
|
||||
" append(xs, 8);\n"
|
||||
" return xs;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: i64 = 0;\n"
|
||||
" for (let v .. mk()) { s += v; };\n"
|
||||
" if (s != 15) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* zero header: the pre-#70 code exited only because ptr==0; the
|
||||
* fixed code must exit because len==0. */
|
||||
{ "empty_field",
|
||||
"package main;\n"
|
||||
"type holder = struct { xs: []i64, name: str };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let h: holder = holder { name = \"e\", ... };\n"
|
||||
" for (let v .. h.xs) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* the range header (base ptr + len) is captured ONCE at loop
|
||||
* entry — Hare evaluates the range operand once. A fix that
|
||||
* re-read the field per iteration would walk b (sum 7+100) and
|
||||
* never see the reassign-then-original discipline. */
|
||||
{ "eval_once",
|
||||
"package main;\n"
|
||||
"type holder = struct { xs: []i64, n: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: []i64;\n"
|
||||
" append(a, 7);\n"
|
||||
" append(a, 8);\n"
|
||||
" let b: []i64;\n"
|
||||
" append(b, 100);\n"
|
||||
" let h: holder = holder { xs = a, n = 0 };\n"
|
||||
" let s: i64 = 0;\n"
|
||||
" for (let v .. h.xs) {\n"
|
||||
" s += v;\n"
|
||||
" h.xs = b;\n"
|
||||
" };\n"
|
||||
" if (s != 15) { return 1; };\n"
|
||||
" if (len(h.xs) != 1) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* deref base: cgexpr on *p does not deliver the AX/BX/CX header
|
||||
* (the #11 deref-spine family) — pre-#70 it crashed or mis-summed
|
||||
* SILENTLY; now loud both stages. */
|
||||
{ "reject_deref",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i64;\n"
|
||||
" append(xs, 1);\n"
|
||||
" let q: *[]i64 = &xs;\n"
|
||||
" let s: i64 = 0;\n"
|
||||
" for (let v .. *q) { s += v; };\n"
|
||||
" return (s: i32);\n"
|
||||
"};\n", 0,
|
||||
K_BUILDERR, "for-range over a deref base unwired (#11)" },
|
||||
/* non-ident ARRAY base: no base spill and a different cgexpr
|
||||
* register shape — loud (rule 7) until a consumer wires it. */
|
||||
{ "reject_arr_field",
|
||||
"package main;\n"
|
||||
"type holder = struct { arr: [3]i64, n: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let h: holder = holder { arr = [1, 2, 3], n = 0 };\n"
|
||||
" let s: i64 = 0;\n"
|
||||
" for (let v .. h.arr) { s += v; };\n"
|
||||
" return (s: i32);\n"
|
||||
"};\n", 0,
|
||||
K_BUILDERR, "for-range over a non-ident array base unwired (#70)" },
|
||||
};
|
||||
|
||||
/* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tfr_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tfr_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tfr_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/tfr_%d_e_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
if (r->kind == K_BUILDERR) {
|
||||
int ok = (brc != 0)
|
||||
&& (r->experr == NULL || errlog_has(errf, r->experr));
|
||||
if (!ok)
|
||||
fprintf(stderr, "row[%s]: %s expected loud builderr "
|
||||
"\"%s\" (brc=%d)\n", r->label, driver,
|
||||
r->experr ? r->experr : "", brc);
|
||||
runwait(rmcmd);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != r->want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, r->want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cs==ww .s byte-id (rule 10). */
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs[96], ws[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/tfr_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/tfr_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/tfr_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
int rc = slurp_eq(cs, ws);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rows[i].kind == K_BUILDERR)
|
||||
continue;
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "forrange_fieldbase: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("forrange_fieldbase: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* 946_append_structlit_evalorder_run — task #59 (#50's eval-order kin):
|
||||
* an append/insert of a STRUCT-LITERAL value used to fill the literal's
|
||||
* fields into the destination slot AFTER cg_append_grow had already bumped
|
||||
* xs.len. So a field expression that reads `len(xs)` saw the POST-grow
|
||||
* length. Hare evaluates the value BEFORE the grow.
|
||||
*
|
||||
* #263 both-wrong-IDENTICAL: cstage and wwstage emitted byte-identical asm
|
||||
* that was wrong on BOTH (exit 6 vs the correct 3) — the asm-diff and
|
||||
* byte-id gates are BLIND to it, so a RUNTIME row is the only net.
|
||||
*
|
||||
* Fix (both stages, byte-identical per rule 10): fill the struct literal
|
||||
* into a fresh per-SITE scratch BEFORE the grow (mirror #50's tagged arm),
|
||||
* then grow, slot, raw-copy scratch->slot. insert() desugars in-place to
|
||||
* append and re-dispatches into THIS exact arm, so it is fixed by
|
||||
* construction — R2 proves the desugar twin (both stages).
|
||||
*
|
||||
* Rows assert RUNTIME on BOTH drivers AND cs==ww .s byte-identical:
|
||||
* append_eval `append(xs, rec{f=len(xs):i64})` x3 -> sum == 3 (pre-grow);
|
||||
* base bug = 6 (post-grow), main returns 1.
|
||||
* insert_eval `insert(xs[1], rec{f=len(xs):i64})` -> inserted.f == 2
|
||||
* (pre-grow len at insert time); base bug = 3.
|
||||
*
|
||||
* All K_RUN: build+run exit 0 on BOTH drivers AND cs==ww byte-identical.
|
||||
* NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling
|
||||
* race does not apply (903/940/945 precedent).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "append_eval",
|
||||
"package main;\n"
|
||||
"type rec = struct { f: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []rec = [];\n"
|
||||
" append(xs, rec{ f = len(xs): i64 });\n"
|
||||
" append(xs, rec{ f = len(xs): i64 });\n"
|
||||
" append(xs, rec{ f = len(xs): i64 });\n"
|
||||
" let s: i64 = xs[0].f + xs[1].f + xs[2].f;\n"
|
||||
" if (s != 3) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "insert_eval",
|
||||
"package main;\n"
|
||||
"type rec = struct { f: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []rec = [];\n"
|
||||
" append(xs, rec{ f = 100 });\n"
|
||||
" append(xs, rec{ f = 200 });\n"
|
||||
" insert(xs[1], rec{ f = len(xs): i64 });\n"
|
||||
" if (xs[1].f != 2) { return 1; };\n"
|
||||
" if (xs[0].f != 100) { return 2; };\n"
|
||||
" if (xs[2].f != 200) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
/*
|
||||
* narrow_neighbor — a sub-8B struct (`{a: i32}`, esz=4, packs at
|
||||
* stride 4) appended to fill the slice exactly to capacity. Catches
|
||||
* BOTH halves of #59 in one row:
|
||||
* pre-fix (pristine 39432f7): field exprs eval post-grow → sum
|
||||
* wrong → returns 1.
|
||||
* 8B-block-copy fix (the over-copy regression): eval-order is
|
||||
* correct but the last slot's 8-byte copy writes 4 bytes PAST
|
||||
* the 32-byte buffer into the adjacent `victim` allocation →
|
||||
* victim[0] clobbered → returns 2.
|
||||
* precise 8/4/2/1 ladder: both correct → 0.
|
||||
* The 8-element fill makes len==cap==8 so the tail over-write lands
|
||||
* past the buffer; `victim` is alloc'd right after xs's buffer (bump
|
||||
* allocator) so the clobber is observable.
|
||||
*/
|
||||
{ "narrow_neighbor",
|
||||
"package main;\n"
|
||||
"type rec = struct { a: i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []rec = [];\n"
|
||||
" append(xs, rec{ a = len(xs): i32 });\n"
|
||||
" let victim: []i64 = [];\n"
|
||||
" append(victim, 1234605616436508552i64);\n"
|
||||
" let i: i32 = 1;\n"
|
||||
" for (i < 8) {\n"
|
||||
" append(xs, rec{ a = len(xs): i32 });\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" let j: i32 = 0;\n"
|
||||
" for (j < 8) { s += xs[j].a; j += 1; };\n"
|
||||
" if (s != 28) { return 1; };\n"
|
||||
" if (victim[0] != 1234605616436508552i64) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
};
|
||||
|
||||
/* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[96], errf[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/ase_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/ase_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/ase_%d_e_%d", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/ase_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != r->want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, r->want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cs==ww .s byte-id (rule 10). */
|
||||
static int
|
||||
byteid(const char *w6c, const char *w6c_ww, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs_s[96], ws_s[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/ase_bi_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "/tmp/ase_bi_%d_%d_cs.s", getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "/tmp/ase_bi_%d_%d_ww.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
int rc = 0;
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", r->label); rc = 1; }
|
||||
else {
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", r->label); rc = 1; }
|
||||
else if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
|
||||
"(rule-10 byte-id)\n", r->label);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
unlink(src); unlink(cs_s); unlink(ws_s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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], wdrv[640], w6c[640], w6c_ww[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr, "append_structlit_evalorder: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(drivers[d].path, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (access(w6c_ww, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (byteid(w6c, w6c_ww, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "append_structlit_evalorder: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("append_structlit_evalorder: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* 949_nullable_assert_run — the `e as T` type assertion on a nullable
|
||||
* `(*T | void)` operand must discriminate POINTER-vs-NULL, not compare
|
||||
* the pointer value against a variant tag index (report item #17, F4).
|
||||
*
|
||||
* Gate-blind hazard: for a nullable operand the slot word IS the
|
||||
* pointer (no tag word), so pre-fix wwstage cgtypeassert emitted
|
||||
* `CMPQ $want,AX; JE ok` against the POINTER and then unwrapped a frame
|
||||
* word past the 8B slot — a valid pointer aborted, a void value
|
||||
* silently PASSED, and the unwrap read garbage. cstage already carried
|
||||
* the nullable arm (cmd/w6c/cgen.c N_TYPEASSERT) and ww's own
|
||||
* cgtypetest carries the matching nullable fold; the fix mirrors that
|
||||
* arm into cgtypeassert. wwstage-only (cstage correct) → after the fix
|
||||
* cs.s == ww.s, so the byte-id row witnesses rule-10 convergence.
|
||||
*
|
||||
* Two assertions per row:
|
||||
* - RUNTIME: build with `ww` (cstage) and `ww_ww` (wwstage), run,
|
||||
* compare exit code. This is what was wrong pre-fix.
|
||||
* - ASM BYTE-ID: compile the same source through `w6c` and `w6c_ww`
|
||||
* and require byte-identical .s (rule 10).
|
||||
*
|
||||
* Rows:
|
||||
* 1. asrt_ptr_ok — valid ptr `as *int` must unwrap and deref.
|
||||
* Pre-fix wwstage aborted exit(1) (CMPQ against the pointer).
|
||||
* 2. asrt_void_fail — void value `as *int` must abort exit(1).
|
||||
* Pre-fix wwstage silently PASSED (null == ptr-tag 0).
|
||||
* 3. asrt_void_target_fail — valid ptr `as void` must abort exit(1)
|
||||
* (non-null != the void variant). Exercises the void polarity.
|
||||
* 4. asrt_void_target_ok — void value `as void` must succeed.
|
||||
* Exercises the void polarity on the matching side.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "asrt_ptr_ok",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: int = 42;\n"
|
||||
" let p: (*int|void) = &x;\n"
|
||||
" let q: *int = p as *int;\n"
|
||||
" return (*q): i32;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
{ "asrt_void_fail",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let p: (*int|void) = void;\n"
|
||||
" let q = p as *int;\n"
|
||||
" return 50;\n"
|
||||
"};\n",
|
||||
1 },
|
||||
{ "asrt_void_target_fail",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: int = 5;\n"
|
||||
" let p: (*int|void) = &x;\n"
|
||||
" let z = p as void;\n"
|
||||
" return 7;\n"
|
||||
"};\n",
|
||||
1 },
|
||||
{ "asrt_void_target_ok",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let p: (*int|void) = void;\n"
|
||||
" let z = p as void;\n"
|
||||
" return 7;\n"
|
||||
"};\n",
|
||||
7 },
|
||||
};
|
||||
|
||||
static int
|
||||
write_src(const char *dir, const char *base, const struct row *r, char *out,
|
||||
size_t outsz)
|
||||
{
|
||||
snprintf(out, outsz, "%s/%s.ww", dir, base);
|
||||
FILE *f = fopen(out, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
build_run(const char *driver, const char *src, const char *workdir)
|
||||
{
|
||||
char cmd[8192];
|
||||
snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1",
|
||||
workdir, driver, src);
|
||||
if (runwait(cmd) != 0) return -1;
|
||||
|
||||
const char *base = strrchr(src, '/');
|
||||
base = base ? base + 1 : src;
|
||||
char outbin[1024];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", workdir, base);
|
||||
char *dot = strrchr(outbin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
return runwait(outbin);
|
||||
}
|
||||
|
||||
static int
|
||||
files_equal(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int ca, cb, eq = 1;
|
||||
do {
|
||||
ca = fgetc(fa);
|
||||
cb = fgetc(fb);
|
||||
if (ca != cb) { eq = 0; break; }
|
||||
} while (ca != EOF);
|
||||
fclose(fa);
|
||||
fclose(fb);
|
||||
return eq;
|
||||
}
|
||||
|
||||
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], wdrv[640], cw6[640], ww6[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
|
||||
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
|
||||
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
int have_w6cww = (access(ww6, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
|
||||
char dir[] = "/tmp/nasr.XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label);
|
||||
fail++; total++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char src[1024];
|
||||
if (write_src(dir, "p", r, src, sizeof src) != 0) {
|
||||
fprintf(stderr, "row[%s]: write src failed\n", r->label);
|
||||
fail++; total++;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (have_w6cww) {
|
||||
char css[1024], wss[1024], cmd[8192];
|
||||
snprintf(css, sizeof css, "%s/cs.s", dir);
|
||||
snprintf(wss, sizeof wss, "%s/ww.s", dir);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
cw6, css, src);
|
||||
int rc1 = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
ww6, wss, src);
|
||||
int rc2 = runwait(cmd);
|
||||
total++;
|
||||
if (rc1 != 0 || rc2 != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: w6c/w6c_ww emit failed (%d/%d)\n",
|
||||
r->label, rc1, rc2);
|
||||
fail++;
|
||||
} else if (files_equal(css, wss) != 1) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cs.s != ww.s (rule-10 break)\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/cs", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(cdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][cstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_ww) {
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/ww", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(wdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][wwstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
{
|
||||
char rm[1100];
|
||||
snprintf(rm, sizeof rm, "rm -rf %s", dir);
|
||||
runwait(rm);
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"nullable_assert_run: %d/%d checks failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("nullable_assert_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* 949_nullable_try_run — the `?` / `!` try operators on a nullable
|
||||
* `(*T | void)` operand must discriminate POINTER-vs-NULL, not
|
||||
* tag-vs-success-index (report item #15, F4).
|
||||
*
|
||||
* Gate-blind hazard: for a nullable operand AX holds the POINTER and
|
||||
* successtag()=0, so pre-fix wwstage cgtryprop/cgtryunw emitted
|
||||
* `CMPQ $0,AX; JE ok` — treating NULL as success and a valid pointer
|
||||
* as the error, exactly inverted. A valid pointer was early-propagated
|
||||
* / aborted; a null pointer fell through to a deref (SEGV). cstage
|
||||
* already carried the nullable arm (cmd/w6c/cgen.c N_TRYPROP/N_TRYUNW)
|
||||
* and ww's own cgtypetest carries the matching nullable fold; the fix
|
||||
* mirrors that arm into cgtryprop/cgtryunw. wwstage-only (cstage
|
||||
* correct) → after the fix cs.s == ww.s, so the byte-id row witnesses
|
||||
* rule-10 convergence.
|
||||
*
|
||||
* Two assertions per row:
|
||||
* - RUNTIME: build with `ww` (cstage) and `ww_ww` (wwstage), run,
|
||||
* compare exit code. This is what was wrong pre-fix.
|
||||
* - ASM BYTE-ID: compile the same source through `w6c` and `w6c_ww`
|
||||
* and require byte-identical .s (rule 10).
|
||||
*
|
||||
* Rows:
|
||||
* 1. tryprop_valid — `p?` on a VALID pointer must unwrap and continue
|
||||
* (mutate through it). Pre-fix wwstage early-propagated → 50.
|
||||
* 2. tryprop_null — `p?` on NULL must propagate (caller sees void).
|
||||
* Pre-fix wwstage treated null as success → null deref SEGV.
|
||||
* 3. tryunw_valid — `p!` on a VALID pointer must unwrap. Pre-fix
|
||||
* wwstage aborted exit(1).
|
||||
* 4. tryunw_null — `p!` on NULL must abort exit(1). Pre-fix wwstage
|
||||
* treated null as success → null deref SEGV.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "tryprop_valid",
|
||||
"package main;\n"
|
||||
"fn g(p: (*int|void)) (*int|void) = { let q = p?; *q = 7; return q; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: int = 1;\n"
|
||||
" let r = g(&x);\n"
|
||||
" if (x != 7) { return 50; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "tryprop_null",
|
||||
"package main;\n"
|
||||
"fn g(p: (*int|void)) (*int|void) = { let q = p?; *q = 99; return q; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let r = g(void);\n"
|
||||
" match (r) {\n"
|
||||
" case let q: *int => return 60;\n"
|
||||
" case void => return 7;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
7 },
|
||||
{ "tryunw_valid",
|
||||
"package main;\n"
|
||||
"fn h(p: (*int|void)) int = { let q = p!; return *q; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: int = 42;\n"
|
||||
" return h(&x): i32;\n"
|
||||
"};\n",
|
||||
42 },
|
||||
{ "tryunw_null",
|
||||
"package main;\n"
|
||||
"fn h(p: (*int|void)) int = { let q = p!; return *q; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let r = h(void);\n"
|
||||
" return 50;\n"
|
||||
"};\n",
|
||||
1 },
|
||||
};
|
||||
|
||||
static int
|
||||
write_src(const char *dir, const char *base, const struct row *r, char *out,
|
||||
size_t outsz)
|
||||
{
|
||||
snprintf(out, outsz, "%s/%s.ww", dir, base);
|
||||
FILE *f = fopen(out, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
build_run(const char *driver, const char *src, const char *workdir)
|
||||
{
|
||||
char cmd[8192];
|
||||
snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1",
|
||||
workdir, driver, src);
|
||||
if (runwait(cmd) != 0) return -1;
|
||||
|
||||
const char *base = strrchr(src, '/');
|
||||
base = base ? base + 1 : src;
|
||||
char outbin[1024];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", workdir, base);
|
||||
char *dot = strrchr(outbin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
return runwait(outbin);
|
||||
}
|
||||
|
||||
static int
|
||||
files_equal(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int ca, cb, eq = 1;
|
||||
do {
|
||||
ca = fgetc(fa);
|
||||
cb = fgetc(fb);
|
||||
if (ca != cb) { eq = 0; break; }
|
||||
} while (ca != EOF);
|
||||
fclose(fa);
|
||||
fclose(fb);
|
||||
return eq;
|
||||
}
|
||||
|
||||
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], wdrv[640], cw6[640], ww6[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
|
||||
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
|
||||
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
int have_w6cww = (access(ww6, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
|
||||
char dir[] = "/tmp/ntry.XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label);
|
||||
fail++; total++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char src[1024];
|
||||
if (write_src(dir, "p", r, src, sizeof src) != 0) {
|
||||
fprintf(stderr, "row[%s]: write src failed\n", r->label);
|
||||
fail++; total++;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (have_w6cww) {
|
||||
char css[1024], wss[1024], cmd[8192];
|
||||
snprintf(css, sizeof css, "%s/cs.s", dir);
|
||||
snprintf(wss, sizeof wss, "%s/ww.s", dir);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
cw6, css, src);
|
||||
int rc1 = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
ww6, wss, src);
|
||||
int rc2 = runwait(cmd);
|
||||
total++;
|
||||
if (rc1 != 0 || rc2 != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: w6c/w6c_ww emit failed (%d/%d)\n",
|
||||
r->label, rc1, rc2);
|
||||
fail++;
|
||||
} else if (files_equal(css, wss) != 1) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cs.s != ww.s (rule-10 break)\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/cs", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(cdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][cstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_ww) {
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/ww", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(wdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][wwstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
{
|
||||
char rm[1100];
|
||||
snprintf(rm, sizeof rm, "rm -rf %s", dir);
|
||||
runwait(rm);
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"nullable_try_run: %d/%d checks failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("nullable_try_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* 951_arrlit_elem_narrow_run — #251: narrow int/rune array-literal
|
||||
* elements to a declared [N]T element type, at the let / def /
|
||||
* struct-field init sites that #130 (test 920) left unwired.
|
||||
*
|
||||
* Pre-#251 state (verified by building both stages):
|
||||
* - cstage REJECTED in-range bare-int/rune array lits at the LOCAL
|
||||
* let, def, and struct-field sites ("init [4]i32 not assignable to
|
||||
* declared [4]u8"): the array-literal element type came from the
|
||||
* elements via type_default (int-lit→i32, rune-lit→rune) with no
|
||||
* declared-element-type propagation. Only the MODULE-level let site
|
||||
* (#130) accepted.
|
||||
* - wwstage was UNEVEN: local/module let correct, but the DEF path ran
|
||||
* NO init-assignability check and the N_STRUCTLIT head-stamp left
|
||||
* field assignability parked (#23) — so def + struct-field SILENTLY
|
||||
* over-accepted out-of-range and str→u8 elements (a rule-7
|
||||
* miscompile: out-of-range truncates).
|
||||
*
|
||||
* #251 fix = the int/rune analogue of coerce_floatlit, realised as the
|
||||
* EXISTING #130 accept-if-fits range-check (NOT a node-type restamp —
|
||||
* cgen drives the element WIDTH from the DECLARED type, so a restamp
|
||||
* would be dead code). Both stages now, at let/def/struct-field:
|
||||
* - foldable int/rune literal → defcastfits range-check vs declared T
|
||||
* (in-range accept; out-of-range REJECT loud, rule-7/Drew).
|
||||
* - non-foldable → type_assignable / isassignable to the element type.
|
||||
* - cstage: arrlit_init_fits wired at check.c clet (local let),
|
||||
* struct-field-init, and def-init.
|
||||
* - wwstage: checkarrlitfits (factored from checkletassign's inline
|
||||
* #130 block) called from the let path, the def path, and a TARGETED
|
||||
* array-field walk in the N_STRUCTLIT arm (the array-field
|
||||
* accept-if-fits ONLY — isolated from the parked #23 field walk).
|
||||
*
|
||||
* Element width is declared-type-driven at cgen, so the array literal
|
||||
* node keeps its [N]i32 / [N]rune type and no restamp is needed; the
|
||||
* cs==ww byte-id gate confirms the emitted bytes are u8-wide regardless.
|
||||
*
|
||||
* Accept rows (run + cs==ww byte-id), int and rune, across the 3 sites:
|
||||
* - let_int `let a:[4]u8=[65,66,67,68]; a[0]` → 65
|
||||
* - let_rune `let a:[4]u8=['A','B','C','D']; a[0]` → 65
|
||||
* - def_int `def D:[4]u8=[65,..]; D[0]` → 65
|
||||
* - def_rune `def D:[4]u8=['A',..]; D[0]` → 65
|
||||
* - struct_int `enc{m=[65,..]}; E.m[0]` → 65
|
||||
* - struct_rune `enc{m=['A',..]}; E.m[1]` → 66
|
||||
*
|
||||
* Reject rows (must FAIL build on BOTH stages — rule-7 loud reject).
|
||||
* Two reject branches per the predicate: the FOLDABLE range-check
|
||||
* (out-of-range int) and the NON-FOLDABLE element-type check (str→u8),
|
||||
* each exercised at all 3 sites. (A rune>u8 over-range row is not
|
||||
* expressible: the ww lexer caps rune escapes at \xFF=255 and does not
|
||||
* decode multi-byte UTF-8 in a rune literal, so every rune literal
|
||||
* already fits u8 — the foldable branch is identical for int and rune.)
|
||||
* - let_over `let a:[2]u8=[300,1]` (foldable: out of range)
|
||||
* - def_over `def D:[2]u8=[300,1]` (foldable: was a ww over-accept)
|
||||
* - struct_over `enc{m=[300,1]}` (foldable: was a ww over-accept)
|
||||
* - let_str `let a:[2]u8=["x","y"]` (non-foldable: str→u8)
|
||||
* - def_str `def D:[2]u8=["x","y"]` (non-foldable: was a ww over-accept)
|
||||
* - struct_str `enc{m=["x","y"]}` (non-foldable: was a ww over-accept)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct arow { const char *label; const char *src; int want_exit; };
|
||||
struct rrow { const char *label; const char *src; };
|
||||
|
||||
static const struct arow accept_rows[] = {
|
||||
{ "let_int",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u8 = [65, 66, 67, 68];\n"
|
||||
" return a[0]: i32;\n"
|
||||
"};\n", 65 },
|
||||
{ "let_rune",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u8 = ['A', 'B', 'C', 'D'];\n"
|
||||
" return a[0]: i32;\n"
|
||||
"};\n", 65 },
|
||||
{ "def_int",
|
||||
"package main;\n"
|
||||
"def D: [4]u8 = [65, 66, 67, 68];\n"
|
||||
"export fn main() i32 = { return D[0]: i32; };\n", 65 },
|
||||
{ "def_rune",
|
||||
"package main;\n"
|
||||
"def D: [4]u8 = ['A', 'B', 'C', 'D'];\n"
|
||||
"export fn main() i32 = { return D[0]: i32; };\n", 65 },
|
||||
{ "struct_int",
|
||||
"package main;\n"
|
||||
"type enc = struct { m: [4]u8 };\n"
|
||||
"let E: enc = enc { m = [65, 66, 67, 68] };\n"
|
||||
"export fn main() i32 = { return E.m[0]: i32; };\n", 65 },
|
||||
{ "struct_rune",
|
||||
"package main;\n"
|
||||
"type enc = struct { m: [4]u8 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: enc = enc { m = ['A', 'B', 'C', 'D'] };\n"
|
||||
" return e.m[1]: i32;\n"
|
||||
"};\n", 66 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static const struct rrow reject_rows[] = {
|
||||
{ "let_over",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = { let a: [2]u8 = [300, 1]; return a[0]: i32; };\n" },
|
||||
{ "def_over",
|
||||
"package main;\n"
|
||||
"def D: [2]u8 = [300, 1];\n"
|
||||
"export fn main() i32 = { return D[0]: i32; };\n" },
|
||||
{ "struct_over",
|
||||
"package main;\n"
|
||||
"type enc = struct { m: [2]u8 };\n"
|
||||
"let E: enc = enc { m = [300, 1] };\n"
|
||||
"export fn main() i32 = { return E.m[0]: i32; };\n" },
|
||||
{ "let_str",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = { let a: [2]u8 = [\"x\", \"y\"]; return a[0]: i32; };\n" },
|
||||
{ "def_str",
|
||||
"package main;\n"
|
||||
"def D: [2]u8 = [\"x\", \"y\"];\n"
|
||||
"export fn main() i32 = { return 0; };\n" },
|
||||
{ "struct_str",
|
||||
"package main;\n"
|
||||
"type enc = struct { m: [2]u8 };\n"
|
||||
"let E: enc = enc { m = [\"x\", \"y\"] };\n"
|
||||
"export fn main() i32 = { return E.m[0]: i32; };\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "arrnarrow: w6c_ww missing — cannot run the "
|
||||
"cs==ww gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
|
||||
/* Accept rows: cstage build + run + cs==ww byte-id. */
|
||||
for (int i = 0; accept_rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwan_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char rmcmd[160];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128], outbin[128];
|
||||
snprintf(src, sizeof src, "%s/wwan_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwan_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(accept_rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "accept[%s]: cstage build failed\n",
|
||||
accept_rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
int got = runwait(outbin);
|
||||
if (got != accept_rows[i].want_exit) {
|
||||
fprintf(stderr, "accept[%s]: exit %d, want %d\n",
|
||||
accept_rows[i].label, got, accept_rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwan_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwan_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "accept[%s]: w6c failed\n", accept_rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "accept[%s]: w6c_ww failed\n", accept_rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "accept[%s]: cs/ww .s DIFFER (rule-10)\n",
|
||||
accept_rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
/* Reject rows: BOTH stages must fail to build (loud reject). */
|
||||
for (int i = 0; reject_rows[i].src; i++, n++) {
|
||||
char src[64];
|
||||
snprintf(src, sizeof src, "/tmp/wwrn_%d_%d.ww", getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; continue; }
|
||||
fputs(reject_rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048], dst[80];
|
||||
snprintf(dst, sizeof dst, "/tmp/wwrn_%d_%d.s", getpid(), i);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, dst, src);
|
||||
int cs_rc = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, dst, src);
|
||||
int ww_rc = runwait(cmd);
|
||||
|
||||
if (cs_rc == 0) {
|
||||
fprintf(stderr, "reject[%s]: cstage ACCEPTED (want reject)\n",
|
||||
reject_rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
if (ww_rc == 0) {
|
||||
fprintf(stderr, "reject[%s]: wwstage ACCEPTED (want reject)\n",
|
||||
reject_rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
unlink(src); unlink(dst);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d arrlit-elem-narrow tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("arrnarrow: %d/%d ok (accept: run + cs==ww; reject: both-stage fail)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* 953_arraytoslice_run — runtime + byte-id + reject net for #258: the
|
||||
* implicit [N]T -> []T array-to-slice BORROW. Hare admits an array with a
|
||||
* defined length wherever its element slice is expected (assign / return /
|
||||
* call-arg / init), as a borrow — `.ptr = &arr[0], .len = N, .cap = N`
|
||||
* (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). ww previously
|
||||
* REJECTED it everywhere (cmd/wcc/type.c:#108(c) exclusion); base64
|
||||
* worked around the gap with explicit `a[0:n]` slices.
|
||||
*
|
||||
* Fix (DESUGAR, checker-only, ZERO new cgen): type_assignable /
|
||||
* isassignable admit array→slice on an exact element match; the four
|
||||
* acceptance sites (clet / N_ASSIGN / call-arg gather / N_RETURN) then
|
||||
* rewrite the array expr to the explicit full slice `arr[0:len(arr)]` (an
|
||||
* N_SLICE over the array base) via the shared desugar_arrayslice /
|
||||
* desugararrayslice helper. cgen is untouched — the existing slice
|
||||
* lowering (#252/#257/#135 made array bases, incl struct-field arrays,
|
||||
* correct) materialises the borrow header, identically in both stages.
|
||||
*
|
||||
* Positive rows: cstage `ww build` + run for exit code (the four contexts
|
||||
* + a BORROW proof — mutate through the slice, observe the change in the
|
||||
* backing array, i.e. alias not copy), then w6c vs w6c_ww `.s` cmp for the
|
||||
* rule-10 byte-id gate (all four contexts emit byte-identical asm).
|
||||
* - let_i32 let-init borrow `let s: []i32 = a`, s[2] → 33
|
||||
* - let_u8 let-init borrow, u8 element, s[3] → 66
|
||||
* - assign_i32 assignment borrow `s = a` (s was a[0:1]), s[3] → 4
|
||||
* - callarg_i32 call-arg borrow `sum(a)` (sums all elements) → 65
|
||||
* - callarg_u8 call-arg borrow, u8 element → 70
|
||||
* - return_i32 return borrow `return g` (g a global array) → 17
|
||||
* - borrow_i32 mutate s[2]=55 through the slice, read a[2] → 55
|
||||
*
|
||||
* Reject rows (cs==ww symmetric loud-reject): an element-type MISMATCH
|
||||
* ([4]i32 -> []u8) is NOT a borrow — both stages must refuse it. w6c and
|
||||
* w6c_ww are each run directly and must exit non-zero (the desugar's
|
||||
* element-exact gate; type_assignable / isassignable fall through to a
|
||||
* confident reject).
|
||||
* - mismatch_let let s: []u8 = a where a:[4]i32 → both reject
|
||||
* - mismatch_callarg f(a) where f wants []u8, a:[4]i32 → both reject
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb");
|
||||
if (fa == NULL || fb == NULL) {
|
||||
if (fa) fclose(fa);
|
||||
if (fb) fclose(fb);
|
||||
return -1;
|
||||
}
|
||||
int ca, cb, eq = 0;
|
||||
do {
|
||||
ca = fgetc(fa);
|
||||
cb = fgetc(fb);
|
||||
if (ca != cb) { eq = -1; break; }
|
||||
} while (ca != EOF);
|
||||
fclose(fa);
|
||||
fclose(fb);
|
||||
return eq;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want_exit; int reject; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "let_i32",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i32 = [11, 22, 33, 44];\n"
|
||||
" let s: []i32 = a;\n"
|
||||
" return s[2];\n"
|
||||
"};\n", 33, 0 },
|
||||
{ "let_u8",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u8 = [11u8, 22u8, 33u8, 66u8];\n"
|
||||
" let s: []u8 = a;\n"
|
||||
" return s[3]: i32;\n"
|
||||
"};\n", 66, 0 },
|
||||
{ "assign_i32",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i32 = [1, 2, 3, 4];\n"
|
||||
" let s: []i32 = a[0:1];\n"
|
||||
" s = a;\n"
|
||||
" return s[3];\n"
|
||||
"};\n", 4, 0 },
|
||||
{ "callarg_i32",
|
||||
"package main;\n"
|
||||
"fn sum(s: []i32) i32 = {\n"
|
||||
" let t: i32 = 0; let i: i32 = 0;\n"
|
||||
" for (i < s.len) { t += s[i]; i += 1; };\n"
|
||||
" return t;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i32 = [10, 20, 30, 5];\n"
|
||||
" return sum(a);\n"
|
||||
"};\n", 65, 0 },
|
||||
{ "callarg_u8",
|
||||
"package main;\n"
|
||||
"fn sum(s: []u8) i32 = {\n"
|
||||
" let t: i32 = 0; let i: i32 = 0;\n"
|
||||
" for (i < s.len) { t += s[i]: i32; i += 1; };\n"
|
||||
" return t;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]u8 = [10u8, 20u8, 40u8];\n"
|
||||
" return sum(a);\n"
|
||||
"};\n", 70, 0 },
|
||||
{ "return_i32",
|
||||
"package main;\n"
|
||||
"let g: [3]i32 = [7, 8, 9];\n"
|
||||
"fn mk() []i32 = { return g; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: []i32 = mk();\n"
|
||||
" return s[1] + s[2];\n"
|
||||
"};\n", 17, 0 },
|
||||
{ "borrow_i32",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i32 = [1, 2, 3, 4];\n"
|
||||
" let s: []i32 = a;\n"
|
||||
" s[2] = 55;\n"
|
||||
" return a[2];\n"
|
||||
"};\n", 55, 0 },
|
||||
/* Reject rows: element-type mismatch — both stages refuse. */
|
||||
{ "mismatch_let",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i32 = [1, 2, 3, 4];\n"
|
||||
" let s: []u8 = a;\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 1 },
|
||||
{ "mismatch_callarg",
|
||||
"package main;\n"
|
||||
"fn f(s: []u8) i32 = { return 0; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i32 = [1, 2, 3, 4];\n"
|
||||
" return f(a);\n"
|
||||
"};\n", 0, 1 },
|
||||
{ NULL, NULL, 0, 0 },
|
||||
};
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "arraytoslice: w6c_ww missing — cannot run the "
|
||||
"cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwa2s_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
char rmcmd[128];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128];
|
||||
snprintf(src, sizeof src, "%s/wwa2s_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwa2s_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwa2s_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
char cmd[2048];
|
||||
|
||||
if (rows[i].reject) {
|
||||
/* Element mismatch — both stages must refuse (non-zero). */
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c ACCEPTED a "
|
||||
"type-mismatch array→slice (must reject)\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww ACCEPTED a "
|
||||
"type-mismatch array→slice (must reject)\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Positive row: cstage build + run for exit code. */
|
||||
char outbin[128];
|
||||
snprintf(outbin, sizeof outbin, "%s/wwa2s_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* rule-10 byte-id: w6c vs w6c_ww .s. */
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d array-to-slice tests failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("arraytoslice: %d/%d ok (cstage run + cs==ww byte-id + reject)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,43 +1,27 @@
|
||||
/*
|
||||
* 953_arrlit_slice_run — runtime + byte-id + reject net for #25/#31: a
|
||||
* one-step array-LITERAL initialiser for a SLICE local (`let xs:[]T=[..]`).
|
||||
* 953_arrlit_slice_run -- SLIMMED to the one irreducible asymmetric row.
|
||||
*
|
||||
* #31 (silent miscompile, cs!=ww): the #258 array→slice borrow wrapped the
|
||||
* un-addressable N_ARRLIT directly as the N_SLICE base; cgen never spilled
|
||||
* the literal to a stack slot, so .ptr pointed at garbage (`let xs:[]i32=
|
||||
* [10,20,30]; xs[1]` returned 1; []u8/[]str segfaulted). #25 (over-strict
|
||||
* reject): a slice target fell through to the exact-element type_eq borrow
|
||||
* gate, so bare-int-width ([]u8=[1,2,3]) and str elements rejected.
|
||||
* The #25/#31 array-literal-slice corpus migrated to fold-2 homes (#5-C6): the
|
||||
* value rows -> test/lang/arrlit_slice_test.ww (@test + T2 byte-id); the
|
||||
* reject_oob / reject_call / reject_ret rows -> test/wcc/data/arrlit_slice_*
|
||||
* runww //ww:error carriers (strong shared substrings, both stages).
|
||||
*
|
||||
* Fix (re-stamp + per-borrow scratch, both stages symmetric, NO new codegen
|
||||
* shape): the checker re-stamps the slice arrlit as [count]T (per-element
|
||||
* coercion + range-check, #25); cgen materialises the borrow base into a
|
||||
* FRESH per-borrow @slicescr stack slot (distinct slot per borrow — a
|
||||
* borrow's backing outlives the lowering, so it can't share a cached slot;
|
||||
* two live borrows would otherwise alias one backing), filled via the shared
|
||||
* array-init element store (#31). Supported ONLY at a `let` init — in
|
||||
* call-arg / return / assign position there is no addressable backing, so
|
||||
* both stages LOUD-REJECT ("bind it to a `let` first"), aligning cstage DOWN
|
||||
* to wwstage per rule-10; full non-let support is deferred (#33).
|
||||
* reject_assign CANNOT move to a runww //ww:error carrier: both stages REJECT an
|
||||
* array-LITERAL assigned to a slice, but at DIFFERENT diagnostic LAYERS -- cstage
|
||||
* at assignability ("cannot assign [3]int to []i32") and wwstage at the borrow
|
||||
* gate ("array literal cannot borrow as a slice here; bind it to a `let` first").
|
||||
* BOTH reject CORRECTLY (the borrow is supported only at a `let` init, #31/#33) --
|
||||
* this is a benign diag-LAYER divergence, NOT a miscompile, so NO bug ticket. No
|
||||
* honest shared substring exists for a #20-non-vacuity carrier, and a build-reject
|
||||
* emits no .s (no byte-id surface). It stays a slim C pin asserting rc!=0 BOTH
|
||||
* stages -- the position the stages diverge, most worth pinning.
|
||||
*
|
||||
* Accept rows (cstage `ww build` + run for exit code, then w6c vs w6c_ww .s
|
||||
* cmp for the rule-10 byte-id gate):
|
||||
* - i32_sum let xs:[]i32=[10,20,30]; xs[0]+xs[1]+xs[2] → 60
|
||||
* - i32_idx xs[1] (the #31 pin: was 1, want 20) → 20
|
||||
* - u8_coerce let zs:[]u8=[1,2,3]; bare-int→u8 width (#25) → 6
|
||||
* - u8_typed let xs:[]u8=[10u8,20u8,30u8]; xs[2] → 30
|
||||
* - i64_stride let xs:[]i64=[7i64,42i64]; xs[1] (8B stride) → 42
|
||||
* - str_read let ys:[]str=["ab","c"]; ys[0].len*10+ys[1].len → 21
|
||||
* - len_read let xs:[]i32=[10,20,30]; xs.len → 3
|
||||
* - multi_live let xs=[1,2,3]; let ys=[4,5]; xs[0]+ys[0] → 5
|
||||
* (SOUNDNESS PIN: fresh-per-borrow; a shared slot → 4+4=8)
|
||||
* - borrow_mut xs[1]=99 through the borrow, read back → 99
|
||||
*
|
||||
* Reject rows (cs==ww symmetric loud-reject, both w6c and w6c_ww non-zero):
|
||||
* - reject_oob let q:[]u8=[256,1] → out-of-range element (#25)
|
||||
* - reject_call sum([1,2,3]) → non-let borrow (#31/#33)
|
||||
* - reject_ret return [1,2,3] → non-let borrow
|
||||
* - reject_assign s = [1,2,3] → non-let borrow
|
||||
* Non-vacuity self-check (the pin CAN go RED): `valid_src` assigns a NAMED array
|
||||
* (an addressable backing), which the #258 borrow ACCEPTS on both stages (rc==0);
|
||||
* it proves the pin discriminates the array-LITERAL reject from a legitimate
|
||||
* array-to-slice assign, not "all builds error". Mutation-checked: binding the
|
||||
* literal to a `let` first (`let t=[1,2,3]; s=t;`) flips both stages to accept ->
|
||||
* the reject leg goes RED (verified).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -54,120 +38,57 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* reject_assign: an array LITERAL in assign position has no addressable backing
|
||||
* -- both stages reject (cstage assignability, wwstage borrow gate). */
|
||||
static const char *reject_src =
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: []i32 = [0, 0];\n"
|
||||
" s = [1, 2, 3];\n"
|
||||
" return s[0];\n"
|
||||
"};\n";
|
||||
|
||||
/* Vacuity control: a NAMED array assigned as a slice borrow (#258) -- both
|
||||
* stages accept. Proves the pin distinguishes accept from reject. */
|
||||
static const char *valid_src =
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]i32 = [1, 2, 3];\n"
|
||||
" let s: []i32 = a[0:1];\n"
|
||||
" s = a;\n"
|
||||
" return s[0];\n"
|
||||
"};\n";
|
||||
|
||||
/* build_only -- write `src`, build via `driver` (no run). Returns the build
|
||||
* exit code (0 = accepted, nonzero = rejected). */
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
build_only(const char *driver, const char *src, int seq)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb");
|
||||
if (fa == NULL || fb == NULL) {
|
||||
if (fa) fclose(fa);
|
||||
if (fb) fclose(fb);
|
||||
return -1;
|
||||
}
|
||||
int ca, cb, eq = 0;
|
||||
do {
|
||||
ca = fgetc(fa);
|
||||
cb = fgetc(fb);
|
||||
if (ca != cb) { eq = -1; break; }
|
||||
} while (ca != EOF);
|
||||
fclose(fa);
|
||||
fclose(fb);
|
||||
return eq;
|
||||
char tmpdir[96], srcp[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/as_%d_%d", getpid(), seq);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir);
|
||||
snprintf(outbin, sizeof outbin, "%s/c", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(srcp, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "timeout 20 %s build -o %s %s >/dev/null 2>&1",
|
||||
driver, outbin, srcp);
|
||||
int brc = runwait(cmd);
|
||||
runwait(rmcmd);
|
||||
return brc;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want_exit; int reject; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "i32_sum",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i32 = [10, 20, 30];\n"
|
||||
" return xs[0] + xs[1] + xs[2];\n"
|
||||
"};\n", 60, 0 },
|
||||
{ "i32_idx",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i32 = [10, 20, 30];\n"
|
||||
" return xs[1];\n"
|
||||
"};\n", 20, 0 },
|
||||
{ "u8_coerce",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let zs: []u8 = [1, 2, 3];\n"
|
||||
" return zs[0]: i32 + zs[1]: i32 + zs[2]: i32;\n"
|
||||
"};\n", 6, 0 },
|
||||
{ "u8_typed",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []u8 = [10u8, 20u8, 30u8];\n"
|
||||
" return xs[2]: i32;\n"
|
||||
"};\n", 30, 0 },
|
||||
{ "i64_stride",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i64 = [7i64, 42i64];\n"
|
||||
" return xs[1]: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
{ "str_read",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let ys: []str = [\"ab\", \"c\"];\n"
|
||||
" return ys[0].len: i32 * 10 + ys[1].len: i32;\n"
|
||||
"};\n", 21, 0 },
|
||||
{ "len_read",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i32 = [10, 20, 30];\n"
|
||||
" return xs.len: i32;\n"
|
||||
"};\n", 3, 0 },
|
||||
{ "multi_live",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i32 = [1, 2, 3];\n"
|
||||
" let ys: []i32 = [4, 5];\n"
|
||||
" return xs[0] + ys[0];\n"
|
||||
"};\n", 5, 0 },
|
||||
{ "borrow_mut",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []i32 = [1, 2, 3];\n"
|
||||
" xs[1] = 99;\n"
|
||||
" return xs[1];\n"
|
||||
"};\n", 99, 0 },
|
||||
/* Reject rows: out-of-range element + the three non-let contexts. */
|
||||
{ "reject_oob",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let q: []u8 = [256, 1];\n"
|
||||
" return q[0]: i32;\n"
|
||||
"};\n", 0, 1 },
|
||||
{ "reject_call",
|
||||
"package main;\n"
|
||||
"fn sum(s: []i32) i32 = { return s[0]; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return sum([1, 2, 3]);\n"
|
||||
"};\n", 0, 1 },
|
||||
{ "reject_ret",
|
||||
"package main;\n"
|
||||
"fn mk() []i32 = { return [1, 2, 3]; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return mk()[0];\n"
|
||||
"};\n", 0, 1 },
|
||||
{ "reject_assign",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: []i32 = [0, 0];\n"
|
||||
" s = [1, 2, 3];\n"
|
||||
" return s[0];\n"
|
||||
"};\n", 0, 1 },
|
||||
{ NULL, NULL, 0, 0 },
|
||||
};
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
@@ -175,108 +96,37 @@ main(void)
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "arrlit_slice: w6c_ww missing — cannot run the "
|
||||
"cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int wwpresent = (access(wdrv, X_OK) == 0);
|
||||
int fail = 0, seq = 0;
|
||||
|
||||
/* reject leg: both stages must REJECT (rc!=0). */
|
||||
if (build_only(cdrv, reject_src, seq++) == 0) {
|
||||
fprintf(stderr, "arrlit_slice[cs]: reject_assign ACCEPTED (want reject)\n");
|
||||
fail++;
|
||||
}
|
||||
if (wwpresent && build_only(wdrv, reject_src, seq++) == 0) {
|
||||
fprintf(stderr, "arrlit_slice[ww]: reject_assign ACCEPTED (want reject)\n");
|
||||
fail++;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
/* src + outbin + cs.s + ws.s all under one tmpdir so the
|
||||
* compiler's source-derived .sepwork scratch stays inside it;
|
||||
* single rm -rf deferred to the end of the row. */
|
||||
char tmpdir[64], rmcmd[160];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwas_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128], outbin[128];
|
||||
snprintf(src, sizeof src, "%s/wwas_%d_%d.ww", tmpdir,
|
||||
getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwas_%d_%d", tmpdir,
|
||||
getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { runwait(rmcmd); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwas_%d_%d_cs.s", tmpdir,
|
||||
getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwas_%d_%d_ww.s", tmpdir,
|
||||
getpid(), i);
|
||||
char cmd[2048];
|
||||
|
||||
if (rows[i].reject) {
|
||||
/* Both stages must refuse (non-zero). */
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c ACCEPTED a row that "
|
||||
"must reject\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww ACCEPTED a row "
|
||||
"that must reject\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Positive row: cstage build + run for exit code. */
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* rule-10 byte-id: w6c vs w6c_ww .s. */
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
/* vacuity control: both stages must ACCEPT (rc==0). */
|
||||
if (build_only(cdrv, valid_src, seq++) != 0) {
|
||||
fprintf(stderr, "arrlit_slice[cs]: vacuity control REJECTED (pin may be vacuous)\n");
|
||||
fail++;
|
||||
}
|
||||
if (wwpresent && build_only(wdrv, valid_src, seq++) != 0) {
|
||||
fprintf(stderr, "arrlit_slice[ww]: vacuity control REJECTED (pin may be vacuous)\n");
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d arrlit-slice tests failed\n", fail, n);
|
||||
fprintf(stderr, "arrlit_slice: %d checks failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("arrlit_slice: %d/%d ok (cstage run + cs==ww byte-id + "
|
||||
"reject)\n", n, n);
|
||||
printf("arrlit_slice: reject_assign pin ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* 953_globalslice_arg_run — cstage runtime pin for #148 (D2).
|
||||
*
|
||||
* A module-global slice (`const`/`let []T`) passed BY VALUE as a slice
|
||||
* arg arrived with a GARBAGE header: the slice-IDENT call-arg fast path
|
||||
* (cgen.c:9082) emitted unconditional `MOVQ off+{0,8,16}(BP)` where
|
||||
* off=localfind(name). For a GLOBAL slice ident localfind→0 (locals are
|
||||
* negative), so it read (BP)/8(BP)/16(BP) = saved-BP/RIP/caller garbage
|
||||
* instead of the global's header at name(SB). The fix mirrors the
|
||||
* sibling N_SLICE arm's isglobal dispatch: LEAQ name(SB) into a base
|
||||
* reg, then push 16/8/0 off that base.
|
||||
*
|
||||
* Generalizes to any []T (fast path keys on node_isslice, not u8) — the
|
||||
* []u32 row catches an elem-width assumption. The direct-read control
|
||||
* row (global slice consumed in-place, never passed as an arg) already
|
||||
* worked pre-fix; it locks that the new arm doesn't regress it.
|
||||
*
|
||||
* CSTAGE-ONLY: wwstage's checker rejects a module-level `const`/`let
|
||||
* []T` global ("let: not assignable", #120/#29-kin) → the cgen path is
|
||||
* UNREACHABLE on wwstage, so there is no .s to diverge and 990-997
|
||||
* byte-id stay green. NO byte-id leg here; add it when #120 + the
|
||||
* cgenexpr.ww twin (#125) land.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* the path bytes.equal(dot,…) consumer shape: a global []u8 passed
|
||||
* by value, the callee reading .len AND a byte. seen = len*1000 +
|
||||
* byte[0]: dotdot = ['.','.'] → 2*1000 + 46 = 2046, truncated to a
|
||||
* u8 exit code = 2046 & 0xff = 254. */
|
||||
{ "u8_dotdot",
|
||||
"package main;\n"
|
||||
"const dotdot: []u8 = ['.', '.'];\n"
|
||||
"fn seen(bs: []u8) i32 = {\n"
|
||||
" let n = bs.len: i32; let f = bs[0]: i32;\n"
|
||||
" return n*1000 + f;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return seen(dotdot) & 255; };\n",
|
||||
254 },
|
||||
/* single-element global []u8 — len 1, byte '.' = 46 → 1*100+46. */
|
||||
{ "u8_dot",
|
||||
"package main;\n"
|
||||
"const dot: []u8 = ['.'];\n"
|
||||
"fn seen(bs: []u8) i32 = {\n"
|
||||
" let n = bs.len: i32; let f = bs[0]: i32;\n"
|
||||
" return n*100 + f;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return seen(dot); };\n",
|
||||
146 },
|
||||
/* []u32 global — proves the fast path is element-width-agnostic
|
||||
* (header is 3 words regardless of esz). len 3, g[0]=7, g[2]=9
|
||||
* → 3*100 + 7 + 9 = 316 & 0xff = 60. */
|
||||
{ "u32_global",
|
||||
"package main;\n"
|
||||
"const g: []u32 = [7u32, 8u32, 9u32];\n"
|
||||
"fn seen(xs: []u32) i32 = {\n"
|
||||
" let n = xs.len: i32;\n"
|
||||
" return n*100 + xs[0]: i32 + xs[2]: i32;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return seen(g) & 255; };\n",
|
||||
60 },
|
||||
/* DIRECT-READ control — global slice consumed in place, NOT passed
|
||||
* as an arg. Already correct pre-fix; locks no regression. len 2,
|
||||
* d[0]=11, d[1]=22 → 2*100 + 11 + 22 = 233. */
|
||||
{ "direct_read",
|
||||
"package main;\n"
|
||||
"const d: []u32 = [11u32, 22u32];\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return d.len: i32 * 100 + d[0]: i32 + d[1]: i32;\n"
|
||||
"};\n",
|
||||
233 },
|
||||
/* LOCAL slice by-value arg control — exercises the off!=0 arm of the
|
||||
* SAME fast path (cgen.c:9082), locking that the new global branch
|
||||
* doesn't regress the unchanged BP-relative local push. len 2,
|
||||
* byte '.' = 46 → 2*1000 + 46 = 2046 & 0xff = 254. */
|
||||
{ "u8_local_arg",
|
||||
"package main;\n"
|
||||
"fn seen(bs: []u8) i32 = {\n"
|
||||
" let n = bs.len: i32; let f = bs[0]: i32;\n"
|
||||
" return n*1000 + f;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [2]u8 = ['.', '.'];\n"
|
||||
" let s: []u8 = a[0:2];\n"
|
||||
" return seen(s) & 255;\n"
|
||||
"};\n",
|
||||
254 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
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 tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwgsa_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char src[128], outbin[128], rmcmd[160];
|
||||
snprintf(src, sizeof src, "%s/wwgsa_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwgsa_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { runwait(rmcmd); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d globalslice-arg tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("globalslice_arg: %d/%d ok (cstage run)\n", n, n);
|
||||
return 0;
|
||||
}
|
||||
9
test/wcc/data/a2s_mismatch_callarg/case.ww
Normal file
9
test/wcc/data/a2s_mismatch_callarg/case.ww
Normal file
@@ -0,0 +1,9 @@
|
||||
//ww:error "not assignable"
|
||||
// #258 carrier: element-type mismatch at a call-arg borrow; both stages refuse.
|
||||
// From test/wcc/953_arraytoslice_run.c mismatch_callarg.
|
||||
package main;
|
||||
fn f(s: []u8) i32 = { return 0; };
|
||||
export fn main() i32 = {
|
||||
let a: [4]i32 = [1, 2, 3, 4];
|
||||
return f(a);
|
||||
};
|
||||
9
test/wcc/data/a2s_mismatch_let/case.ww
Normal file
9
test/wcc/data/a2s_mismatch_let/case.ww
Normal file
@@ -0,0 +1,9 @@
|
||||
//ww:error "not assignable"
|
||||
// #258 carrier: an element-type mismatch ([4]i32 -> []u8) is NOT a borrow;
|
||||
// both stages must refuse it. From test/wcc/953_arraytoslice_run.c mismatch_let.
|
||||
package main;
|
||||
export fn main() i32 = {
|
||||
let a: [4]i32 = [1, 2, 3, 4];
|
||||
let s: []u8 = a;
|
||||
return 0;
|
||||
};
|
||||
9
test/wcc/data/arrlit_slice_reject_call/case.ww
Normal file
9
test/wcc/data/arrlit_slice_reject_call/case.ww
Normal file
@@ -0,0 +1,9 @@
|
||||
//ww:error "not assignable"
|
||||
// #31/#33 carrier: an array-lit slice borrow in CALL-ARG position must REJECT
|
||||
// (supported only at a `let` init). From test/wcc/953_arrlit_slice_run.c
|
||||
// reject_call.
|
||||
package main;
|
||||
fn sum(s: []i32) i32 = { return s[0]; };
|
||||
export fn main() i32 = {
|
||||
return sum([1, 2, 3]);
|
||||
};
|
||||
8
test/wcc/data/arrlit_slice_reject_oob/case.ww
Normal file
8
test/wcc/data/arrlit_slice_reject_oob/case.ww
Normal file
@@ -0,0 +1,8 @@
|
||||
//ww:error "array element out of range"
|
||||
// #25 carrier: an out-of-range element in a slice array-lit init must REJECT on
|
||||
// both stages. From test/wcc/953_arrlit_slice_run.c reject_oob.
|
||||
package main;
|
||||
export fn main() i32 = {
|
||||
let q: []u8 = [256, 1];
|
||||
return q[0]: i32;
|
||||
};
|
||||
8
test/wcc/data/arrlit_slice_reject_ret/case.ww
Normal file
8
test/wcc/data/arrlit_slice_reject_ret/case.ww
Normal file
@@ -0,0 +1,8 @@
|
||||
//ww:error "not assignable"
|
||||
// #31/#33 carrier: an array-lit slice borrow in RETURN position must REJECT.
|
||||
// From test/wcc/953_arrlit_slice_run.c reject_ret.
|
||||
package main;
|
||||
fn mk() []i32 = { return [1, 2, 3]; };
|
||||
export fn main() i32 = {
|
||||
return mk()[0];
|
||||
};
|
||||
6
test/wcc/data/arrnarrow_def_over/case.ww
Normal file
6
test/wcc/data/arrnarrow_def_over/case.ww
Normal file
@@ -0,0 +1,6 @@
|
||||
//ww:error "array element out of range"
|
||||
// #251 carrier: out-of-range element at a def (was a ww over-accept). From
|
||||
// test/wcc/951_arrlit_elem_narrow_run.c def_over.
|
||||
package main;
|
||||
def D: [2]u8 = [300, 1];
|
||||
export fn main() i32 = { return D[0]: i32; };
|
||||
6
test/wcc/data/arrnarrow_def_str/case.ww
Normal file
6
test/wcc/data/arrnarrow_def_str/case.ww
Normal file
@@ -0,0 +1,6 @@
|
||||
//ww:error "not assignable"
|
||||
// #251 carrier: str element at a def (was a ww over-accept). From
|
||||
// test/wcc/951_arrlit_elem_narrow_run.c def_str.
|
||||
package main;
|
||||
def D: [2]u8 = ["x", "y"];
|
||||
export fn main() i32 = { return 0; };
|
||||
5
test/wcc/data/arrnarrow_let_over/case.ww
Normal file
5
test/wcc/data/arrnarrow_let_over/case.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
//ww:error "array element out of range"
|
||||
// #251 carrier: out-of-range int array-lit element at a local let must REJECT
|
||||
// on both stages. Migrated from test/wcc/951_arrlit_elem_narrow_run.c let_over.
|
||||
package main;
|
||||
export fn main() i32 = { let a: [2]u8 = [300, 1]; return a[0]: i32; };
|
||||
5
test/wcc/data/arrnarrow_let_str/case.ww
Normal file
5
test/wcc/data/arrnarrow_let_str/case.ww
Normal file
@@ -0,0 +1,5 @@
|
||||
//ww:error "not assignable"
|
||||
// #251 carrier: str array-lit element to a [N]u8 let must REJECT (non-foldable
|
||||
// element-type mismatch). From test/wcc/951_arrlit_elem_narrow_run.c let_str.
|
||||
package main;
|
||||
export fn main() i32 = { let a: [2]u8 = ["x", "y"]; return a[0]: i32; };
|
||||
7
test/wcc/data/arrnarrow_struct_over/case.ww
Normal file
7
test/wcc/data/arrnarrow_struct_over/case.ww
Normal file
@@ -0,0 +1,7 @@
|
||||
//ww:error "array element out of range"
|
||||
// #251 carrier: out-of-range element at a struct-field init (was a ww
|
||||
// over-accept). From test/wcc/951_arrlit_elem_narrow_run.c struct_over.
|
||||
package main;
|
||||
type enc = struct { m: [2]u8 };
|
||||
let E: enc = enc { m = [300, 1] };
|
||||
export fn main() i32 = { return E.m[0]: i32; };
|
||||
7
test/wcc/data/arrnarrow_struct_str/case.ww
Normal file
7
test/wcc/data/arrnarrow_struct_str/case.ww
Normal file
@@ -0,0 +1,7 @@
|
||||
//ww:error "not assignable"
|
||||
// #251 carrier: str element at a struct-field init (was a ww over-accept).
|
||||
// From test/wcc/951_arrlit_elem_narrow_run.c struct_str.
|
||||
package main;
|
||||
type enc = struct { m: [2]u8 };
|
||||
let E: enc = enc { m = ["x", "y"] };
|
||||
export fn main() i32 = { return E.m[0]: i32; };
|
||||
12
test/wcc/data/forrange_arr_field_base/case.ww
Normal file
12
test/wcc/data/forrange_arr_field_base/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:error "for-range over a non-ident array base unwired (#70)"
|
||||
// #70 carrier: for-range over a non-ident ARRAY base (struct field) stays
|
||||
// LOUD on both stages until a consumer wires it. Migrated from
|
||||
// test/wcc/937_forrange_fieldbase_run.c reject_arr_field row.
|
||||
package main;
|
||||
type holder = struct { arr: [3]i64, n: i64 };
|
||||
export fn main() i32 = {
|
||||
let h: holder = holder { arr = [1, 2, 3], n = 0 };
|
||||
let s: i64 = 0;
|
||||
for (let v .. h.arr) { s += v; };
|
||||
return (s: i32);
|
||||
};
|
||||
13
test/wcc/data/forrange_deref_base/case.ww
Normal file
13
test/wcc/data/forrange_deref_base/case.ww
Normal file
@@ -0,0 +1,13 @@
|
||||
//ww:error "for-range over a deref base unwired (#11)"
|
||||
// #70 carrier: for-range over a *p deref base stays LOUD on both stages
|
||||
// (the #11 deref-spine family — cgexpr does not deliver the slice header).
|
||||
// Migrated from test/wcc/937_forrange_fieldbase_run.c reject_deref row.
|
||||
package main;
|
||||
export fn main() i32 = {
|
||||
let xs: []i64;
|
||||
append(xs, 1);
|
||||
let q: *[]i64 = &xs;
|
||||
let s: i64 = 0;
|
||||
for (let v .. *q) { s += v; };
|
||||
return (s: i32);
|
||||
};
|
||||
14
test/wcc/data/nullable_asrt_void_fail/case.ww
Normal file
14
test/wcc/data/nullable_asrt_void_fail/case.ww
Normal file
@@ -0,0 +1,14 @@
|
||||
//ww:run-exit 1
|
||||
// #17 (F4) carrier: `e as T` on a null (*int|void) operand must ABORT (exit 1),
|
||||
// not silently pass. Hare guarantees a failed type-assert calls rt::abort (drew)
|
||||
// — not a row in @test (an abort kills `ww test`). The clean twin asrt_ptr_ok in
|
||||
// test/lang/nullable_assert_test.ww carries the abort-arm byte-id (ken: ww emits
|
||||
// the check-and-abort arm INLINE, so a wwstage regression diverges that twin);
|
||||
// the non-redundant dimension here is the RUNTIME abort. Underlying fix was
|
||||
// wwstage cgen (cgtypeassert nullable arm). From 949_nullable_assert_run.c.
|
||||
package main;
|
||||
export fn main() i32 = {
|
||||
let p: (*int|void) = void;
|
||||
let q = p as *int;
|
||||
return 50;
|
||||
};
|
||||
12
test/wcc/data/nullable_asrt_void_target_fail/case.ww
Normal file
12
test/wcc/data/nullable_asrt_void_target_fail/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 1
|
||||
// #17 (F4) carrier: a valid ptr `as void` must ABORT (exit 1) — the void
|
||||
// polarity of the type-assert. Clean twin asrt_void_target_ok carries the
|
||||
// abort-arm byte-id; this pins the runtime abort fires. From
|
||||
// 949_nullable_assert_run.c (drew: rt::abort is a real Hare guarantee).
|
||||
package main;
|
||||
export fn main() i32 = {
|
||||
let x: int = 5;
|
||||
let p: (*int|void) = &x;
|
||||
let z = p as void;
|
||||
return 7;
|
||||
};
|
||||
12
test/wcc/data/nullable_tryunw_null/case.ww
Normal file
12
test/wcc/data/nullable_tryunw_null/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:run-exit 1
|
||||
// #15 (F4) carrier: `p!` on a null (*int|void) must ABORT (exit 1), not treat
|
||||
// null as success and deref (SEGV pre-fix). Clean twin tryunw_valid in
|
||||
// test/lang/nullable_try_test.ww carries the abort-arm byte-id; this pins the
|
||||
// runtime abort. Underlying fix was wwstage cgen (cgtryunw nullable arm). From
|
||||
// 949_nullable_try_run.c.
|
||||
package main;
|
||||
fn h(p: (*int|void)) int = { let q = p!; return *q; };
|
||||
export fn main() i32 = {
|
||||
let r = h(void);
|
||||
return 50;
|
||||
};
|
||||
Reference in New Issue
Block a user