test: migrate Fam8 tuple value tests to @test + reject carriers (#5-C2)

fold-2 chunk C2 (drew's Fam8-13 plan): 14 tuple value-row C drivers migrate to
15 test/lang/*_test.ww @test row-tables (the +1 is 954_tuprecv, slimmed not
deleted -- its value rows split out while the asserttyped-stamp dimension stays
as a carrier-split C pin, mutation-proven non-vacuous). Reject rows move to 32
test/wcc/data/*/case.ww //ww:error carriers (runww asserts the substring in
BOTH stages). The test-lang byte-id (LANGBYTEID) gate gives cs==ww automatically
and is strictly more sensitive than re-running the wwstage leg; floor 59->74.
Tuple surfaced zero cs!=ww as the plan predicted -- no value-only carve. The 945
trio folds in here; 940_global_sret / 940_str_forrange / 926_tagscr untouched
(routed to drew per-file). Test count 402->388 = the 14 retired drivers.
This commit is contained in:
2026-06-24 01:44:13 +09:00
parent 47c7dbc2c8
commit 07b3c74ab0
63 changed files with 3005 additions and 6577 deletions

View File

@@ -0,0 +1,64 @@
// mixed_scalar_tuple_sret_test — project #240: an over-cap tuple whose elements
// mix a scalar with slices/str (e.g. `(int, []u8, str)`) must lay out
// identically in both stages and round-trip through an sret return. Migrated
// from test/wcc/940_mixed_scalar_tuple_sret_run.c. Two silent, gate-blind
// cs!=ww divergences: callee SEND packed a leading scalar at the literal's
// (untyped-int, size 0) stride so trailing elements packed 8 bytes low; caller
// RECEIVE had no capacity gate so a 56B over-cap tuple was received via
// registers instead of the sret dest. The @test fns pin the runtime element
// values; T2 byte-id keeps cs==ww (rule 10). Each row covers an annotated and
// an inferred receive and a distinct scalar position.
package mixed_scalar_tuple_sret_test;
fn mk_int_slice_str() (int, []u8, str) = {
let b: []u8; b.len = 5; b.cap = 9;
let s: str = "abcd";
return (42, b, s);
};
fn mk_int_slice_str_inferred() (int, []u8, str) = {
let b: []u8; b.len = 6; b.cap = 8;
let s: str = "xyz";
return (17, b, s);
};
fn mk_slice_str_int() ([]u8, str, int) = {
let b: []u8; b.len = 7; b.cap = 9;
let s: str = "hello";
return (b, s, 99);
};
fn mk_str_int_slice() (str, int, []u8) = {
let s: str = "abcde";
let b: []u8; b.len = 3; b.cap = 4;
return (s, 77, b);
};
@test fn int_slice_str() void = {
let t: (int, []u8, str) = mk_int_slice_str();
assert(!(t.0 != 42));
assert(!(len(t.1) != 5));
assert(!(len(t.2) != 4));
};
@test fn int_slice_str_inferred() void = {
let t = mk_int_slice_str_inferred();
assert(!(t.0 != 17));
assert(!(len(t.1) != 6));
assert(!(len(t.2) != 3));
};
@test fn slice_str_int() void = {
let t: ([]u8, str, int) = mk_slice_str_int();
assert(!(len(t.0) != 7));
assert(!(len(t.1) != 5));
assert(!(t.2 != 99));
};
@test fn str_int_slice() void = {
let t: (str, int, []u8) = mk_str_int_slice();
assert(!(len(t.0) != 5));
assert(!(t.1 != 77));
assert(!(len(t.2) != 3));
};

View File

@@ -0,0 +1,88 @@
// nonlit_tuple_widen_test — project #116: a NON-LITERAL tuple source widened
// into a tagged box (cg_widen_tagged_store / cgwidentaggedstore). Pre-#116 only a
// bare/cast tuple LITERAL carried a full payload into the union box; every
// ADDRESSABLE non-literal tuple source — a tuple IDENT var, a slice/array INDEX
// `tbl[i]`, a DEREF `*p` — hit the `else fatal` ("tuple-typed source shape
// unwired"), LOUD and IDENTICAL on both stages, blocking fold-6's
// `append(charsets[...], charclass_map[cc_idx])`. The fix resolves the source
// ADDRESS through cgplaceaddr and BLOCK-COPIES the tuple's table size into the
// box payload, stamping the variant tag. Byte-id is BLIND here (both stages were
// loud — #263), so the RUNTIME read-back is the correctness net: `len(x.0)`
// proves the str header rode over, `(*x.1)(arg)` indirect-calls the fn-ptr
// element. Migrated from test/wcc/944_nonlit_tuple_widen_run.c. The never-taken
// rune match arm -> assert(false) (the C original's `return 90`).
package nonlit_tuple_widen_test;
type ci = (str, *fn(c: rune) bool);
type u = (rune | ci);
fn fa(c: rune) bool = { return c == 'a'; };
fn fb(c: rune) bool = { return c == 'b'; };
@test fn literal() void = {
let nm: str = "ww";
let cf: *fn(c: rune) bool = &fa;
let s: []u = [];
append(s, ((nm, cf): ci));
match (s[0]) {
case let r: rune => { assert(false); };
case let x: ci => {
assert(!((len(x.0): i32) != 2));
assert(!(!(*x.1)('a')));
};
};
};
@test fn ident() void = {
let nm: str = "zz";
let cf: *fn(c: rune) bool = &fb;
let t: ci = (nm, cf);
let s: []u = [];
append(s, t);
match (s[0]) {
case let r: rune => { assert(false); };
case let x: ci => {
assert(!((len(x.0): i32) != 2));
assert(!(!(*x.1)('b')));
assert(!((*x.1)('a')));
};
};
};
@test fn index() void = {
let n0: str = "aa"; let c0: *fn(c: rune) bool = &fa;
let n1: str = "zzz"; let c1: *fn(c: rune) bool = &fb;
let t0: ci = (n0, c0);
let t1: ci = (n1, c1);
let tbl: [2]ci = [];
tbl[0] = t0;
tbl[1] = t1;
let s: []u = [];
append(s, tbl[1]);
match (s[0]) {
case let r: rune => { assert(false); };
case let x: ci => {
assert(!((len(x.0): i32) != 3));
assert(!(!(*x.1)('b')));
assert(!((*x.1)('a')));
};
};
};
@test fn deref() void = {
let nm: str = "qqq";
let cf: *fn(c: rune) bool = &fa;
let t: ci = (nm, cf);
let p: *ci = &t;
let s: []u = [];
append(s, *p);
match (s[0]) {
case let r: rune => { assert(false); };
case let x: ci => {
assert(!((len(x.0): i32) != 3));
assert(!(!(*x.1)('a')));
assert(!((*x.1)('b')));
};
};
};

View File

@@ -0,0 +1,50 @@
// overcap_tuple_field_store_test — project #234: storing the result of an
// over-capacity tuple-returning call (sret: ([]u8,[]u8) = 6 GP) into a struct
// field or an indexed lvalue. Migrated from
// test/wcc/940_overcap_tuple_field_store_run.c. A FIELD or INDEXED dest stayed
// unwired: the store dropped the callee's sret body (a truncated MOVQ through a
// stale RDI), a SILENT miscompile gate-blind because the bootstrap never
// field-stores a wide tuple. The fix points the callee's hidden sret dest
// straight at the destination slot when it is a LOCAL (BP-relative). These
// @test fns pin the runtime contract (T1, exit 0 both stages); the byte-id
// gate (T2) keeps cs==ww. Every other dest (via_ptr / global / runtime-index /
// N_DOT-base index / chained index) HARD-ERRORS LOUD (#234-tail) and lives as a
// reject carrier (test/wcc/data/overcap_*_be/). cap != len in every wide
// element so a dropped len OR cap is caught.
package overcap_tuple_field_store_test;
fn wide() ([]u8, []u8) = {
let a: []u8; a.len = 1; a.cap = 5;
let b: []u8; b.len = 2; b.cap = 6;
return (a, b);
};
type S_struct_field = struct { hdr: int, f: ([]u8, []u8) };
// indexed_local — `arr[1] = wide()`, arr: [2]([]u8,[]u8). Readback via
// `(&arr[1]):*int` p[i] (a tuple-element READ arr[1].N is a separate gap, filed
// #238) — len0=1, cap0=5, len1=2, cap1=6.
@test fn indexed_local() void = {
let arr: [2]([]u8, []u8);
arr[1] = wide();
let p: *int = (&arr[1]): *int;
assert(!(p[1] != 1));
assert(!(p[2] != 5));
assert(!(p[4] != 2));
assert(!(p[5] != 6));
};
// struct_field — `s.f = wide()`, S = struct { hdr:int, f:([]u8,[]u8) }. foff!=0
// (after hdr) + hdr-uncorrupted check. Pointer readback `(&s.f):*int`.
@test fn struct_field() void = {
let s: S_struct_field;
s.hdr = 99;
s.f = wide();
assert(!(s.hdr != 99));
let p: *int = (&s.f): *int;
assert(!(p[1] != 1));
assert(!(p[2] != 5));
assert(!(p[4] != 2));
assert(!(p[5] != 6));
};

View File

@@ -0,0 +1,58 @@
// rvalue_tuple_destructure_test — project #241: materialise an RVALUE tuple
// into the SysV register-return cursor before a destructure / let bind.
// Migrated from test/wcc/945_rvalue_tuple_destructure_run.c. cgexpr could not
// produce a tuple VALUE: a literal `(a, b)` fell to the MOVQ $0 default, a
// tuple-typed IDENT loaded only word0, and a `?`/`!` unwrap of a tuple-in-
// union payload lifted only word0 — so a destructure read GARBAGE past the
// first element (cstage), and the un-typed binder aborted wwstage's checker
// (asserttyped). A DANGEROUS gate-blind cs!=ww (the strconv blocker
// `let (sign, u) = parseint(s, base)?`). The fix packs an N_TUPLE literal and a
// tuple IDENT into the register-return cursor and shifts a `?`/`!` tuple
// success payload down past the tag; both stages byte-identical (rule 10).
//
// The C driver ran each row on both stages + asserted cs==ww .s byte-id; here
// the cstage run is test-lang and the byte-id is test-lang-byteid (T2), so both
// dimensions survive. The never-taken error arms abort() via assert(false), the
// migrated-test idiom for a diverging arm in a yield/destructure context (the C
// original `return 9`).
package rvalue_tuple_destructure_test;
type myerr = !void;
type invalid = !void;
fn mk(x: u64, s: bool) ((bool, u64) | myerr) = { return (s, x); };
fn parseint(base: int) ((bool, u64) | invalid) = {
let sg: bool = true;
let n: u64 = 42u64;
return (sg, n);
};
fn stoi(base: int) (u64 | invalid) = {
let (sign, u) = parseint(base)?;
if (!sign) { return 0u64; };
return u;
};
@test fn literal_destructure() void = {
let (a, b) = (true, 11u64);
assert(b == 11u64);
assert(a);
};
@test fn match_yield() void = {
let (sg, u) = match (mk(6u64, true)) {
case let t: (bool, u64) => yield t;
case myerr => { assert(false); };
};
assert(u == 6u64);
assert(sg);
};
@test fn trycall_strconv() void = {
match (stoi(10)) {
case let v: u64 => { assert(v == 42u64); };
case invalid => { assert(false); };
};
};

View File

@@ -0,0 +1,227 @@
// tagged_tuple_widen_test — #66 (fold-4 prereq): the cast-wrapped tuple literal
// `((a, b): variant_alias)` widened into a tagged-union payload. Pre-#66 the
// #242 tuple arm of the widen choke-point (cg_widen_tagged_store /
// cgwidentaggedstorebp) gated on a BARE N_TUPLE source; the cast-to-CONCRETE-
// VARIANT wrapper — the only spelling real code uses (ref/hare/regex/regex.ha:213
// `(start_b, end_b): charset_range_item`) — was not peeled and silently stored
// cursor word 0 only, zero-filling payload slot 1+ (both stages, byte-id —
// gate-blind). The read side (match-extract slot walk) was always correct.
// Migrated from test/wcc/936_tagged_tuple_widen_run.c. The K_RUN value rows are
// @test fns here; the bare_literal_reject K_BUILDERR row is carried at
// test/wcc/data/twiden_bare_literal_reject/case.ww. Never-taken match arms ->
// assert(false) (the migrated idiom for the C original's `return N`).
package tagged_tuple_widen_test;
type lit = rune;
type rng = (u32, u32);
type item = (lit | rng);
@test fn cast_let_packed() void = {
let v: item = ((65, 90): rng);
match (v) {
case let r: rng => {
assert(!(r.0 != 65));
assert(!(r.1 != 90));
};
case => assert(false);
};
};
type cls_3member = (str, *fn(c: rune) bool);
type item_3member = (lit | rng | cls_3member);
@test fn cast_let_3member() void = {
let v: item_3member = ((65, 90): rng);
match (v) {
case let r: rng => {
assert(!(r.0 != 65));
assert(!(r.1 != 90));
};
case => assert(false);
};
let w: item_3member = ('a': lit);
match (w) {
case let l: lit => { assert(!((l: rune) != 'a')); };
case => assert(false);
};
};
@test fn cast_append_local() void = {
let xs: []item;
append(xs, ('a': lit));
append(xs, ((65, 90): rng));
assert(!(len(xs) != 2));
let e: item = xs[1];
match (e) {
case let r: rng => {
assert(!(r.0 != 65));
assert(!(r.1 != 90));
};
case => assert(false);
};
};
@test fn cast_append_indexplace() void = {
let charsets: [][]item;
let empty: []item;
append(charsets, empty);
append(charsets[len(charsets) - 1], ((7, 9): rng));
assert(!(len(charsets[0]) != 1));
let e: item = charsets[0][0];
match (e) {
case let r: rng => {
assert(!(r.0 != 7));
assert(!(r.1 != 9));
};
case => assert(false);
};
};
fn addrange(cs: *[][]item, a: u32, b: u32) void = {
if (len(*cs) == 0) {
let empty: []item;
append(*cs, empty);
};
append((*cs)[len(*cs) - 1], ((a, b): rng));
};
@test fn cast_append_derefplace() void = {
let charsets: [][]item;
addrange(&charsets, 5, 8);
assert(!(len(charsets) != 1));
assert(!(len(charsets[0]) != 1));
let e: item = charsets[0][0];
match (e) {
case let r: rng => {
assert(!(r.0 != 5));
assert(!(r.1 != 8));
};
case => assert(false);
};
};
type holder = struct { cs: [][]item, n: i64 };
@test fn cast_append_ptrfield() void = {
let h: holder = holder { n = 0, ... };
let p: *holder = &h;
let empty: []item;
append(p.cs, empty);
append(p.cs[len(p.cs) - 1], ((7, 9): rng));
assert(!(len(h.cs[0]) != 1));
match (h.cs[0][0]) {
case let r: rng => {
assert(!(r.0 != 7));
assert(!(r.1 != 9));
};
case => assert(false);
};
};
@test fn cast_ident_elems() void = {
let start_b: u32 = 97;
let end_b: u32 = 122;
let v: item = ((start_b, end_b): rng);
match (v) {
case let r: rng => {
assert(!(r.0 != 97));
assert(!(r.1 != 122));
};
case => assert(false);
};
};
type mf = (u32, f64);
type item_float = (rune | mf);
@test fn cast_float_elem() void = {
let a: u32 = 9;
let x: f64 = 2.5;
let v: item_float = ((a, x): mf);
match (v) {
case let m: mf => {
assert(!(m.0 != 9));
assert(!(m.1 != 2.5));
};
case => assert(false);
};
};
type ms = (i64, str);
type item_str = (rune | ms);
@test fn cast_str_elem() void = {
let n: i64 = 12;
let s: str = "wxyz";
let v: item_str = ((n, s): ms);
match (v) {
case let m: ms => {
assert(!(m.0 != 12));
assert(!(len(m.1) != 4));
};
case => assert(false);
};
};
fn check_arg_direct(v: item) i32 = {
match (v) {
case let r: rng => {
assert(!(r.0 != 65));
assert(!(r.1 != 90));
return 0;
};
case => return 3;
};
};
@test fn cast_arg_direct() void = {
assert(check_arg_direct(((65, 90): rng)) == 0);
};
type item_ii = (rune | (i64, i64));
fn check_bare_ident_arg(v: item_ii) i32 = {
match (v) {
case let r: (i64, i64) => {
assert(!(r.0 != 5));
assert(!(r.1 != 6));
return 0;
};
case => return 3;
};
};
@test fn bare_ident_arg() void = {
let a: i64 = 5;
let b: i64 = 6;
assert(check_bare_ident_arg((a, b)) == 0);
};
fn mk_no_regress() ((i64, i64) | nomem) = {
let a: i64 = 5;
let b: i64 = 6;
return (a, b);
};
@test fn bare_ident_no_regress() void = {
match (mk_no_regress()) {
case let t: (i64, i64) => {
assert(!(t.0 != 5));
assert(!(t.1 != 6));
};
case => assert(false);
};
};
@test fn tuple_ident_widen() void = {
let t: rng = ((65, 90): rng);
let v: item = t;
match (v) {
case let r: rng => {
assert(!(r.0 != 65));
assert(!(r.1 != 90));
};
case => assert(false);
};
};

View File

@@ -0,0 +1,46 @@
// tagtupfieldsize_test — #53 (sibling of #43): a for-range destructure over an
// array of tuples whose field is a TAGGED UNION must stride by the tuple's true
// size and copy the field's full box (tag + payload), not the 8B scalar
// default. Migrated from test/wcc/989_tagtupfieldsize_run.c.
//
// THE BUG (cat-A silent miscompile, gate-blind): wwstage paramfieldsize
// (selfhost/cmd/wcc/cgenstmt.ww) had no tagged-union arm, so a `(i64|bool)`
// field sized 8 instead of its 16B box (tag + slot-padded payload); the 8B bind
// copied only the tag word and read the payload from stale stack (ww=9 vs
// cs=56). THE FIX: read the tagged box width from the checker-stamped tinfo
// (rule-13), mirroring the N_TSLICE arm; the stamp collapses the `tu` alias so
// an N_TNAME field naming a tagged union resolves too.
//
// The tagged value enters the tuple from a LOCAL ident box (the only wired
// tagged-tuple-element source); a cast source loud-rejects.
//
// The C driver ran each row on both stages + asserted cs==ww exit; here the
// cstage run is test-lang (T1) and the byte-id is test-lang-byteid (T2).
package tagtupfieldsize_test;
type tu = (i64 | bool);
@test fn tag_payload() void = {
let t0: tu = 7: tu;
let t1: tu = 9: tu;
let xs: [2](tu, i64);
xs[0] = (t0, 10);
xs[1] = (t1, 30);
let sum: i64 = 0;
for (let (a, n) .. xs) {
match (a) {
case let v: i64 => sum = sum + v + n;
case let b: bool => sum = sum + 1000;
};
};
assert(sum: int == 56);
};
@test fn scalar_tuple_ctl() void = {
let xs: [2](i64, i64);
xs[0] = (3, 10); xs[1] = (5, 30);
let sum: i64 = 0;
for (let (a, b) .. xs) { sum = sum + a + b; };
assert(sum: int == 48);
};

View File

@@ -0,0 +1,65 @@
// tupfieldsize_test — F7-c4 (#43) + #40: a for-range destructure over an array
// of tuples must stride by the tuple's TRUE size, and a slice/str/nested-tuple
// FIELD carries its full 24B width, not the 8B scalar default; the destructure
// LOAD-into-binding must copy the full extent of a sz>8 aggregate binding (the
// recurring dropped word is the CAP). Migrated from test/wcc/989_tupfieldsize_run.c.
//
// THE BUG (cat-A silent miscompile, gate-blind): wwstage paramfieldsize
// (selfhost/cmd/wcc/cgenstmt.ww) had no N_TSLICE / N_TTUPLE arm, so a `[]T`
// tuple-field sized 8 instead of its 24B header; and the destructure copy
// landed only the PTR word, dropping .len/.cap. The fix adds the N_TSLICE arm
// (tyslicesize()=24, rule-13) + the N_TTUPLE arm, and copies the binding's full
// extent (cgen.c N_FORRANGE + cgenstmt.ww cgforrange twin), aligning wwstage UP.
//
// The cap row slices the bases so cap != len, teething the recurring dropped
// CAP word; a ptr+len-only set would pass green while cap stayed stale.
//
// The C driver ran each row on both stages + asserted cs==ww exit; here the
// cstage run is test-lang (T1) and the byte-id is test-lang-byteid (T2).
package tupfieldsize_test;
@test fn len_read() void = {
let b0: []u8 = ['a', 'b'];
let b1: []u8 = ['x', 'y', 'z'];
let xs: [2]([]u8, i64);
xs[0] = (b0, 10);
xs[1] = (b1, 30);
let sum: i64 = 0;
for (let (b, n) .. xs) {
sum = sum + len(b): i64 + n;
};
assert(sum: int == 45);
};
@test fn cap_read() void = {
let base0: []u8 = ['a', 'b', 'c', 'd'];
let base1: []u8 = ['p', 'q', 'r', 's', 't'];
let xs: [2]([]u8, i64);
xs[0] = (base0[0:2], 10);
xs[1] = (base1[0:3], 30);
let sum: i64 = 0;
for (let (b, n) .. xs) {
sum = sum + b.cap: i64 + n;
};
assert(sum: int == 49);
};
@test fn ptr_read() void = {
let b0: []u8 = ['a', 'b'];
let b1: []u8 = ['x', 'y', 'z'];
let xs: [2]([]u8, i64);
xs[0] = (b0, 10);
xs[1] = (b1, 30);
let sum: i64 = 0;
for (let (b, n) .. xs) { sum = sum + b[0]: i64; };
assert(sum: int == 217);
};
@test fn scalar_tuple_ctl() void = {
let xs: [2](i64, i64);
xs[0] = (3, 10); xs[1] = (5, 30);
let sum: i64 = 0;
for (let (a, b) .. xs) { sum = sum + a + b; };
assert(sum: int == 48);
};

View File

@@ -0,0 +1,84 @@
// tuple_in_union_test — project #242: a mixed-scalar tuple wrapped in a tagged
// union must construct, match-bind, and destructure identically in both stages
// and round-trip every element. Migrated from test/wcc/940_tuple_in_union_run.c
// (K_RUN value rows). Three silent gate-blind cs!=ww divergences were met:
// cstage construction zeroed the whole value instead of packing the tuple
// variant; wwstage's checker left un-annotated destructure binders untyped for
// a non-call rhs; both stages destructured a tuple ident off a stale register
// cursor. The @test fns pin the runtime element values at variant 0 and at a
// non-zero variant index; T2 byte-id keeps cs==ww (rule 10). The K_BUILDERR
// bare-literal-element loud-stop row lives at test/wcc/data/tupunion_bool_literal/.
package tuple_in_union_test;
fn mku_bool_u64(x: u64, s: bool) ((bool, u64) | void) = { return (s, x); };
fn mku_void_arm(ok: bool, x: u64, s: bool) ((bool, u64) | void) = {
if (ok) { return (s, x); };
return;
};
fn mku_tuple_tag1(x: u64, s: bool) (void | (bool, u64)) = { return (s, x); };
fn mk_tuple_after_int(which: bool) (int | (bool, u64)) = {
let s: bool = true;
let v: u64 = 88u64;
if (which) { return (s, v); };
return 5;
};
fn f_eightbyte_share(a: i32, b: i32, c: u64) ((i32, i32, u64) | void) = {
return (a, b, c);
};
@test fn bool_u64() void = {
match (mku_bool_u64(7u64, true)) {
case let t: (bool, u64) => {
let (sg, u) = t;
assert(!(u != 7u64));
assert(!(!sg));
};
case void => { assert(false); };
};
};
@test fn void_arm() void = {
match (mku_void_arm(false, 1u64, true)) {
case let t: (bool, u64) => { assert(false); };
case void => { };
};
};
@test fn tuple_tag1() void = {
match (mku_tuple_tag1(9u64, true)) {
case void => { assert(false); };
case let t: (bool, u64) => {
let (sg, u) = t;
assert(!(u != 9u64));
assert(!(!sg));
};
};
};
@test fn tuple_after_int() void = {
match (mk_tuple_after_int(true)) {
case let n: int => { assert(false); };
case let t: (bool, u64) => {
let (sg, u) = t;
assert(!(u != 88u64));
assert(!(!sg));
};
};
};
@test fn eightbyte_share() void = {
match (f_eightbyte_share(-3, 4, 9u64)) {
case let t: (i32, i32, u64) => {
let (x, y, z) = t;
assert(!(x != -3));
assert(!(y != 4));
assert(!(z != 9u64));
};
case void => { assert(false); };
};
};

View File

@@ -0,0 +1,61 @@
// tuple_index_read_test — #121: const-slice tuple-element READ. Migrated from
// test/wcc/947_tuple_index_read_run.c.
//
// Three legs of indexed-tuple read off a `const [](str,*fn)` (fold-6's
// charclass_map shape), all sharing the &tbl[i] place-spine (#116):
//
// field_read — direct `tbl[i].N` field read, was LOUD both stages.
// whole_element — `let e = tbl[i]` then read e.N, was SILENT both-wrong-
// IDENTICAL (#263, word0-only truncation). The runtime read-
// back with DISTINCT-per-row values is the net: a wrong word
// is CAUGHT, not masked.
// store_roundtrip — write-face: a tuple-LITERAL store `a[i] = (3,4)`, read
// back whole; DISTINCT words per row catch the word0-only
// store.
//
// The for-range-over-module-global LOUD-STOP reject row migrated to
// test/wcc/data/tupidx_forrange_global_loud/case.ww (K_BUILDERR). Every value
// row's cs==ww .s byte-id rides T2 (test-lang-byteid).
package tuple_index_read_test;
fn fa(c: rune) bool = { return c == 'a'; };
fn fz(c: rune) bool = { return c == 'z'; };
const tbl: [](str, *fn(c: rune) bool) = [("ab", &fa), ("cdef", &fz)];
@test fn field_read() void = {
let s1 = tbl[1].0;
let s0 = tbl[0].0;
assert(len(s1) == 4);
assert(len(s0) == 2);
let f1 = tbl[1].1;
assert((*f1)('z'));
assert(!((*f1)('a')));
let f0 = tbl[0].1;
assert((*f0)('a'));
assert(!((*f0)('z')));
};
@test fn whole_element() void = {
let e = tbl[1];
assert(len(e.0) == 4);
assert((*e.1)('z'));
assert(!((*e.1)('a')));
let e0 = tbl[0];
assert(len(e0.0) == 2);
assert((*e0.1)('a'));
assert(!((*e0.1)('z')));
};
@test fn store_roundtrip() void = {
let a: [2](u64, u64);
a[1] = (3u64, 4u64);
a[0] = (7u64, 9u64);
let t = a[1];
assert(t.0 == 3);
assert(t.1 == 4);
let s = a[0];
assert(s.0 == 7);
assert(s.1 == 9);
};

View File

@@ -0,0 +1,65 @@
// tuple_lit_declblind_test — #64 + #68: a tuple LITERAL fills the register
// cursor DECL-BLIND. The #57 decl wire (a declared-tagged element's concrete
// rvalue widens into its box) stopped at N_LET / N_RETURN; the other two
// tuple-literal consumers — destructure-REASSIGN (N_MASSIGN, #64) and CALL-ARG
// send (#68) — rode the decl-less route, so a declared-tagged element was
// stored/sent WORD-0 ONLY (the box tag never set) and the cursor skewed every
// later element. Both stages, #263 gate-blind: the asm was byte-identical
// cs==ww and both ran wrong, so only RUNTIME catches it.
//
// Migrated from test/wcc/945_tuple_lit_declblind_run.c. Each value row reads
// the box PAYLOAD back (binds n and asserts its VALUE), not merely which match
// arm fired — a skew that sets the tag right but corrupts the payload still
// fails. The never-taken arms abort via assert(false), the migrated-test idiom
// for a diverging arm (the C original's non-`want` exit codes). The reject row
// (nested_tuple_arg) carries to test/wcc/data/tldb_nested_tuple_arg/. cstage
// run is test-lang; cs==ww byte-id is test-lang-byteid (T2) — both dimensions
// survive.
package tuple_lit_declblind_test;
type ev = (i32 | i64);
fn f(t: (ev, i64)) i32 = {
match (t.0) {
case let n: i64 => { if (n == 7) { return 1; }; return 3; };
case let m: i32 => { return 0; };
};
return 2;
};
fn g(t: (ev, i64)) i32 = {
let bp: i64 = 0;
match (t.0) {
case let n: i64 => { bp = n; };
case let m: i32 => { bp = 0; };
};
return (bp + t.1): i32;
};
@test fn massign_tagged() void = {
let a: ev = 5i32;
let b: i64 = 0;
a, b = (7i64, 99);
match (a) {
case let n: i64 => { assert(n == 7); };
case let m: i32 => { assert(false); };
};
};
@test fn callarg_tagged() void = {
assert(f((7i64, 99)) == 1);
};
@test fn massign_blank() void = {
let a: ev = 5i32;
_, a = (99, 7i64);
match (a) {
case let n: i64 => { assert(n == 7); };
case let m: i32 => { assert(false); };
};
};
@test fn callarg_drain() void = {
assert(g((7i64, 35)) == 42);
};

View File

@@ -0,0 +1,120 @@
// tuple_nary_destructure_test — project #83: the general N-ary tuple-
// destructure per-element register-return. The send (N_RETURN) and receive
// (N_MLET / N_MASSIGN) sites previously gated the 3-word {ptr,len,cap} store
// on a str-only XOR (e0_is_str ^ e1_is_str), which admitted EXACTLY ONE str
// element and was slice-BLIND: a tuple with a []u8 element fell to the
// scalar-pair fallback and silently DROPPED len+cap (only .ptr landed). The
// fix REPLACES the XOR with a positional per-element cursor (harec
// create_unpack_bindings, ref/harec/src/check.c:1354-1416): each element rides
// consecutive eightbytes over [AX,DX,CX,R8]; a slice/str rides its 3-word
// header, a scalar rides 1. SEND and RECEIVE walk the SAME cursor.
//
// Migrated from test/wcc/945_tuple_nary_destructure_run.c. Every wide row has
// cap!=len so a dropped len OR cap is caught; N_MASSIGN rows pre-poison the
// destination slot (distinct len/cap via a proven 3-word let-init copy) so a
// dropped len/cap reads the poison, never the test value. Each []u8 element
// borrows a str literal's .ptr (stable .rodata). Row F (slice_slice_recv) is
// the over-cap 6-eightbyte tuple that #10 Fold A sret's at the SEND and Fold B
// copies out at the RECEIVE — a working round-trip. cstage run is test-lang;
// cs==ww byte-id is test-lang-byteid (T2) — both dimensions survive.
package tuple_nary_destructure_test;
fn mk_mlet_islice() (i64, []u8) = {
let b: str = "hi";
let p: []u8; p.ptr = b.ptr; p.len = 2; p.cap = 5;
return (4i64, p);
};
fn mk_massign_islice() (i64, []u8) = {
let b: str = "hi";
let p: []u8; p.ptr = b.ptr; p.len = 2; p.cap = 5;
return (4i64, p);
};
fn mk_mlet_istr() (i64, str) = {
let p: str = "hi"; p.cap = 7i32;
return (4i64, p);
};
fn mk_massign_istr() (i64, str) = {
let p: str = "hi"; p.cap = 7i32;
return (4i64, p);
};
fn mk_str_control() str = {
let p: str = "hello"; p.cap = 9i32;
return p;
};
fn mk_massign_blank_wide() ([]u8, i64) = {
let b: str = "hi";
let p: []u8; p.ptr = b.ptr; p.len = 2; p.cap = 5;
return (p, 7i64);
};
fn mk_slice_slice_recv() ([]u8, []u8) = {
let a: []u8; a.len = 1; a.cap = 5;
let b: []u8; b.len = 2; b.cap = 6;
return (a, b);
};
@test fn mlet_islice() void = {
let (a, s) = mk_mlet_islice();
assert(a == 4);
assert(s.len: i32 == 2);
assert(s.cap: i32 == 5);
assert(s[0] == 104u8);
};
@test fn massign_islice() void = {
let qb: str = "zzzz";
let q: []u8; q.ptr = qb.ptr; q.len = 9; q.cap = 9;
let a: i64 = 0i64;
let s: []u8 = q;
a, s = mk_massign_islice();
assert(a == 4);
assert(s.len: i32 == 2);
assert(s.cap: i32 == 5);
assert(s[0] == 104u8);
};
@test fn mlet_istr() void = {
let (a, s) = mk_mlet_istr();
assert(a == 4);
assert(s.len: i32 == 2);
assert(s.cap: i32 == 7);
assert(s[0] == 104u8);
};
@test fn massign_istr() void = {
let q: str = "qqqq"; q.cap = 6i32;
let a: i64 = 0i64;
let s: str = q;
a, s = mk_massign_istr();
assert(a == 4);
assert(s.len: i32 == 2);
assert(s.cap: i32 == 7);
assert(s[0] == 104u8);
};
@test fn str_control() void = {
let s: str = mk_str_control();
assert(s.len: i32 == 5);
assert(s.cap: i32 == 9);
assert(s[0] == 104u8);
};
@test fn massign_blank_wide() void = {
let a: i64 = 0i64;
_, a = mk_massign_blank_wide();
assert(a == 7);
};
@test fn slice_slice_recv() void = {
let (x, y) = mk_slice_slice_recv();
assert(x.len: i32 == 1);
assert(x.cap: i32 == 5);
assert(y.len: i32 == 2);
assert(y.cap: i32 == 6);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
// tuple_tagged_union_test — project #47 gap-A/gap-B: a tuple whose elements
// include a TAGGED union, boxed as a member of a tagged union, must CONSTRUCT
// + RETURN-STORE identically in both stages. Migrated from
// test/wcc/944_tuple_tagged_union_run.c.
//
// A PLAIN tuple-in-union already worked (#242); the blocker was the TAGGED
// ELEMENT. gap-A adds a RECURSIVE two-level widen: each tagged element is boxed
// into its tuple slot, THEN the whole tuple is boxed. The element's tuple-slot
// stride is its OWN box size (roundup8) — a (void|size) elem is 16B (2 slots).
// gap-B taught typeeqast the N_TTAGGED arm so wwstage ACCEPTS the tuple-with-
// tagged case pattern (b1c_full). Both stages get the SAME arm → byte-identical
// asm (rule 10); the C driver's cs==ww .s byte-id rides T2 (test-lang-byteid).
// The never-taken error/match arms abort via assert(false) per the migrated-
// test idiom for a diverging arm.
package tuple_tagged_union_test;
type error = !str;
fn pr_ret_3tuple(sel: i32) (((void | size), (void | size), size) | error) = {
if (sel == 0) {
let min: (void | size) = void;
let max: (void | size) = 5: size;
return (min, max, 7: size);
};
return "neg": error;
};
fn pr_ret_2tuple(sel: i32) (((void | size), size) | error) = {
let min: (void | size) = 5: size;
return (min, 7: size);
};
fn pr_scalar_ctrl(sel: i32) ((size, size, size) | error) = {
return (3: size, 4: size, 5: size);
};
fn pr_b1c_full(sel: i32) (((void | size), (void | size), size) | error) = {
if (sel == 0) {
let min: (void | size) = void;
let max: (void | size) = 5: size;
return (min, max, 7: size);
};
if (sel == 1) {
let min: (void | size) = 3: size;
let max: (void | size) = void;
return (min, max, 9: size);
};
return "Negative repetition count": error;
};
@test fn ret_3tuple() void = {
let a: (((void | size), (void | size), size) | error) = pr_ret_3tuple(0);
assert(!(a is error));
let b: (((void | size), (void | size), size) | error) = pr_ret_3tuple(2);
assert(b is error);
};
@test fn ret_2tuple() void = {
let a: (((void | size), size) | error) = pr_ret_2tuple(0);
assert(!(a is error));
};
@test fn scalar_ctrl() void = {
let a: ((size, size, size) | error) = pr_scalar_ctrl(0);
assert(!(a is error));
};
@test fn b1c_full() void = {
let acc: size = 0;
let a: (((void | size), (void | size), size) | error) = pr_b1c_full(0);
match (a) {
case let t: ((void | size), (void | size), size) => {
if (t.0 is void) { acc += 1; };
if (t.1 is size) { acc += t.1 as size; };
acc += t.2 * 10;
};
case let e: error => { assert(false); };
};
let b: (((void | size), (void | size), size) | error) = pr_b1c_full(1);
match (b) {
case let t: ((void | size), (void | size), size) => {
if (t.0 is size && t.0 as size == 3) { acc += 100; };
if (t.1 is void) { acc += 50; };
acc += t.2;
};
case let e: error => { assert(false); };
};
let c: (((void | size), (void | size), size) | error) = pr_b1c_full(2);
match (c) {
case let t: ((void | size), (void | size), size) => { assert(false); };
case let e: error => {
let s: str = e: str;
assert(len(s) == 25);
};
};
assert(acc: i32 == 235);
};

View File

@@ -0,0 +1,84 @@
// tupparam_test — runtime contract for #163, the tuple-PARAM ABI (the param
// twin of #164's tuple RETURN). Migrated from test/wcc/905_tupparam_run.c.
// A tuple passed AS AN ARGUMENT was unhandled in both stages: a tuple-typed
// call result left its elements in the return-ABI cursor and the SEND fell to
// the 1-GP-word else, so all but the first element was dropped. The fix
// restages the tuple into a frame slot by SysV class then pushes the slot
// words; both stages byte-identical (rule 10). GATE-BLIND TO BYTE-ID ALONE:
// pre-fix both stages were symmetric-WRONG, so cs==ww held — the rows diverge
// only at RUNTIME; these @test fns pin that runtime contract (T1), the byte-id
// gate (T2) keeps cs==ww. The loud-stop arg-reg-overflow row is a reject
// carrier (test/wcc/data/tupparam_gp_overflow_loudstop/).
package tupparam_test;
fn pair_f64f64_arg(a: f64, b: f64) (f64, f64) = { return (a, b); };
fn add_f64f64_arg(t: (f64, f64)) f64 = { return t.0 + t.1; };
fn pair_i64i64_arg(a: i64, b: i64) (i64, i64) = { return (a, b); };
fn add_i64i64_arg(t: (i64, i64)) i64 = { return t.0 + t.1; };
fn mk_f64i64_arg(a: f64, b: i64) (f64, i64) = { return (a, b); };
fn add_f64i64_arg(t: (f64, i64)) i64 = { return (t.0: i64) + t.1; };
fn mk_i64f64_arg(a: i64, b: f64) (i64, f64) = { return (a, b); };
fn add_i64f64_arg(t: (i64, f64)) i64 = { return t.0 + (t.1: i64); };
fn mk_f64str_arg(a: f64) (f64, str) = { return (a, "hello"); };
fn add_f64str_arg(t: (f64, str)) i64 = {
return (t.0: i64) + (t.1.len: i64);
};
fn pair_two_tuple_args(a: i64, b: i64) (i64, i64) = { return (a, b); };
fn add4_two_tuple_args(s: (i64, i64), t: (i64, i64)) i64 = {
return s.0 + s.1 + t.0 + t.1;
};
fn pair_f64f64_chain(a: f64, b: f64) (f64, f64) = { return (a, b); };
fn id_f64f64_chain(t: (f64, f64)) f64 = { return t.0 * 10.0 + t.1; };
// HEADLINE — (f64, f64) arg. Pre-fix the SEND pushes only AX (the two floats
// stay stranded in X0/X1) and the callee reads one GP word. Post-fix each
// float rides the SSE arg cursor (X0, X1).
@test fn f64f64_arg() void = {
assert(!(add_f64f64_arg(pair_f64f64_arg(3.0, 5.0)) != 8.0));
};
// (i64, i64) arg — proves the broader INTEGER-tuple-param drop is fixed
// (master dropped the second i64 too). e0->DI, e1->SI.
@test fn i64i64_arg() void = {
assert(!(add_i64i64_arg(pair_i64i64_arg(3, 5)) != 8));
};
// (f64, i64) — class independent of position: f64@X0 (SSE cursor), i64@DI
// (INTEGER cursor), independent counters.
@test fn f64i64_arg() void = {
assert(!(add_f64i64_arg(mk_f64i64_arg(3.0, 5)) != 8));
};
// (i64, f64) — order-swap: i64@DI, f64@X0. Confirms the float lands in the
// next XMM regardless of its positional slot.
@test fn i64f64_arg() void = {
assert(!(add_i64f64_arg(mk_i64f64_arg(3, 5.0)) != 8));
};
// (f64, str) — SSE + wide (24B {ptr,len,cap}) coexist. The f64 rides X0 (SSE,
// consuming no GP slot); the str rides DI/SI/DX (INTEGER cursor). f=4.0,
// s.len=5 -> 4+5 = 9.
@test fn f64str_arg() void = {
assert(!(add_f64str_arg(mk_f64str_arg(4.0)) != 9));
};
// MULTI-TUPLE-ARG, ONE CALL — f(g(), h()) where both args are tuple-producing
// calls. Proves @tupargscr (single-slot-per-fn) is REUSED per arg, not
// COLLIDED. 1+2+3+4 = 10.
@test fn two_tuple_args() void = {
assert(!(add4_two_tuple_args(pair_two_tuple_args(1, 2),
pair_two_tuple_args(3, 4)) != 10));
};
// CONTROL — a tuple arg threaded through a chain of two calls, proving the
// SEND/RECV round-trips through the slot intact.
@test fn f64f64_chain() void = {
assert(!(id_f64f64_chain(pair_f64f64_chain(3.0, 5.0)) != 35.0));
};

74
test/lang/tuprecv_test.ww Normal file
View File

@@ -0,0 +1,74 @@
// tuprecv_test — runtime + byte-id net for #102: the 16B whole-tuple-from-call
// receive (`let t = mk()`, then field reads t.0 / t.1). Migrated from
// test/wcc/954_tuprecv_run.c.
//
// Root: wwstage's cglet had NO 16B tuple-from-call receive branch, so a
// `let t = call()` whose callee returns a 2-eightbyte (16B) tuple fell through
// to the generic single-word store and never spilled word1 (the DX eightbyte)
// — silent loss of t.1. cstage has the branch (cmd/w6c/cgen.c:6652). The fix
// mirrors it in cglet. The all-integer (i64,i64) receive dropped DX too, so
// that "control" row is ALSO a fix row, not a pure no-op. The destructure form
// `let (a,b) = mk()` (cgmlet + tupstore cursor) was already byte-id; the
// wide/str-element 32B and the struct{i64,i64} 16B receives are over-catch
// guards (the sz==16 + rettupleof!=nil gate must not hijack them).
//
// The C driver ran each row on both stages + asserted cs==ww .s byte-id; here
// the cstage run is test-lang (T1) and the byte-id is test-lang-byteid (T2), so
// both dimensions survive.
//
// NOTE: the #121 asserttyped sub-dimension of ctl_destr (the C driver asserted
// w6c_ww emits NO `asserttyped:` diagnostic on the float destructure binding,
// the #121 A-narrow stamp net) is NOT carried by test-lang (no stderr grep);
// flagged to advisor drew, not silently dropped.
package tuprecv_test;
fn mk_f64_i64() (f64, i64) = { return (2.5, 7); };
fn mk_i64_f64() (i64, f64) = { return (7, 2.5); };
fn mk_i64_i64() (i64, i64) = { return (5, 7); };
fn mk_ctl_destr() (f64, i64) = { return (2.5, 7); };
fn mk_ctl_str() (i64, str) = { return (7, "hi"); };
type pair = struct { a: i64, b: i64 };
fn mk_ctl_struct() pair = { return pair { a = 5, b = 7 }; };
@test fn f64_i64() void = {
let t = mk_f64_i64();
let f: f64 = t.0;
let i: i64 = t.1;
assert((f: i32) + (i: i32) == 9);
};
@test fn i64_f64() void = {
let t = mk_i64_f64();
let i: i64 = t.0;
let f: f64 = t.1;
assert((i: i32) + (f: i32) == 9);
};
@test fn i64_i64() void = {
let t = mk_i64_i64();
let a: i64 = t.0;
let b: i64 = t.1;
assert((a: i32) + (b: i32) == 12);
};
@test fn ctl_destr() void = {
let (f, i) = mk_ctl_destr();
assert((f: i32) + (i: i32) == 9);
};
@test fn ctl_str() void = {
let t: (i64, str) = mk_ctl_str();
assert(t.0: i32 == 7);
};
@test fn ctl_struct() void = {
let t = mk_ctl_struct();
assert((t.a: i32) + (t.b: i32) == 12);
};