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:
64
test/lang/mixed_scalar_tuple_sret_test.ww
Normal file
64
test/lang/mixed_scalar_tuple_sret_test.ww
Normal 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));
|
||||
};
|
||||
88
test/lang/nonlit_tuple_widen_test.ww
Normal file
88
test/lang/nonlit_tuple_widen_test.ww
Normal 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')));
|
||||
};
|
||||
};
|
||||
};
|
||||
50
test/lang/overcap_tuple_field_store_test.ww
Normal file
50
test/lang/overcap_tuple_field_store_test.ww
Normal 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));
|
||||
};
|
||||
58
test/lang/rvalue_tuple_destructure_test.ww
Normal file
58
test/lang/rvalue_tuple_destructure_test.ww
Normal 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); };
|
||||
};
|
||||
};
|
||||
227
test/lang/tagged_tuple_widen_test.ww
Normal file
227
test/lang/tagged_tuple_widen_test.ww
Normal 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);
|
||||
};
|
||||
};
|
||||
46
test/lang/tagtupfieldsize_test.ww
Normal file
46
test/lang/tagtupfieldsize_test.ww
Normal 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);
|
||||
};
|
||||
65
test/lang/tupfieldsize_test.ww
Normal file
65
test/lang/tupfieldsize_test.ww
Normal 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);
|
||||
};
|
||||
84
test/lang/tuple_in_union_test.ww
Normal file
84
test/lang/tuple_in_union_test.ww
Normal 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); };
|
||||
};
|
||||
};
|
||||
61
test/lang/tuple_index_read_test.ww
Normal file
61
test/lang/tuple_index_read_test.ww
Normal 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);
|
||||
};
|
||||
65
test/lang/tuple_lit_declblind_test.ww
Normal file
65
test/lang/tuple_lit_declblind_test.ww
Normal 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);
|
||||
};
|
||||
120
test/lang/tuple_nary_destructure_test.ww
Normal file
120
test/lang/tuple_nary_destructure_test.ww
Normal 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);
|
||||
};
|
||||
1453
test/lang/tuple_slot_layout_test.ww
Normal file
1453
test/lang/tuple_slot_layout_test.ww
Normal file
File diff suppressed because it is too large
Load Diff
98
test/lang/tuple_tagged_union_test.ww
Normal file
98
test/lang/tuple_tagged_union_test.ww
Normal 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);
|
||||
};
|
||||
84
test/lang/tupparam_test.ww
Normal file
84
test/lang/tupparam_test.ww
Normal 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
74
test/lang/tuprecv_test.ww
Normal 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);
|
||||
};
|
||||
Reference in New Issue
Block a user